aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/comedi/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/comedi/drivers')
-rw-r--r--drivers/staging/comedi/drivers/8253.h420
-rw-r--r--drivers/staging/comedi/drivers/8255.c442
-rw-r--r--drivers/staging/comedi/drivers/8255.h57
-rw-r--r--drivers/staging/comedi/drivers/Makefile113
-rw-r--r--drivers/staging/comedi/drivers/acl7225b.c149
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_82x54.c1047
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_82x54.h73
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_Chrono.c2032
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_Chrono.h74
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_Dig_io.c1020
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_Dig_io.h46
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_INCCPT.c5363
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_INCCPT.h271
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_Inp_cpt.c861
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_Inp_cpt.h47
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_Pwm.c3588
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_Pwm.h76
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_Ssi.c848
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_Ssi.h43
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_Tor.c2049
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_Tor.h57
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_Ttl.c1038
-rw-r--r--drivers/staging/comedi/drivers/addi-data/APCI1710_Ttl.h44
-rw-r--r--drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.c203
-rw-r--r--drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.h27
-rw-r--r--drivers/staging/comedi/drivers/addi-data/addi_amcc_s5933.h476
-rw-r--r--drivers/staging/comedi/drivers/addi-data/addi_common.c3062
-rw-r--r--drivers/staging/comedi/drivers/addi-data/addi_common.h462
-rw-r--r--drivers/staging/comedi/drivers/addi-data/addi_eeprom.c1158
-rw-r--r--drivers/staging/comedi/drivers/addi-data/amcc_s5933_58.h457
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_APCI1710.c1265
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_APCI1710.h71
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci035.c600
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci035.h123
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci1032.c285
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci1032.h63
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci1500.c3045
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci1500.h165
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci1516.c542
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci1516.h64
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci1564.c1105
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci1564.h119
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci16xx.c780
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci16xx.h94
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci2016.c460
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci2016.h70
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci2032.c579
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci2032.h81
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci2200.c549
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci2200.h61
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c2697
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.h241
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci3200.c3642
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci3200.h187
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci3501.c742
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci3501.h94
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci3xxx.c1691
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci3xxx.h62
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_035.c5
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_1032.c3
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_1500.c3
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_1516.c3
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_1564.c3
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_16xx.c3
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_1710.c3
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_2016.c3
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_2032.c3
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_2200.c3
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_3001.c3
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_3120.c3
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_3200.c3
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_3300.c3
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_3501.c3
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_3xxx.c3
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_all.c18
-rw-r--r--drivers/staging/comedi/drivers/adl_pci6208.c391
-rw-r--r--drivers/staging/comedi/drivers/adl_pci7296.c174
-rw-r--r--drivers/staging/comedi/drivers/adl_pci7432.c202
-rw-r--r--drivers/staging/comedi/drivers/adl_pci8164.c512
-rw-r--r--drivers/staging/comedi/drivers/adl_pci9111.c1391
-rw-r--r--drivers/staging/comedi/drivers/adl_pci9118.c2100
-rw-r--r--drivers/staging/comedi/drivers/adq12b.c394
-rw-r--r--drivers/staging/comedi/drivers/adv_pci1710.c1568
-rw-r--r--drivers/staging/comedi/drivers/adv_pci1723.c465
-rw-r--r--drivers/staging/comedi/drivers/adv_pci_dio.c1077
-rw-r--r--drivers/staging/comedi/drivers/aio_aio12_8.c226
-rw-r--r--drivers/staging/comedi/drivers/aio_iiro_16.c177
-rw-r--r--drivers/staging/comedi/drivers/am9513.h79
-rw-r--r--drivers/staging/comedi/drivers/amcc_s5933.h172
-rw-r--r--drivers/staging/comedi/drivers/amplc_dio200.c1483
-rw-r--r--drivers/staging/comedi/drivers/amplc_pc236.c654
-rw-r--r--drivers/staging/comedi/drivers/amplc_pc263.c431
-rw-r--r--drivers/staging/comedi/drivers/amplc_pci224.c1545
-rw-r--r--drivers/staging/comedi/drivers/amplc_pci230.c2977
-rw-r--r--drivers/staging/comedi/drivers/c6xdigio.c517
-rw-r--r--drivers/staging/comedi/drivers/cb_das16_cs.c976
-rw-r--r--drivers/staging/comedi/drivers/cb_pcidas.c1831
-rw-r--r--drivers/staging/comedi/drivers/cb_pcidas64.c4222
-rw-r--r--drivers/staging/comedi/drivers/cb_pcidda.c841
-rw-r--r--drivers/staging/comedi/drivers/cb_pcidio.c294
-rw-r--r--drivers/staging/comedi/drivers/cb_pcimdas.c484
-rw-r--r--drivers/staging/comedi/drivers/cb_pcimdda.c474
-rw-r--r--drivers/staging/comedi/drivers/comedi_bond.c56
-rw-r--r--drivers/staging/comedi/drivers/comedi_fc.c14
-rw-r--r--drivers/staging/comedi/drivers/comedi_fc.h18
-rw-r--r--drivers/staging/comedi/drivers/comedi_parport.c44
-rw-r--r--drivers/staging/comedi/drivers/comedi_rt_timer.c728
-rw-r--r--drivers/staging/comedi/drivers/comedi_test.c80
-rw-r--r--drivers/staging/comedi/drivers/contec_pci_dio.c230
-rw-r--r--drivers/staging/comedi/drivers/daqboard2000.c877
-rw-r--r--drivers/staging/comedi/drivers/das08.c1068
-rw-r--r--drivers/staging/comedi/drivers/das08.h78
-rw-r--r--drivers/staging/comedi/drivers/das08_cs.c488
-rw-r--r--drivers/staging/comedi/drivers/das16.c1730
-rw-r--r--drivers/staging/comedi/drivers/das16m1.c765
-rw-r--r--drivers/staging/comedi/drivers/das1800.c1758
-rw-r--r--drivers/staging/comedi/drivers/das6402.c354
-rw-r--r--drivers/staging/comedi/drivers/das800.c894
-rw-r--r--drivers/staging/comedi/drivers/dmm32at.c1081
-rw-r--r--drivers/staging/comedi/drivers/dt2801.c697
-rw-r--r--drivers/staging/comedi/drivers/dt2811.c604
-rw-r--r--drivers/staging/comedi/drivers/dt2814.c383
-rw-r--r--drivers/staging/comedi/drivers/dt2815.c264
-rw-r--r--drivers/staging/comedi/drivers/dt2817.c178
-rw-r--r--drivers/staging/comedi/drivers/dt282x.c1471
-rw-r--r--drivers/staging/comedi/drivers/dt3000.c983
-rw-r--r--drivers/staging/comedi/drivers/dt9812.c40
-rw-r--r--drivers/staging/comedi/drivers/fl512.c186
-rw-r--r--drivers/staging/comedi/drivers/gsc_hpdi.c1060
-rw-r--r--drivers/staging/comedi/drivers/icp_multi.c392
-rw-r--r--drivers/staging/comedi/drivers/icp_multi.h19
-rw-r--r--drivers/staging/comedi/drivers/ii_pci20kc.c612
-rw-r--r--drivers/staging/comedi/drivers/jr3_pci.c976
-rw-r--r--drivers/staging/comedi/drivers/jr3_pci.h681
-rw-r--r--drivers/staging/comedi/drivers/ke_counter.c254
-rw-r--r--drivers/staging/comedi/drivers/me4000.c296
-rw-r--r--drivers/staging/comedi/drivers/me4000.h268
-rw-r--r--drivers/staging/comedi/drivers/me_daq.c55
-rw-r--r--drivers/staging/comedi/drivers/mite.c63
-rw-r--r--drivers/staging/comedi/drivers/mite.h68
-rw-r--r--drivers/staging/comedi/drivers/mpc624.c386
-rw-r--r--drivers/staging/comedi/drivers/mpc8260cpm.c169
-rw-r--r--drivers/staging/comedi/drivers/multiq3.c333
-rw-r--r--drivers/staging/comedi/drivers/ni_6527.c489
-rw-r--r--drivers/staging/comedi/drivers/ni_65xx.c809
-rw-r--r--drivers/staging/comedi/drivers/ni_660x.c1324
-rw-r--r--drivers/staging/comedi/drivers/ni_670x.c337
-rw-r--r--drivers/staging/comedi/drivers/ni_at_a2150.c909
-rw-r--r--drivers/staging/comedi/drivers/ni_at_ao.c454
-rw-r--r--drivers/staging/comedi/drivers/ni_atmio.c513
-rw-r--r--drivers/staging/comedi/drivers/ni_atmio16d.c860
-rw-r--r--drivers/staging/comedi/drivers/ni_daq_700.c841
-rw-r--r--drivers/staging/comedi/drivers/ni_daq_dio24.c598
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc.c2005
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc.h85
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc_cs.c573
-rw-r--r--drivers/staging/comedi/drivers/ni_mio_common.c5862
-rw-r--r--drivers/staging/comedi/drivers/ni_mio_cs.c551
-rw-r--r--drivers/staging/comedi/drivers/ni_pcidio.c1302
-rw-r--r--drivers/staging/comedi/drivers/ni_pcimio.c1788
-rw-r--r--drivers/staging/comedi/drivers/ni_stc.h1498
-rw-r--r--drivers/staging/comedi/drivers/ni_tio.c1691
-rw-r--r--drivers/staging/comedi/drivers/ni_tio.h163
-rw-r--r--drivers/staging/comedi/drivers/ni_tio_internal.h774
-rw-r--r--drivers/staging/comedi/drivers/ni_tiocmd.c523
-rw-r--r--drivers/staging/comedi/drivers/pcl711.c623
-rw-r--r--drivers/staging/comedi/drivers/pcl724.c222
-rw-r--r--drivers/staging/comedi/drivers/pcl725.c111
-rw-r--r--drivers/staging/comedi/drivers/pcl726.c377
-rw-r--r--drivers/staging/comedi/drivers/pcl730.c168
-rw-r--r--drivers/staging/comedi/drivers/pcl812.c1605
-rw-r--r--drivers/staging/comedi/drivers/pcl816.c1251
-rw-r--r--drivers/staging/comedi/drivers/pcl818.c1988
-rw-r--r--drivers/staging/comedi/drivers/pcm3724.c307
-rw-r--r--drivers/staging/comedi/drivers/pcm3730.c152
-rw-r--r--drivers/staging/comedi/drivers/pcmad.c173
-rw-r--r--drivers/staging/comedi/drivers/pcmda12.c306
-rw-r--r--drivers/staging/comedi/drivers/pcmmio.c1333
-rw-r--r--drivers/staging/comedi/drivers/pcmuio.c1101
-rw-r--r--drivers/staging/comedi/drivers/plx9052.h86
-rw-r--r--drivers/staging/comedi/drivers/plx9080.h80
-rw-r--r--drivers/staging/comedi/drivers/poc.c247
-rw-r--r--drivers/staging/comedi/drivers/quatech_daqp_cs.c1363
-rw-r--r--drivers/staging/comedi/drivers/rtd520.c277
-rw-r--r--drivers/staging/comedi/drivers/rtd520.h676
-rw-r--r--drivers/staging/comedi/drivers/rti800.c458
-rw-r--r--drivers/staging/comedi/drivers/rti802.c151
-rw-r--r--drivers/staging/comedi/drivers/s526.c977
-rw-r--r--drivers/staging/comedi/drivers/s626.c2538
-rw-r--r--drivers/staging/comedi/drivers/s626.h1226
-rw-r--r--drivers/staging/comedi/drivers/serial2002.c870
-rw-r--r--drivers/staging/comedi/drivers/skel.c624
-rw-r--r--drivers/staging/comedi/drivers/ssv_dnp.c311
-rw-r--r--drivers/staging/comedi/drivers/unioxx5.c515
-rw-r--r--drivers/staging/comedi/drivers/usbdux.c304
-rw-r--r--drivers/staging/comedi/drivers/usbduxfast.c2028
196 files changed, 133686 insertions, 4217 deletions
diff --git a/drivers/staging/comedi/drivers/8253.h b/drivers/staging/comedi/drivers/8253.h
new file mode 100644
index 000000000000..08a11a5a16e6
--- /dev/null
+++ b/drivers/staging/comedi/drivers/8253.h
@@ -0,0 +1,420 @@
+/*
+ comedi/drivers/8253.h
+ Header file for 8253
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef _8253_H
+#define _8253_H
+
+#ifndef CMDTEST
+#include "../comedi.h"
+#else
+#include "../comedi.h"
+#endif
+
+#define i8253_cascade_ns_to_timer i8253_cascade_ns_to_timer_2div
+
+static inline void i8253_cascade_ns_to_timer_2div_old(int i8253_osc_base,
+ unsigned int *d1, unsigned int *d2, unsigned int *nanosec,
+ int round_mode)
+{
+ int divider;
+ int div1, div2;
+ int div1_glb, div2_glb, ns_glb;
+ int div1_lub, div2_lub, ns_lub;
+ int ns;
+
+ divider = (*nanosec + i8253_osc_base / 2) / i8253_osc_base;
+
+ /* find 2 integers 1<={x,y}<=65536 such that x*y is
+ close to divider */
+
+ div1_lub = div2_lub = 0;
+ div1_glb = div2_glb = 0;
+
+ ns_glb = 0;
+ ns_lub = 0xffffffff;
+
+ div2 = 0x10000;
+ for (div1 = divider / 65536 + 1; div1 < div2; div1++) {
+ div2 = divider / div1;
+
+ ns = i8253_osc_base * div1 * div2;
+ if (ns <= *nanosec && ns > ns_glb) {
+ ns_glb = ns;
+ div1_glb = div1;
+ div2_glb = div2;
+ }
+
+ div2++;
+ if (div2 <= 65536) {
+ ns = i8253_osc_base * div1 * div2;
+ if (ns > *nanosec && ns < ns_lub) {
+ ns_lub = ns;
+ div1_lub = div1;
+ div2_lub = div2;
+ }
+ }
+ }
+
+ *nanosec = div1_lub * div2_lub * i8253_osc_base;
+ *d1 = div1_lub & 0xffff;
+ *d2 = div2_lub & 0xffff;
+ return;
+}
+
+static inline void i8253_cascade_ns_to_timer_power(int i8253_osc_base,
+ unsigned int *d1, unsigned int *d2, unsigned int *nanosec,
+ int round_mode)
+{
+ int div1, div2;
+ int base;
+
+ for (div1 = 2; div1 <= (1 << 16); div1 <<= 1) {
+ base = i8253_osc_base * div1;
+ round_mode &= TRIG_ROUND_MASK;
+ switch (round_mode) {
+ case TRIG_ROUND_NEAREST:
+ default:
+ div2 = (*nanosec + base / 2) / base;
+ break;
+ case TRIG_ROUND_DOWN:
+ div2 = (*nanosec) / base;
+ break;
+ case TRIG_ROUND_UP:
+ div2 = (*nanosec + base - 1) / base;
+ break;
+ }
+ if (div2 < 2)
+ div2 = 2;
+ if (div2 <= 65536) {
+ *nanosec = div2 * base;
+ *d1 = div1 & 0xffff;
+ *d2 = div2 & 0xffff;
+ return;
+ }
+ }
+
+ /* shouldn't get here */
+ div1 = 0x10000;
+ div2 = 0x10000;
+ *nanosec = div1 * div2 * i8253_osc_base;
+ *d1 = div1 & 0xffff;
+ *d2 = div2 & 0xffff;
+}
+
+static inline void i8253_cascade_ns_to_timer_2div(int i8253_osc_base,
+ unsigned int *d1, unsigned int *d2, unsigned int *nanosec,
+ int round_mode)
+{
+ unsigned int divider;
+ unsigned int div1, div2;
+ unsigned int div1_glb, div2_glb, ns_glb;
+ unsigned int div1_lub, div2_lub, ns_lub;
+ unsigned int ns;
+ unsigned int start;
+ unsigned int ns_low, ns_high;
+ static const unsigned int max_count = 0x10000;
+ /* exit early if everything is already correct (this can save time
+ * since this function may be called repeatedly during command tests
+ * and execution) */
+ div1 = *d1 ? *d1 : max_count;
+ div2 = *d2 ? *d2 : max_count;
+ divider = div1 * div2;
+ if (div1 * div2 * i8253_osc_base == *nanosec &&
+ div1 > 1 && div1 <= max_count &&
+ div2 > 1 && div2 <= max_count &&
+ /* check for overflow */
+ divider > div1 && divider > div2 &&
+ divider * i8253_osc_base > divider &&
+ divider * i8253_osc_base > i8253_osc_base) {
+ return;
+ }
+
+ divider = *nanosec / i8253_osc_base;
+
+ div1_lub = div2_lub = 0;
+ div1_glb = div2_glb = 0;
+
+ ns_glb = 0;
+ ns_lub = 0xffffffff;
+
+ div2 = max_count;
+ start = divider / div2;
+ if (start < 2)
+ start = 2;
+ for (div1 = start; div1 <= divider / div1 + 1 && div1 <= max_count;
+ div1++) {
+ for (div2 = divider / div1;
+ div1 * div2 <= divider + div1 + 1 && div2 <= max_count;
+ div2++) {
+ ns = i8253_osc_base * div1 * div2;
+ if (ns <= *nanosec && ns > ns_glb) {
+ ns_glb = ns;
+ div1_glb = div1;
+ div2_glb = div2;
+ }
+ if (ns >= *nanosec && ns < ns_lub) {
+ ns_lub = ns;
+ div1_lub = div1;
+ div2_lub = div2;
+ }
+ }
+ }
+
+ round_mode &= TRIG_ROUND_MASK;
+ switch (round_mode) {
+ case TRIG_ROUND_NEAREST:
+ default:
+ ns_high = div1_lub * div2_lub * i8253_osc_base;
+ ns_low = div1_glb * div2_glb * i8253_osc_base;
+ if (ns_high - *nanosec < *nanosec - ns_low) {
+ div1 = div1_lub;
+ div2 = div2_lub;
+ } else {
+ div1 = div1_glb;
+ div2 = div2_glb;
+ }
+ break;
+ case TRIG_ROUND_UP:
+ div1 = div1_lub;
+ div2 = div2_lub;
+ break;
+ case TRIG_ROUND_DOWN:
+ div1 = div1_glb;
+ div2 = div2_glb;
+ break;
+ }
+
+ *nanosec = div1 * div2 * i8253_osc_base;
+ *d1 = div1 & 0xffff; // masking is done since counter maps zero to 0x10000
+ *d2 = div2 & 0xffff;
+ return;
+}
+
+#ifndef CMDTEST
+/* i8254_load programs 8254 counter chip. It should also work for the 8253.
+ * base_address is the lowest io address for the chip (the address of counter 0).
+ * counter_number is the counter you want to load (0,1 or 2)
+ * count is the number to load into the counter.
+ *
+ * You probably want to use mode 2.
+ *
+ * Use i8254_mm_load() if you board uses memory-mapped io, it is
+ * the same as i8254_load() except it uses writeb() instead of outb().
+ *
+ * Neither i8254_load() or i8254_read() do their loading/reading
+ * atomically. The 16 bit read/writes are performed with two successive
+ * 8 bit read/writes. So if two parts of your driver do a load/read on
+ * the same counter, it may be necessary to protect these functions
+ * with a spinlock.
+ *
+ * FMH
+ */
+
+#define i8254_control_reg 3
+
+static inline int i8254_load(unsigned long base_address, unsigned int regshift,
+ unsigned int counter_number, unsigned int count, unsigned int mode)
+{
+ unsigned int byte;
+
+ if (counter_number > 2)
+ return -1;
+ if (count > 0xffff)
+ return -1;
+ if (mode > 5)
+ return -1;
+ if ((mode == 2 || mode == 3) && count == 1)
+ return -1;
+
+ byte = counter_number << 6;
+ byte |= 0x30; // load low then high byte
+ byte |= (mode << 1); // set counter mode
+ outb(byte, base_address + (i8254_control_reg << regshift));
+ byte = count & 0xff; // lsb of counter value
+ outb(byte, base_address + (counter_number << regshift));
+ byte = (count >> 8) & 0xff; // msb of counter value
+ outb(byte, base_address + (counter_number << regshift));
+
+ return 0;
+}
+
+static inline int i8254_mm_load(void *base_address, unsigned int regshift,
+ unsigned int counter_number, unsigned int count, unsigned int mode)
+{
+ unsigned int byte;
+
+ if (counter_number > 2)
+ return -1;
+ if (count > 0xffff)
+ return -1;
+ if (mode > 5)
+ return -1;
+ if ((mode == 2 || mode == 3) && count == 1)
+ return -1;
+
+ byte = counter_number << 6;
+ byte |= 0x30; // load low then high byte
+ byte |= (mode << 1); // set counter mode
+ writeb(byte, base_address + (i8254_control_reg << regshift));
+ byte = count & 0xff; // lsb of counter value
+ writeb(byte, base_address + (counter_number << regshift));
+ byte = (count >> 8) & 0xff; // msb of counter value
+ writeb(byte, base_address + (counter_number << regshift));
+
+ return 0;
+}
+
+/* Returns 16 bit counter value, should work for 8253 also.*/
+static inline int i8254_read(unsigned long base_address, unsigned int regshift,
+ unsigned int counter_number)
+{
+ unsigned int byte;
+ int ret;
+
+ if (counter_number > 2)
+ return -1;
+
+ // latch counter
+ byte = counter_number << 6;
+ outb(byte, base_address + (i8254_control_reg << regshift));
+
+ // read lsb
+ ret = inb(base_address + (counter_number << regshift));
+ // read msb
+ ret += inb(base_address + (counter_number << regshift)) << 8;
+
+ return ret;
+}
+
+static inline int i8254_mm_read(void *base_address, unsigned int regshift,
+ unsigned int counter_number)
+{
+ unsigned int byte;
+ int ret;
+
+ if (counter_number > 2)
+ return -1;
+
+ // latch counter
+ byte = counter_number << 6;
+ writeb(byte, base_address + (i8254_control_reg << regshift));
+
+ // read lsb
+ ret = readb(base_address + (counter_number << regshift));
+ // read msb
+ ret += readb(base_address + (counter_number << regshift)) << 8;
+
+ return ret;
+}
+
+/* Loads 16 bit initial counter value, should work for 8253 also. */
+static inline void i8254_write(unsigned long base_address,
+ unsigned int regshift, unsigned int counter_number, unsigned int count)
+{
+ unsigned int byte;
+
+ if (counter_number > 2)
+ return;
+
+ byte = count & 0xff; // lsb of counter value
+ outb(byte, base_address + (counter_number << regshift));
+ byte = (count >> 8) & 0xff; // msb of counter value
+ outb(byte, base_address + (counter_number << regshift));
+}
+
+static inline void i8254_mm_write(void *base_address,
+ unsigned int regshift, unsigned int counter_number, unsigned int count)
+{
+ unsigned int byte;
+
+ if (counter_number > 2)
+ return;
+
+ byte = count & 0xff; // lsb of counter value
+ writeb(byte, base_address + (counter_number << regshift));
+ byte = (count >> 8) & 0xff; // msb of counter value
+ writeb(byte, base_address + (counter_number << regshift));
+}
+
+/* Set counter mode, should work for 8253 also.
+ * Note: the 'mode' value is different to that for i8254_load() and comes
+ * from the INSN_CONFIG_8254_SET_MODE command:
+ * I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
+ * OR'ed with:
+ * I8254_BCD, I8254_BINARY
+ */
+static inline int i8254_set_mode(unsigned long base_address,
+ unsigned int regshift, unsigned int counter_number, unsigned int mode)
+{
+ unsigned int byte;
+
+ if (counter_number > 2)
+ return -1;
+ if (mode > (I8254_MODE5 | I8254_BINARY))
+ return -1;
+
+ byte = counter_number << 6;
+ byte |= 0x30; // load low then high byte
+ byte |= mode; // set counter mode and BCD|binary
+ outb(byte, base_address + (i8254_control_reg << regshift));
+
+ return 0;
+}
+
+static inline int i8254_mm_set_mode(void *base_address,
+ unsigned int regshift, unsigned int counter_number, unsigned int mode)
+{
+ unsigned int byte;
+
+ if (counter_number > 2)
+ return -1;
+ if (mode > (I8254_MODE5 | I8254_BINARY))
+ return -1;
+
+ byte = counter_number << 6;
+ byte |= 0x30; // load low then high byte
+ byte |= mode; // set counter mode and BCD|binary
+ writeb(byte, base_address + (i8254_control_reg << regshift));
+
+ return 0;
+}
+
+static inline int i8254_status(unsigned long base_address,
+ unsigned int regshift, unsigned int counter_number)
+{
+ outb(0xE0 | (2 << counter_number),
+ base_address + (i8254_control_reg << regshift));
+ return inb(base_address + (counter_number << regshift));
+}
+
+static inline int i8254_mm_status(void *base_address,
+ unsigned int regshift, unsigned int counter_number)
+{
+ writeb(0xE0 | (2 << counter_number),
+ base_address + (i8254_control_reg << regshift));
+ return readb(base_address + (counter_number << regshift));
+}
+
+#endif
+
+#endif
diff --git a/drivers/staging/comedi/drivers/8255.c b/drivers/staging/comedi/drivers/8255.c
new file mode 100644
index 000000000000..0369c7c84ac5
--- /dev/null
+++ b/drivers/staging/comedi/drivers/8255.c
@@ -0,0 +1,442 @@
+/*
+ comedi/drivers/8255.c
+ Driver for 8255
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: 8255
+Description: generic 8255 support
+Devices: [standard] 8255 (8255)
+Author: ds
+Status: works
+Updated: Fri, 7 Jun 2002 12:56:45 -0700
+
+The classic in digital I/O. The 8255 appears in Comedi as a single
+digital I/O subdevice with 24 channels. The channel 0 corresponds
+to the 8255's port A, bit 0; channel 23 corresponds to port C, bit
+7. Direction configuration is done in blocks, with channels 0-7,
+8-15, 16-19, and 20-23 making up the 4 blocks. The only 8255 mode
+supported is mode 0.
+
+You should enable compilation 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.
+
+This driver also works independently with ISA and PCI cards that
+directly map the 8255 registers to I/O ports, including cards with
+multiple 8255 chips. To configure the driver for such a card, the
+option list should be a list of the I/O port bases for each of the
+8255 chips. For example,
+
+ comedi_config /dev/comedi0 8255 0x200,0x204,0x208,0x20c
+
+Note that most PCI 8255 boards do NOT work with this driver, and
+need a separate driver as a wrapper. For those that do work, the
+I/O port base address can be found in the output of 'lspci -v'.
+
+*/
+
+/*
+ This file contains an exported subdevice for driving an 8255.
+
+ To use this subdevice as part of another driver, you need to
+ set up the subdevice in the attach function of the driver by
+ calling:
+
+ subdev_8255_init(device, subdevice, callback_function, arg)
+
+ device and subdevice are pointers to the device and subdevice
+ structures. callback_function will be called to provide the
+ low-level input/output to the device, i.e., actual register
+ access. callback_function will be called with the value of arg
+ as the last parameter. If the 8255 device is mapped as 4
+ consecutive I/O ports, you can use NULL for callback_function
+ and the I/O port base for arg, and an internal function will
+ handle the register access.
+
+ In addition, if the main driver handles interrupts, you can
+ enable commands on the subdevice by calling subdev_8255_init_irq()
+ instead. Then, when you get an interrupt that is likely to be
+ from the 8255, you should call subdev_8255_interrupt(), which
+ will copy the latched value to a Comedi buffer.
+ */
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+#define _8255_SIZE 4
+
+#define _8255_DATA 0
+#define _8255_CR 3
+
+#define CR_C_LO_IO 0x01
+#define CR_B_IO 0x02
+#define CR_B_MODE 0x04
+#define CR_C_HI_IO 0x08
+#define CR_A_IO 0x10
+#define CR_A_MODE(a) ((a)<<5)
+#define CR_CW 0x80
+
+struct subdev_8255_struct {
+ unsigned long cb_arg;
+ int (*cb_func) (int, int, int, unsigned long);
+ int have_irq;
+};
+
+#define CALLBACK_ARG (((struct subdev_8255_struct *)s->private)->cb_arg)
+#define CALLBACK_FUNC (((struct subdev_8255_struct *)s->private)->cb_func)
+#define subdevpriv ((struct subdev_8255_struct *)s->private)
+
+static int dev_8255_attach(struct comedi_device *dev, struct comedi_devconfig * it);
+static int dev_8255_detach(struct comedi_device *dev);
+static struct comedi_driver driver_8255 = {
+ driver_name:"8255",
+ module:THIS_MODULE,
+ attach:dev_8255_attach,
+ detach:dev_8255_detach,
+};
+
+COMEDI_INITCLEANUP(driver_8255);
+
+static void do_config(struct comedi_device *dev, struct comedi_subdevice * s);
+
+void subdev_8255_interrupt(struct comedi_device *dev, struct comedi_subdevice * s)
+{
+ short d;
+
+ d = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG);
+ d |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8);
+
+ comedi_buf_put(s->async, d);
+ s->async->events |= COMEDI_CB_EOS;
+
+ comedi_event(dev, s);
+}
+
+static int subdev_8255_cb(int dir, int port, int data, unsigned long arg)
+{
+ unsigned long iobase = arg;
+
+ if (dir) {
+ outb(data, iobase + port);
+ return 0;
+ } else {
+ return inb(iobase + port);
+ }
+}
+
+static int subdev_8255_insn(struct comedi_device *dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+
+ if (data[0] & 0xff)
+ CALLBACK_FUNC(1, _8255_DATA, s->state & 0xff,
+ CALLBACK_ARG);
+ if (data[0] & 0xff00)
+ CALLBACK_FUNC(1, _8255_DATA + 1, (s->state >> 8) & 0xff,
+ CALLBACK_ARG);
+ if (data[0] & 0xff0000)
+ CALLBACK_FUNC(1, _8255_DATA + 2,
+ (s->state >> 16) & 0xff, CALLBACK_ARG);
+ }
+
+ data[1] = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG);
+ data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8);
+ data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 2, 0, CALLBACK_ARG) << 16);
+
+ return 2;
+}
+
+static int subdev_8255_insn_config(struct comedi_device *dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int mask;
+ unsigned int bits;
+
+ mask = 1 << CR_CHAN(insn->chanspec);
+ if (mask & 0x0000ff) {
+ bits = 0x0000ff;
+ } else if (mask & 0x00ff00) {
+ bits = 0x00ff00;
+ } else if (mask & 0x0f0000) {
+ bits = 0x0f0000;
+ } else {
+ bits = 0xf00000;
+ }
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~bits;
+ break;
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= bits;
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ do_config(dev, s);
+
+ return 1;
+}
+
+static void do_config(struct comedi_device *dev, struct 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);
+}
+
+static int subdev_8255_cmdtest(struct comedi_device *dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ unsigned int tmp;
+
+ /* step 1 */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_FOLLOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2 */
+
+ if (err)
+ return 2;
+
+ /* step 3 */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ if (cmd->scan_end_arg != 1) {
+ cmd->scan_end_arg = 1;
+ err++;
+ }
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4 */
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int subdev_8255_cmd(struct comedi_device *dev, struct comedi_subdevice * s)
+{
+ /* FIXME */
+
+ return 0;
+}
+
+static int subdev_8255_cancel(struct comedi_device *dev, struct comedi_subdevice * s)
+{
+ /* FIXME */
+
+ return 0;
+}
+
+int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice * s, int (*cb) (int,
+ int, int, unsigned long), unsigned long arg)
+{
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 24;
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+
+ s->private = kmalloc(sizeof(struct subdev_8255_struct), GFP_KERNEL);
+ if (!s->private)
+ return -ENOMEM;
+
+ CALLBACK_ARG = arg;
+ if (cb == NULL) {
+ CALLBACK_FUNC = subdev_8255_cb;
+ } else {
+ CALLBACK_FUNC = cb;
+ }
+ s->insn_bits = subdev_8255_insn;
+ s->insn_config = subdev_8255_insn_config;
+
+ s->state = 0;
+ s->io_bits = 0;
+ do_config(dev, s);
+
+ return 0;
+}
+
+int subdev_8255_init_irq(struct comedi_device *dev, struct comedi_subdevice * s,
+ int (*cb) (int, int, int, unsigned long), unsigned long arg)
+{
+ int ret;
+
+ ret = subdev_8255_init(dev, s, cb, arg);
+ if (ret < 0)
+ return ret;
+
+ s->do_cmdtest = subdev_8255_cmdtest;
+ s->do_cmd = subdev_8255_cmd;
+ s->cancel = subdev_8255_cancel;
+
+ subdevpriv->have_irq = 1;
+
+ return 0;
+}
+
+void subdev_8255_cleanup(struct comedi_device *dev, struct comedi_subdevice * s)
+{
+ if (s->private) {
+ if (subdevpriv->have_irq) {
+ }
+
+ kfree(s->private);
+ }
+}
+
+/*
+
+ Start of the 8255 standalone device
+
+ */
+
+static int dev_8255_attach(struct comedi_device *dev, struct comedi_devconfig * it)
+{
+ int ret;
+ unsigned long iobase;
+ int i;
+
+ printk("comedi%d: 8255:", dev->minor);
+
+ dev->board_name = "8255";
+
+ for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) {
+ iobase = it->options[i];
+ if (!iobase)
+ break;
+ }
+ if (i == 0) {
+ printk(" no devices specified\n");
+ return -EINVAL;
+ }
+
+ if ((ret = alloc_subdevices(dev, i)) < 0)
+ return ret;
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ iobase = it->options[i];
+
+ printk(" 0x%04lx", iobase);
+ if (!request_region(iobase, _8255_SIZE, "8255")) {
+ printk(" (I/O port conflict)");
+
+ dev->subdevices[i].type = COMEDI_SUBD_UNUSED;
+ } else {
+ subdev_8255_init(dev, dev->subdevices + i, NULL,
+ iobase);
+ }
+ }
+
+ printk("\n");
+
+ return 0;
+}
+
+static int dev_8255_detach(struct comedi_device *dev)
+{
+ int i;
+ unsigned long iobase;
+ struct comedi_subdevice *s;
+
+ printk("comedi%d: 8255: remove\n", dev->minor);
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = dev->subdevices + i;
+ if (s->type != COMEDI_SUBD_UNUSED) {
+ iobase = CALLBACK_ARG;
+ release_region(iobase, _8255_SIZE);
+ }
+ subdev_8255_cleanup(dev, s);
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL(subdev_8255_init);
+EXPORT_SYMBOL(subdev_8255_init_irq);
+EXPORT_SYMBOL(subdev_8255_cleanup);
+EXPORT_SYMBOL(subdev_8255_interrupt);
diff --git a/drivers/staging/comedi/drivers/8255.h b/drivers/staging/comedi/drivers/8255.h
new file mode 100644
index 000000000000..979311b99275
--- /dev/null
+++ b/drivers/staging/comedi/drivers/8255.h
@@ -0,0 +1,57 @@
+/*
+ module/8255.h
+ Header file for 8255
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef _8255_H
+#define _8255_H
+
+#include "../comedidev.h"
+
+#if defined(CONFIG_COMEDI_8255) || defined(CONFIG_COMEDI_8255_MODULE)
+
+int subdev_8255_init(struct comedi_device * dev, struct comedi_subdevice * s,
+ int (*cb) (int, int, int, unsigned long), unsigned long arg);
+int subdev_8255_init_irq(struct comedi_device * dev, struct comedi_subdevice * s,
+ int (*cb) (int, int, int, unsigned long), unsigned long arg);
+void subdev_8255_cleanup(struct comedi_device * dev, struct comedi_subdevice * s);
+void subdev_8255_interrupt(struct comedi_device * dev, struct comedi_subdevice * s);
+
+#else
+
+static inline int subdev_8255_init(struct comedi_device * dev, struct comedi_subdevice * s,
+ void *x, unsigned long y)
+{
+ printk("8255 support not configured -- disabling subdevice\n");
+
+ s->type = COMEDI_SUBD_UNUSED;
+
+ return 0;
+}
+
+static inline void subdev_8255_cleanup(struct comedi_device * dev,
+ struct comedi_subdevice * s)
+{
+}
+
+#endif
+
+#endif
diff --git a/drivers/staging/comedi/drivers/Makefile b/drivers/staging/comedi/drivers/Makefile
index eb7a615e0859..12d6e4312f3a 100644
--- a/drivers/staging/comedi/drivers/Makefile
+++ b/drivers/staging/comedi/drivers/Makefile
@@ -8,12 +8,121 @@ obj-$(CONFIG_COMEDI) += comedi_test.o
obj-$(CONFIG_COMEDI) += comedi_parport.o
# Comedi PCI drivers
-obj-$(CONFIG_COMEDI_PCI_DRIVERS) += mite.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += 8255.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += acl7225b.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += addi_apci_035.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += addi_apci_1032.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += addi_apci_1500.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += addi_apci_1516.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += addi_apci_1564.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += addi_apci_16xx.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += addi_apci_2016.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += addi_apci_2032.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += addi_apci_2200.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += addi_apci_3001.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += addi_apci_3120.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += addi_apci_3501.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += addi_apci_3xxx.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += adl_pci6208.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += adl_pci7296.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += adl_pci7432.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += adl_pci8164.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += adl_pci9111.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += adl_pci9118.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += adq12b.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += adv_pci1710.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += adv_pci1723.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += adv_pci_dio.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += aio_aio12_8.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += aio_iiro_16.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += amplc_dio200.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += amplc_pc236.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += amplc_pc263.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += amplc_pci224.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += amplc_pci230.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += c6xdigio.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += cb_pcidas64.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += cb_pcidas.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += cb_pcidda.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += cb_pcidio.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += cb_pcimdas.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += cb_pcimdda.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += comedi_bond.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += comedi_parport.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += comedi_test.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += contec_pci_dio.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += daqboard2000.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += das08.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += das16m1.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += das16.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += das1800.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += das6402.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += das800.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += dmm32at.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += dt2801.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += dt2811.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += dt2814.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += dt2815.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += dt2817.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += dt282x.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += dt3000.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += fl512.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += gsc_hpdi.o
obj-$(CONFIG_COMEDI_PCI_DRIVERS) += icp_multi.o
-obj-$(CONFIG_COMEDI_PCI_DRIVERS) += me_daq.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ii_pci20kc.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += jr3_pci.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ke_counter.o
obj-$(CONFIG_COMEDI_PCI_DRIVERS) += me4000.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += me_daq.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += mite.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += mpc624.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += multiq3.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ni_6527.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ni_65xx.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ni_660x.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ni_670x.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ni_at_a2150.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ni_at_ao.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ni_atmio16d.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ni_atmio.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ni_labpc.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ni_pcidio.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ni_pcimio.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ni_tiocmd.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ni_tio.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += pcl711.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += pcl724.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += pcl725.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += pcl726.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += pcl730.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += pcl812.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += pcl816.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += pcl818.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += pcm3724.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += pcm3730.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += pcmad.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += pcmda12.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += pcmmio.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += pcmuio.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += poc.o
obj-$(CONFIG_COMEDI_PCI_DRIVERS) += rtd520.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += rti800.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += rti802.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += s526.o
obj-$(CONFIG_COMEDI_PCI_DRIVERS) += s626.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += serial2002.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += skel.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += ssv_dnp.o
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += unioxx5.o
+
+# Comedi PCMCIA drivers
+obj-$(CONFIG_COMEDI_PCMCIA_DRIVERS) += cb_das16_cs.o
+obj-$(CONFIG_COMEDI_PCMCIA_DRIVERS) += das08_cs.o
+obj-$(CONFIG_COMEDI_PCMCIA_DRIVERS) += ni_daq_700.o
+obj-$(CONFIG_COMEDI_PCMCIA_DRIVERS) += ni_daq_dio24.o
+obj-$(CONFIG_COMEDI_PCMCIA_DRIVERS) += ni_labpc_cs.o
+obj-$(CONFIG_COMEDI_PCMCIA_DRIVERS) += ni_mio_cs.o
+obj-$(CONFIG_COMEDI_PCMCIA_DRIVERS) += quatech_daqp_cs.o
# Comedi USB drivers
obj-$(CONFIG_COMEDI_USB_DRIVERS) += usbdux.o
diff --git a/drivers/staging/comedi/drivers/acl7225b.c b/drivers/staging/comedi/drivers/acl7225b.c
new file mode 100644
index 000000000000..b21320f0959f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/acl7225b.c
@@ -0,0 +1,149 @@
+/*
+ * comedi/drivers/acl7225b.c
+ * Driver for Adlink NuDAQ ACL-7225b and clones
+ * José Luis Sánchez
+ */
+/*
+Driver: acl7225b
+Description: Adlink NuDAQ ACL-7225b & compatibles
+Author: José Luis Sánchez (jsanchezv@teleline.es)
+Status: testing
+Devices: [Adlink] ACL-7225b (acl7225b), [ICP] P16R16DIO (p16r16dio)
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+#define ACL7225_SIZE 8 /* Requires 8 ioports, but only 4 are used */
+#define P16R16DIO_SIZE 4
+#define ACL7225_RIO_LO 0 /* Relays input/output low byte (R0-R7) */
+#define ACL7225_RIO_HI 1 /* Relays input/output high byte (R8-R15) */
+#define ACL7225_DI_LO 2 /* Digital input low byte (DI0-DI7) */
+#define ACL7225_DI_HI 3 /* Digital input high byte (DI8-DI15) */
+
+static int acl7225b_attach(struct comedi_device *dev, struct comedi_devconfig * it);
+static int acl7225b_detach(struct comedi_device *dev);
+
+struct boardtype {
+ const char *name; // driver name
+ int io_range; // len of I/O space
+};
+
+static const struct boardtype boardtypes[] = {
+ {"acl7225b", ACL7225_SIZE,},
+ {"p16r16dio", P16R16DIO_SIZE,},
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype))
+#define this_board ((const struct boardtype *)dev->board_ptr)
+
+static struct comedi_driver driver_acl7225b = {
+ driver_name:"acl7225b",
+ module:THIS_MODULE,
+ attach:acl7225b_attach,
+ detach:acl7225b_detach,
+ board_name:&boardtypes[0].name,
+ num_names:n_boardtypes,
+ offset:sizeof(struct boardtype),
+};
+
+COMEDI_INITCLEANUP(driver_acl7225b);
+
+static int acl7225b_do_insn(struct comedi_device *dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ }
+ if (data[0] & 0x00ff)
+ outb(s->state & 0xff, dev->iobase + (unsigned long)s->private);
+ if (data[0] & 0xff00)
+ outb((s->state >> 8),
+ dev->iobase + (unsigned long)s->private + 1);
+
+ data[1] = s->state;
+
+ return 2;
+}
+
+static int acl7225b_di_insn(struct comedi_device *dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ data[1] = inb(dev->iobase + (unsigned long)s->private) |
+ (inb(dev->iobase + (unsigned long)s->private + 1) << 8);
+
+ return 2;
+}
+
+static int acl7225b_attach(struct comedi_device *dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int iobase, iorange;
+
+ iobase = it->options[0];
+ iorange = this_board->io_range;
+ printk("comedi%d: acl7225b: board=%s 0x%04x ", dev->minor,
+ this_board->name, iobase);
+ if (!request_region(iobase, iorange, "acl7225b")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->board_name = this_board->name;
+ dev->iobase = iobase;
+ dev->irq = 0;
+
+ if (alloc_subdevices(dev, 3) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ /* Relays outputs */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->maxdata = 1;
+ s->n_chan = 16;
+ s->insn_bits = acl7225b_do_insn;
+ s->range_table = &range_digital;
+ s->private = (void *)ACL7225_RIO_LO;
+
+ s = dev->subdevices + 1;
+ /* Relays status */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->maxdata = 1;
+ s->n_chan = 16;
+ s->insn_bits = acl7225b_di_insn;
+ s->range_table = &range_digital;
+ s->private = (void *)ACL7225_RIO_LO;
+
+ s = dev->subdevices + 2;
+ /* Isolated digital inputs */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->maxdata = 1;
+ s->n_chan = 16;
+ s->insn_bits = acl7225b_di_insn;
+ s->range_table = &range_digital;
+ s->private = (void *)ACL7225_DI_LO;
+
+ printk("\n");
+
+ return 0;
+}
+
+static int acl7225b_detach(struct comedi_device *dev)
+{
+ printk("comedi%d: acl7225b: remove\n", dev->minor);
+
+ if (dev->iobase)
+ release_region(dev->iobase, this_board->io_range);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_82x54.c b/drivers/staging/comedi/drivers/addi-data/APCI1710_82x54.c
new file mode 100644
index 000000000000..c96aee09e1b6
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_82x54.c
@@ -0,0 +1,1047 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+/*
+ | Description : APCI-1710 82X54 timer module |
+*/
+
+#include "APCI1710_82x54.h"
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_InitTimer |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_TimerNbr, |
+| BYTE_ b_TimerMode, |
+| ULONG_ ul_ReloadValue, |
+| BYTE_ b_InputClockSelection, |
+| BYTE_ b_InputClockLevel, |
+| BYTE_ b_OutputLevel, |
+| BYTE_ b_HardwareGateLevel)
+INT i_InsnConfig_InitTimer(struct comedi_device *dev,struct comedi_subdevice *s,
+ struct comedi_insn *insn,unsigned int *data)
+|
++----------------------------------------------------------------------------+
+| Task : Configure the Timer (b_TimerNbr) operating mode |
+| (b_TimerMode) from selected module (b_ModulNbr). |
+| You must calling this function be for you call any |
+| other function witch access of the timer. |
+| |
+| |
+| Timer mode description table |
+| |
+|+--------+-----------------------------+--------------+--------------------+|
+||Selected+ Mode description +u_ReloadValue | Hardware gate input||
+|| mode | | description | action ||
+|+--------+-----------------------------+--------------+--------------------+|
+|| |Mode 0 is typically used | | ||
+|| |for event counting. After | | ||
+|| |the initialisation, OUT | | ||
+|| |is initially low, and | | ||
+|| 0 |will remain low until the |Start counting| Hardware gate ||
+|| |counter reaches zero. | value | ||
+|| |OUT then goes high and | | ||
+|| |remains high until a new | | ||
+|| |count is written. See | | ||
+|| |"i_APCI1710_WriteTimerValue" | | ||
+|| |function. | | ||
+|+--------+-----------------------------+--------------+--------------------+|
+|| |Mode 1 is similar to mode 0 | | ||
+|| |except for the gate input | | ||
+|| 1 |action. The gate input is not|Start counting| Hardware trigger ||
+|| |used for enabled or disabled | value | ||
+|| |the timer. | | ||
+|| |The gate input is used for | | ||
+|| |triggered the timer. | | ||
+|+--------+-----------------------------+--------------+--------------------+|
+|| |This mode functions like a | | ||
+|| |divide-by-ul_ReloadValue | | ||
+|| |counter. It is typically used| | ||
+|| |to generate a real time clock| | ||
+|| |interrupt. OUT will initially| | ||
+|| 2 |be high after the | Division | Hardware gate ||
+|| |initialisation. When the | factor | ||
+|| |initial count has decremented| | ||
+|| |to 1, OUT goes low for one | | ||
+|| |CLK pule. OUT then goes high | | ||
+|| |again, the counter reloads | | ||
+|| |the initial count | | ||
+|| |(ul_ReloadValue) and the | | ||
+|| |process is repeated. | | ||
+|| |This action can generated a | | ||
+|| |interrupt. See function | | ||
+|| |"i_APCI1710_SetBoardInt- | | ||
+|| |RoutineX" | | ||
+|| |and "i_APCI1710_EnableTimer" | | ||
+|+--------+-----------------------------+--------------+--------------------+|
+|| |Mode 3 is typically used for | | ||
+|| |baud rate generation. This | | ||
+|| |mode is similar to mode 2 | | ||
+|| |except for the duty cycle of | | ||
+|| 3 |OUT. OUT will initially be | Division | Hardware gate ||
+|| |high after the initialisation| factor | ||
+|| |When half the initial count | | ||
+|| |(ul_ReloadValue) has expired,| | ||
+|| |OUT goes low for the | | ||
+|| |remainder of the count. The | | ||
+|| |mode is periodic; the | | ||
+|| |sequence above is repeated | | ||
+|| |indefinitely. | | ||
+|+--------+-----------------------------+--------------+--------------------+|
+|| |OUT will be initially high | | ||
+|| |after the initialisation. | | ||
+|| |When the initial count | | ||
+|| 4 |expires OUT will go low for |Start counting| Hardware gate ||
+|| |one CLK pulse and then go | value | ||
+|| |high again. | | ||
+|| |The counting sequences is | | ||
+|| |triggered by writing a new | | ||
+|| |value. See | | ||
+|| |"i_APCI1710_WriteTimerValue" | | ||
+|| |function. If a new count is | | ||
+|| |written during counting, | | ||
+|| |it will be loaded on the | | ||
+|| |next CLK pulse | | ||
+|+--------+-----------------------------+--------------+--------------------+|
+|| |Mode 5 is similar to mode 4 | | ||
+|| |except for the gate input | | ||
+|| |action. The gate input is not| | ||
+|| 5 |used for enabled or disabled |Start counting| Hardware trigger ||
+|| |the timer. The gate input is | value | ||
+|| |used for triggered the timer.| | ||
+|+--------+-----------------------------+--------------+--------------------+|
+| |
+| |
+| |
+| Input clock selection table |
+| |
+| +--------------------------------+------------------------------------+ |
+| | b_InputClockSelection | Description | |
+| | parameter | | |
+| +--------------------------------+------------------------------------+ |
+| | APCI1710_PCI_BUS_CLOCK | For the timer input clock, the PCI | |
+| | | bus clock / 4 is used. This PCI bus| |
+| | | clock can be 30MHz or 33MHz. For | |
+| | | Timer 0 only this selection are | |
+| | | available. | |
+| +--------------------------------+------------------------------------+ |
+| | APCI1710_ FRONT_CONNECTOR_INPUT| Of the front connector you have the| |
+| | | possibility to inject a input clock| |
+| | | for Timer 1 or Timer 2. The source | |
+| | | from this clock can eat the output | |
+| | | clock from Timer 0 or any other | |
+| | | clock source. | |
+| +--------------------------------+------------------------------------+ |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board |
+| APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to |
+| configure (0 to 3) |
+| BYTE_ b_TimerNbr : Timer number to |
+| configure (0 to 2) |
+| BYTE_ b_TimerMode : Timer mode selection |
+| (0 to 5) |
+| 0: Interrupt on terminal|
+| count |
+| 1: Hardware |
+| retriggerable one- |
+| shot |
+| 2: Rate generator |
+| 3: Square wave mode |
+| 4: Software triggered |
+| strobe |
+| 5: Hardware triggered |
+| strobe |
+| See timer mode |
+| description table. |
+| ULONG_ ul_ReloadValue : Start counting value |
+| or division factor |
+| See timer mode |
+| description table. |
+| BYTE_ b_InputClockSelection : Selection from input |
+| timer clock. |
+| See input clock |
+| selection table. |
+| BYTE_ b_InputClockLevel : Selection from input |
+| clock level. |
+| 0 : Low active |
+| (Input inverted) |
+| 1 : High active |
+| BYTE_ b_OutputLevel, : Selection from output |
+| clock level. |
+| 0 : Low active |
+| 1 : High active |
+| (Output inverted) |
+| BYTE_ b_HardwareGateLevel : Selection from |
+| hardware gate level. |
+| 0 : Low active |
+| (Input inverted) |
+| 1 : High active |
+| If you will not used |
+| the hardware gate set |
+| this value to 0.
+|b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_TimerNbr = (BYTE) CR_CHAN(insn->chanspec);
+ b_TimerMode = (BYTE) data[0];
+ ul_ReloadValue = (ULONG) data[1];
+ b_InputClockSelection =(BYTE) data[2];
+ b_InputClockLevel =(BYTE) data[3];
+ b_OutputLevel =(BYTE) data[4];
+ b_HardwareGateLevel =(BYTE) data[5];
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: Timer selection wrong |
+| -4: The module is not a TIMER module |
+| -5: Timer mode selection is wrong |
+| -6: Input timer clock selection is wrong |
+| -7: Selection from input clock level is wrong |
+| -8: Selection from output clock level is wrong |
+| -9: Selection from hardware gate level is wrong |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnConfigInitTimer(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ INT i_ReturnValue = 0;
+ BYTE b_ModulNbr;
+ BYTE b_TimerNbr;
+ BYTE b_TimerMode;
+ ULONG ul_ReloadValue;
+ BYTE b_InputClockSelection;
+ BYTE b_InputClockLevel;
+ BYTE b_OutputLevel;
+ BYTE b_HardwareGateLevel;
+
+ //BEGIN JK 27.10.2003 : Add the possibility to use a 40 Mhz quartz
+ DWORD dw_Test = 0;
+ //END JK 27.10.2003 : Add the possibility to use a 40 Mhz quartz
+
+ i_ReturnValue = insn->n;
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_TimerNbr = (BYTE) CR_CHAN(insn->chanspec);
+ b_TimerMode = (BYTE) data[0];
+ ul_ReloadValue = (ULONG) data[1];
+ b_InputClockSelection = (BYTE) data[2];
+ b_InputClockLevel = (BYTE) data[3];
+ b_OutputLevel = (BYTE) data[4];
+ b_HardwareGateLevel = (BYTE) data[5];
+
+ /* Test the module number */
+ if (b_ModulNbr < 4) {
+ /* Test if 82X54 timer */
+ if ((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF0000UL) == APCI1710_82X54_TIMER) {
+ /* Test the timer number */
+
+ if (b_TimerNbr <= 2) {
+ /* Test the timer mode */
+ if (b_TimerMode <= 5) {
+ //BEGIN JK 27.10.2003 : Add the possibility to use a 40 Mhz quartz
+ /* Test te imput clock selection */
+ /*
+ if (((b_TimerNbr == 0) && (b_InputClockSelection == 0)) ||
+ ((b_TimerNbr != 0) && ((b_InputClockSelection == 0) || (b_InputClockSelection == 1))))
+ */
+
+ if (((b_TimerNbr == 0) &&
+ (b_InputClockSelection == APCI1710_PCI_BUS_CLOCK)) ||
+ ((b_TimerNbr == 0) &&
+ (b_InputClockSelection == APCI1710_10MHZ)) ||
+ ((b_TimerNbr != 0) &&
+ ((b_InputClockSelection == APCI1710_PCI_BUS_CLOCK) ||
+ (b_InputClockSelection == APCI1710_FRONT_CONNECTOR_INPUT) ||
+ (b_InputClockSelection == APCI1710_10MHZ)))) {
+ //BEGIN JK 27.10.2003 : Add the possibility to use a 40 Mhz quartz
+ if (((b_InputClockSelection == APCI1710_10MHZ) &&
+ ((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0x0000FFFFUL) >= 0x3131)) ||
+ (b_InputClockSelection != APCI1710_10MHZ)) {
+ //END JK 27.10.2003 : Add the possibility to use a 40 Mhz quartz
+ /* Test the input clock level selection */
+
+ if ((b_InputClockLevel == 0) ||
+ (b_InputClockLevel == 1)) {
+ /* Test the output clock level selection */
+ if ((b_OutputLevel == 0) || (b_OutputLevel == 1)) {
+ /* Test the hardware gate level selection */
+ if ((b_HardwareGateLevel == 0) || (b_HardwareGateLevel == 1)) {
+ //BEGIN JK 27.10.03 : Add the possibility to use a 40 Mhz quartz
+ /* Test if version > 1.1 and clock selection = 10MHz */
+ if ((b_InputClockSelection == APCI1710_10MHZ) && ((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0x0000FFFFUL) > 0x3131)) {
+ /* Test if 40MHz quartz on board */
+ dw_Test = inl(devpriv->s_BoardInfos.ui_Address + (16 + (b_TimerNbr * 4) + (64 * b_ModulNbr)));
+
+ dw_Test = (dw_Test >> 16) & 1;
+ } else {
+ dw_Test = 1;
+ }
+
+ /* Test if detection OK */
+ if (dw_Test == 1) {
+ //END JK 27.10.03 : Add the possibility to use a 40 Mhz quartz
+ /* Initialisation OK */
+ devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].b_82X54Init = 1;
+
+ /* Save the input clock selection */
+ devpriv-> s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].b_InputClockSelection = b_InputClockSelection;
+
+ /* Save the input clock level */
+ devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].b_InputClockLevel = ~b_InputClockLevel & 1;
+
+ /* Save the output level */
+ devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].b_OutputLevel = ~b_OutputLevel & 1;
+
+ /* Save the gate level */
+ devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].b_HardwareGateLevel = b_HardwareGateLevel;
+
+ /* Set the configuration word and disable the timer */
+ //BEGIN JK 27.10.03 : Add the possibility to use a 40 Mhz quartz
+ /*
+ devpriv->s_ModuleInfo [b_ModulNbr].
+ s_82X54ModuleInfo.
+ s_82X54TimerInfo [b_TimerNbr].
+ dw_ConfigurationWord = (DWORD) (((b_HardwareGateLevel << 0) & 0x1) |
+ ((b_InputClockLevel << 1) & 0x2) |
+ (((~b_OutputLevel & 1) << 2) & 0x4) |
+ ((b_InputClockSelection << 4) & 0x10));
+ */
+ /* Test if 10MHz selected */
+ if (b_InputClockSelection == APCI1710_10MHZ) {
+ b_InputClockSelection = 2;
+ }
+
+ devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].dw_ConfigurationWord = (DWORD)(((b_HardwareGateLevel << 0) & 0x1) | ((b_InputClockLevel << 1) & 0x2) | (((~b_OutputLevel & 1) << 2) & 0x4) | ((b_InputClockSelection << 4) & 0x30));
+ //END JK 27.10.03 : Add the possibility to use a 40 Mhz quartz
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].dw_ConfigurationWord, devpriv->s_BoardInfos.ui_Address + 32 + (b_TimerNbr * 4) + (64 * b_ModulNbr));
+
+ /* Initialise the 82X54 Timer */
+ outl((DWORD) b_TimerMode, devpriv->s_BoardInfos.ui_Address + 16 + (b_TimerNbr * 4) + (64 * b_ModulNbr));
+
+ /* Write the reload value */
+ outl(ul_ReloadValue, devpriv->s_BoardInfos.ui_Address + 0 + (b_TimerNbr * 4) + (64 * b_ModulNbr));
+ //BEGIN JK 27.10.03 : Add the possibility to use a 40 Mhz quartz
+ } // if (dw_Test == 1)
+ else {
+ /* Input timer clock selection is wrong */
+ i_ReturnValue = -6;
+ } // if (dw_Test == 1)
+ //END JK 27.10.03 : Add the possibility to use a 40 Mhz quartz
+ } // if ((b_HardwareGateLevel == 0) || (b_HardwareGateLevel == 1))
+ else {
+ /* Selection from hardware gate level is wrong */
+ DPRINTK("Selection from hardware gate level is wrong\n");
+ i_ReturnValue = -9;
+ } // if ((b_HardwareGateLevel == 0) || (b_HardwareGateLevel == 1))
+ } // if ((b_OutputLevel == 0) || (b_OutputLevel == 1))
+ else {
+ /* Selection from output clock level is wrong */
+ DPRINTK("Selection from output clock level is wrong\n");
+ i_ReturnValue = -8;
+ } // if ((b_OutputLevel == 0) || (b_OutputLevel == 1))
+ } // if ((b_InputClockLevel == 0) || (b_InputClockLevel == 1))
+ else {
+ /* Selection from input clock level is wrong */
+ DPRINTK("Selection from input clock level is wrong\n");
+ i_ReturnValue = -7;
+ } // if ((b_InputClockLevel == 0) || (b_InputClockLevel == 1))
+ } else {
+ /* Input timer clock selection is wrong */
+ DPRINTK("Input timer clock selection is wrong\n");
+ i_ReturnValue = -6;
+ }
+ } else {
+ /* Input timer clock selection is wrong */
+ DPRINTK("Input timer clock selection is wrong\n");
+ i_ReturnValue = -6;
+ }
+ } // if ((b_TimerMode >= 0) && (b_TimerMode <= 5))
+ else {
+ /* Timer mode selection is wrong */
+ DPRINTK("Timer mode selection is wrong\n");
+ i_ReturnValue = -5;
+ } // if ((b_TimerMode >= 0) && (b_TimerMode <= 5))
+ } // if ((b_TimerNbr >= 0) && (b_TimerNbr <= 2))
+ else {
+ /* Timer selection wrong */
+ DPRINTK("Timer selection wrong\n");
+ i_ReturnValue = -3;
+ } // if ((b_TimerNbr >= 0) && (b_TimerNbr <= 2))
+ } else {
+ /* The module is not a TIMER module */
+ DPRINTK("The module is not a TIMER module\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /* Module number error */
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_EnableTimer |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_TimerNbr, |
+| BYTE_ b_InterruptEnable)
+INT i_APCI1710_InsnWriteEnableDisableTimer(struct comedi_device *dev,struct comedi_subdevice *s,
+ struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Enable OR Disable the Timer (b_TimerNbr) from selected module |
+| (b_ModulNbr). You must calling the |
+| "i_APCI1710_InitTimer" function be for you call this |
+| function. If you enable the timer interrupt, the timer |
+| generate a interrupt after the timer value reach |
+| the zero. See function "i_APCI1710_SetBoardIntRoutineX"|
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board |
+| APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number |
+| (0 to 3) |
+| BYTE_ b_TimerNbr : Timer number to enable |
+| (0 to 2) |
+| BYTE_ b_InterruptEnable : Enable or disable the |
+| timer interrupt. |
+| APCI1710_ENABLE : |
+| Enable the timer interrupt |
+| APCI1710_DISABLE : |
+| Disable the timer interrupt|
+i_ReturnValue=insn->n;
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_TimerNbr = (BYTE) CR_CHAN(insn->chanspec);
+ b_ActionType = (BYTE) data[0]; // enable disable
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: Timer selection wrong |
+| -4: The module is not a TIMER module |
+| -5: Timer not initialised see function |
+| "i_APCI1710_InitTimer" |
+| -6: Interrupt parameter is wrong |
+| -7: Interrupt function not initialised. |
+| See function "i_APCI1710_SetBoardIntRoutineX" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnWriteEnableDisableTimer(struct comedi_device * dev,
+ struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_DummyRead;
+ BYTE b_ModulNbr;
+ BYTE b_TimerNbr;
+ BYTE b_ActionType;
+ BYTE b_InterruptEnable;
+
+ i_ReturnValue = insn->n;
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_TimerNbr = (BYTE) CR_CHAN(insn->chanspec);
+ b_ActionType = (BYTE) data[0]; // enable disable
+
+ /* Test the module number */
+ if (b_ModulNbr < 4) {
+ /* Test if 82X54 timer */
+ if ((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF0000UL) == APCI1710_82X54_TIMER) {
+ /* Test the timer number */
+ if (b_TimerNbr <= 2) {
+ /* Test if timer initialised */
+ if (devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].b_82X54Init == 1) {
+
+ switch (b_ActionType) {
+ case APCI1710_ENABLE:
+ b_InterruptEnable = (BYTE) data[1];
+ /* Test the interrupt selection */
+ if ((b_InterruptEnable == APCI1710_ENABLE) ||
+ (b_InterruptEnable == APCI1710_DISABLE)) {
+ if (b_InterruptEnable == APCI1710_ENABLE) {
+
+ dw_DummyRead = inl(devpriv->s_BoardInfos.ui_Address + 12 + (b_TimerNbr * 4) + (64 * b_ModulNbr));
+
+ /* Enable the interrupt */
+ devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].dw_ConfigurationWord = devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].dw_ConfigurationWord | 0x8;
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].dw_ConfigurationWord, devpriv->s_BoardInfos.ui_Address + 32 + (b_TimerNbr * 4) + (64 * b_ModulNbr));
+ devpriv->tsk_Current = current; // Save the current process task structure
+
+ } // if (b_InterruptEnable == APCI1710_ENABLE)
+ else {
+ /* Disable the interrupt */
+ devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].dw_ConfigurationWord = devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].dw_ConfigurationWord & 0xF7;
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].dw_ConfigurationWord, devpriv->s_BoardInfos.ui_Address + 32 + (b_TimerNbr * 4) + (64 * b_ModulNbr));
+
+ /* Save the interrupt flag */
+ devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.b_InterruptMask = devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.b_InterruptMask & (0xFF - (1 << b_TimerNbr));
+ } // if (b_InterruptEnable == APCI1710_ENABLE)
+
+ /* Test if error occur */
+ if (i_ReturnValue >= 0) {
+ /* Save the interrupt flag */
+ devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.b_InterruptMask = devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.b_InterruptMask | ((1 & b_InterruptEnable) << b_TimerNbr);
+
+ /* Enable the timer */
+ outl(1, devpriv->s_BoardInfos.ui_Address + 44 + (b_TimerNbr * 4) + (64 * b_ModulNbr));
+ }
+ } else {
+ /* Interrupt parameter is wrong */
+ DPRINTK("\n");
+ i_ReturnValue = -6;
+ }
+ break;
+ case APCI1710_DISABLE:
+ /* Test the interrupt flag */
+ if (((devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.b_InterruptMask >> b_TimerNbr) & 1) == 1) {
+ /* Disable the interrupt */
+
+ devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr]. dw_ConfigurationWord = devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].dw_ConfigurationWord & 0xF7;
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].dw_ConfigurationWord, devpriv->s_BoardInfos.ui_Address + 32 + (b_TimerNbr * 4) + (64 * b_ModulNbr));
+
+ /* Save the interrupt flag */
+ devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.b_InterruptMask = devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.b_InterruptMask & (0xFF - (1 << b_TimerNbr));
+ }
+
+ /* Disable the timer */
+ outl(0, devpriv->s_BoardInfos.ui_Address + 44 + (b_TimerNbr * 4) + (64 * b_ModulNbr));
+ break;
+ } // Switch end
+ } else {
+ /* Timer not initialised see function */
+ DPRINTK ("Timer not initialised see function\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /* Timer selection wrong */
+ DPRINTK("Timer selection wrong\n");
+ i_ReturnValue = -3;
+ } // if ((b_TimerNbr >= 0) && (b_TimerNbr <= 2))
+ } else {
+ /* The module is not a TIMER module */
+ DPRINTK("The module is not a TIMER module\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /* Module number error */
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_ReadAllTimerValue |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| PULONG_ pul_TimerValueArray)
+INT i_APCI1710_InsnReadAllTimerValue(struct comedi_device *dev,struct comedi_subdevice *s,
+ struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Return the all timer values from selected timer |
+| module (b_ModulNbr). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board |
+| APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : PULONG_ pul_TimerValueArray : Timer value array. |
+| Element 0 contain the timer 0 value. |
+| Element 1 contain the timer 1 value. |
+| Element 2 contain the timer 2 value. |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a TIMER module |
+| -4: Timer 0 not initialised see function |
+| "i_APCI1710_InitTimer" |
+| -5: Timer 1 not initialised see function |
+| "i_APCI1710_InitTimer" |
+| -6: Timer 2 not initialised see function |
+| "i_APCI1710_InitTimer" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnReadAllTimerValue(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ INT i_ReturnValue = 0;
+ BYTE b_ModulNbr, b_ReadType;
+ PULONG pul_TimerValueArray;
+
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ b_ReadType = CR_CHAN(insn->chanspec);
+ pul_TimerValueArray = (PULONG) data;
+ i_ReturnValue = insn->n;
+
+ switch (b_ReadType) {
+ case APCI1710_TIMER_READINTERRUPT:
+
+ data[0] = devpriv->s_InterruptParameters.s_FIFOInterruptParameters[devpriv->s_InterruptParameters.ui_Read].b_OldModuleMask;
+ data[1] = devpriv->s_InterruptParameters.s_FIFOInterruptParameters[devpriv->s_InterruptParameters.ui_Read].ul_OldInterruptMask;
+ data[2] = devpriv->s_InterruptParameters.s_FIFOInterruptParameters[devpriv->s_InterruptParameters.ui_Read].ul_OldCounterLatchValue;
+
+ /* Increment the read FIFO */
+ devpriv->s_InterruptParameters.ui_Read = (devpriv->s_InterruptParameters.ui_Read + 1) % APCI1710_SAVE_INTERRUPT;
+
+ break;
+
+ case APCI1710_TIMER_READALLTIMER:
+ /* Test the module number */
+ if (b_ModulNbr < 4) {
+ /* Test if 82X54 timer */
+ if ((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF0000UL) == APCI1710_82X54_TIMER) {
+ /* Test if timer 0 iniutialised */
+ if (devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[0].b_82X54Init == 1) {
+ /* Test if timer 1 iniutialised */
+ if (devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[1].b_82X54Init == 1) {
+ /* Test if timer 2 iniutialised */
+ if (devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[2].b_82X54Init == 1) {
+ /* Latch all counter */
+ outl(0x17, devpriv->s_BoardInfos.ui_Address + 12 + (64 * b_ModulNbr));
+
+ /* Read the timer 0 value */
+ pul_TimerValueArray[0] = inl(devpriv->s_BoardInfos.ui_Address + 0 + (64 * b_ModulNbr));
+
+ /* Read the timer 1 value */
+ pul_TimerValueArray[1] = inl(devpriv->s_BoardInfos.ui_Address + 4 + (64 * b_ModulNbr));
+
+ /* Read the timer 2 value */
+ pul_TimerValueArray[2] = inl(devpriv->s_BoardInfos.ui_Address + 8 + (64 * b_ModulNbr));
+ } else {
+ /* Timer 2 not initialised see function */
+ DPRINTK("Timer 2 not initialised see function\n");
+ i_ReturnValue = -6;
+ }
+ } else {
+ /* Timer 1 not initialised see function */
+ DPRINTK("Timer 1 not initialised see function\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /* Timer 0 not initialised see function */
+ DPRINTK("Timer 0 not initialised see function\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /* The module is not a TIMER module */
+ DPRINTK("The module is not a TIMER module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /* Module number error */
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ } // End of Switch
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name :INT i_APCI1710_InsnBitsTimer(struct comedi_device *dev,
+struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read write functions for Timer |
++----------------------------------------------------------------------------+
+| Input Parameters :
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value :
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnBitsTimer(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ BYTE b_BitsType;
+ INT i_ReturnValue = 0;
+ b_BitsType = data[0];
+
+ printk("\n82X54");
+
+ switch (b_BitsType) {
+ case APCI1710_TIMER_READVALUE:
+ i_ReturnValue = i_APCI1710_ReadTimerValue(dev,
+ (BYTE)CR_AREF(insn->chanspec),
+ (BYTE)CR_CHAN(insn->chanspec),
+ (PULONG) & data[0]);
+ break;
+
+ case APCI1710_TIMER_GETOUTPUTLEVEL:
+ i_ReturnValue = i_APCI1710_GetTimerOutputLevel(dev,
+ (BYTE)CR_AREF(insn->chanspec),
+ (BYTE)CR_CHAN(insn->chanspec),
+ (PBYTE) &data[0]);
+ break;
+
+ case APCI1710_TIMER_GETPROGRESSSTATUS:
+ i_ReturnValue = i_APCI1710_GetTimerProgressStatus(dev,
+ (BYTE)CR_AREF(insn->chanspec),
+ (BYTE)CR_CHAN(insn->chanspec),
+ (PBYTE)&data[0]);
+ break;
+
+ case APCI1710_TIMER_WRITEVALUE:
+ i_ReturnValue = i_APCI1710_WriteTimerValue(dev,
+ (BYTE)CR_AREF(insn->chanspec),
+ (BYTE)CR_CHAN(insn->chanspec),
+ (ULONG)data[1]);
+
+ break;
+
+ default:
+ printk("Bits Config Parameter Wrong\n");
+ i_ReturnValue = -1;
+ }
+
+ if (i_ReturnValue >= 0)
+ i_ReturnValue = insn->n;
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_ReadTimerValue |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_TimerNbr, |
+| PULONG_ pul_TimerValue) |
++----------------------------------------------------------------------------+
+| Task : Return the timer value from selected digital timer |
+| (b_TimerNbr) from selected timer module (b_ModulNbr). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board |
+| APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number |
+| (0 to 3) |
+| BYTE_ b_TimerNbr : Timer number to read |
+| (0 to 2) |
++----------------------------------------------------------------------------+
+| Output Parameters : PULONG_ pul_TimerValue : Timer value |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: Timer selection wrong |
+| -4: The module is not a TIMER module |
+| -5: Timer not initialised see function |
+| "i_APCI1710_InitTimer" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_ReadTimerValue(struct comedi_device * dev,
+ BYTE b_ModulNbr, BYTE b_TimerNbr,
+ PULONG pul_TimerValue)
+{
+ INT i_ReturnValue = 0;
+
+ /* Test the module number */
+ if (b_ModulNbr < 4) {
+ /* Test if 82X54 timer */
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_82X54_TIMER) {
+ /* Test the timer number */
+ if (b_TimerNbr <= 2) {
+ /* Test if timer initialised */
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_82X54ModuleInfo.
+ s_82X54TimerInfo[b_TimerNbr].
+ b_82X54Init == 1) {
+ /* Latch the timer value */
+ outl((2 << b_TimerNbr) | 0xD0,
+ devpriv->s_BoardInfos.
+ ui_Address + 12 +
+ (64 * b_ModulNbr));
+
+ /* Read the counter value */
+ *pul_TimerValue =
+ inl(devpriv->s_BoardInfos.
+ ui_Address + (b_TimerNbr * 4) +
+ (64 * b_ModulNbr));
+ } else {
+ /* Timer not initialised see function */
+ DPRINTK("Timer not initialised see function\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /* Timer selection wrong */
+ DPRINTK("Timer selection wrong\n");
+ i_ReturnValue = -3;
+ } // if ((b_TimerNbr >= 0) && (b_TimerNbr <= 2))
+ } else {
+ /* The module is not a TIMER module */
+ DPRINTK("The module is not a TIMER module\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /* Module number error */
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+ /*
+ +----------------------------------------------------------------------------+
+ | Function Name : _INT_ i_APCI1710_GetTimerOutputLevel |
+ | (BYTE_ b_BoardHandle, |
+ | BYTE_ b_ModulNbr, |
+ | BYTE_ b_TimerNbr, |
+ | PBYTE_ pb_OutputLevel) |
+ +----------------------------------------------------------------------------+
+ | Task : Return the output signal level (pb_OutputLevel) from |
+ | selected digital timer (b_TimerNbr) from selected timer|
+ | module (b_ModulNbr). |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : BYTE_ b_BoardHandle : Handle of board |
+ | APCI-1710 |
+ | BYTE_ b_ModulNbr : Selected module number |
+ | (0 to 3) |
+ | BYTE_ b_TimerNbr : Timer number to test |
+ | (0 to 2) |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : PBYTE_ pb_OutputLevel : Output signal level |
+ | 0 : The output is low |
+ | 1 : The output is high |
+ +----------------------------------------------------------------------------+
+ | Return Value : 0: No error |
+ | -1: The handle parameter of the board is wrong |
+ | -2: Module selection wrong |
+ | -3: Timer selection wrong |
+ | -4: The module is not a TIMER module |
+ | -5: Timer not initialised see function |
+ | "i_APCI1710_InitTimer" |
+ +----------------------------------------------------------------------------+
+ */
+
+INT i_APCI1710_GetTimerOutputLevel(struct comedi_device * dev,
+ BYTE b_ModulNbr, BYTE b_TimerNbr,
+ PBYTE pb_OutputLevel)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_TimerStatus;
+
+ /* Test the module number */
+ if (b_ModulNbr < 4) {
+ /* Test if 82X54 timer */
+ if ((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF0000UL) == APCI1710_82X54_TIMER) {
+ /* Test the timer number */
+ if (b_TimerNbr <= 2) {
+ /* Test if timer initialised */
+ if (devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].b_82X54Init == 1) {
+ /* Latch the timer value */
+ outl((2 << b_TimerNbr) | 0xE0, devpriv->s_BoardInfos.ui_Address + 12 + (64 * b_ModulNbr));
+
+ /* Read the timer status */
+ dw_TimerStatus = inl(devpriv->s_BoardInfos.ui_Address + 16 + (b_TimerNbr * 4) + (64 * b_ModulNbr));
+
+ *pb_OutputLevel = (BYTE) (((dw_TimerStatus >> 7) & 1) ^ devpriv-> s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].b_OutputLevel);
+ } else {
+ /* Timer not initialised see function */
+ DPRINTK("Timer not initialised see function\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /* Timer selection wrong */
+ DPRINTK("Timer selection wrong\n");
+ i_ReturnValue = -3;
+ } // if ((b_TimerNbr >= 0) && (b_TimerNbr <= 2))
+ } else {
+ /* The module is not a TIMER module */
+ DPRINTK("The module is not a TIMER module\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /* Module number error */
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_GetTimerProgressStatus |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_TimerNbr, |
+| PBYTE_ pb_TimerStatus) |
++----------------------------------------------------------------------------+
+| Task : Return the progress status (pb_TimerStatus) from |
+| selected digital timer (b_TimerNbr) from selected timer|
+| module (b_ModulNbr). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board |
+| APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number |
+| (0 to 3) |
+| BYTE_ b_TimerNbr : Timer number to test |
+| (0 to 2) |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_TimerStatus : Output signal level |
+| 0 : Timer not in progress |
+| 1 : Timer in progress |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: Timer selection wrong |
+| -4: The module is not a TIMER module |
+| -5: Timer not initialised see function |
+| "i_APCI1710_InitTimer" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_GetTimerProgressStatus(struct comedi_device *dev,
+ BYTE b_ModulNbr, BYTE b_TimerNbr,
+ PBYTE pb_TimerStatus)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_TimerStatus;
+
+ /* Test the module number */
+ if (b_ModulNbr < 4) {
+ /* Test if 82X54 timer */
+
+ if ((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF0000UL) == APCI1710_82X54_TIMER) {
+ /* Test the timer number */
+ if (b_TimerNbr <= 2) {
+ /* Test if timer initialised */
+ if (devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].b_82X54Init == 1) {
+ /* Latch the timer value */
+ outl((2 << b_TimerNbr) | 0xE0, devpriv->s_BoardInfos.ui_Address + 12 + (64 * b_ModulNbr));
+
+ /* Read the timer status */
+ dw_TimerStatus = inl(devpriv->s_BoardInfos.ui_Address + 16 + (b_TimerNbr * 4) + (64 * b_ModulNbr));
+
+ *pb_TimerStatus = (BYTE) ((dw_TimerStatus) >> 8) & 1;
+ printk("ProgressStatus : %d", *pb_TimerStatus);
+ } else {
+ /* Timer not initialised see function */
+ i_ReturnValue = -5;
+ }
+ } else {
+ /* Timer selection wrong */
+ i_ReturnValue = -3;
+ } // if ((b_TimerNbr >= 0) && (b_TimerNbr <= 2))
+ } else {
+ /* The module is not a TIMER module */
+
+ i_ReturnValue = -4;
+ }
+ } else {
+ /* Module number error */
+
+ i_ReturnValue = -2;
+ }
+
+ return i_ReturnValue;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_WriteTimerValue |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_TimerNbr, |
+| ULONG_ ul_WriteValue) |
++----------------------------------------------------------------------------+
+| Task : Write the value (ul_WriteValue) into the selected timer|
+| (b_TimerNbr) from selected timer module (b_ModulNbr). |
+| The action in depend of the time mode selection. |
+| See timer mode description table. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board |
+| APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number |
+| (0 to 3) |
+| BYTE_ b_TimerNbr : Timer number to write |
+| (0 to 2) |
+| ULONG_ ul_WriteValue : Value to write |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: Timer selection wrong |
+| -4: The module is not a TIMER module |
+| -5: Timer not initialised see function |
+| "i_APCI1710_InitTimer" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_WriteTimerValue(struct comedi_device * dev,
+ BYTE b_ModulNbr, BYTE b_TimerNbr,
+ ULONG ul_WriteValue)
+{
+ INT i_ReturnValue = 0;
+
+ /* Test the module number */
+ if (b_ModulNbr < 4) {
+ /* Test if 82X54 timer */
+ if ((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF0000UL) == APCI1710_82X54_TIMER) {
+ /* Test the timer number */
+ if (b_TimerNbr <= 2) {
+ /* Test if timer initialised */
+ if (devpriv->s_ModuleInfo[b_ModulNbr].s_82X54ModuleInfo.s_82X54TimerInfo[b_TimerNbr].b_82X54Init == 1) {
+ /* Write the value */
+ outl(ul_WriteValue, devpriv->s_BoardInfos.ui_Address + (b_TimerNbr * 4) + (64 * b_ModulNbr));
+ } else {
+ /* Timer not initialised see function */
+ DPRINTK("Timer not initialised see function\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /* Timer selection wrong */
+ DPRINTK("Timer selection wrong\n");
+ i_ReturnValue = -3;
+ } // if ((b_TimerNbr >= 0) && (b_TimerNbr <= 2))
+ } else {
+ /* The module is not a TIMER module */
+ DPRINTK("The module is not a TIMER module\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /* Module number error */
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return i_ReturnValue;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_82x54.h b/drivers/staging/comedi/drivers/addi-data/APCI1710_82x54.h
new file mode 100644
index 000000000000..4797c0b77a43
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_82x54.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+#define APCI1710_PCI_BUS_CLOCK 0
+#define APCI1710_FRONT_CONNECTOR_INPUT 1
+#define APCI1710_TIMER_READVALUE 0
+#define APCI1710_TIMER_GETOUTPUTLEVEL 1
+#define APCI1710_TIMER_GETPROGRESSSTATUS 2
+#define APCI1710_TIMER_WRITEVALUE 3
+
+#define APCI1710_TIMER_READINTERRUPT 1
+#define APCI1710_TIMER_READALLTIMER 2
+
+/* BEGIN JK 27.10.03 : Add the possibility to use a 40 Mhz quartz */
+#ifndef APCI1710_10MHZ
+#define APCI1710_10MHZ 10
+#endif
+/* END JK 27.10.03 : Add the possibility to use a 40 Mhz quartz */
+
+/*
+ * 82X54 TIMER INISIALISATION FUNCTION
+ */
+INT i_APCI1710_InsnConfigInitTimer(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+INT i_APCI1710_InsnWriteEnableDisableTimer(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+/*
+ * 82X54 READ FUNCTION
+ */
+INT i_APCI1710_InsnReadAllTimerValue(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+INT i_APCI1710_InsnBitsTimer(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+/*
+ * 82X54 READ & WRITE FUNCTION
+ */
+INT i_APCI1710_ReadTimerValue(struct comedi_device *dev,
+ BYTE b_ModulNbr, BYTE b_TimerNbr,
+ PULONG pul_TimerValue);
+
+INT i_APCI1710_GetTimerOutputLevel(struct comedi_device *dev,
+ BYTE b_ModulNbr, BYTE b_TimerNbr,
+ PBYTE pb_OutputLevel);
+
+INT i_APCI1710_GetTimerProgressStatus(struct comedi_device *dev,
+ BYTE b_ModulNbr, BYTE b_TimerNbr,
+ PBYTE pb_TimerStatus);
+
+/*
+ * 82X54 WRITE FUNCTION
+ */
+INT i_APCI1710_WriteTimerValue(struct comedi_device *dev,
+ BYTE b_ModulNbr, BYTE b_TimerNbr,
+ ULONG ul_WriteValue);
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_Chrono.c b/drivers/staging/comedi/drivers/addi-data/APCI1710_Chrono.c
new file mode 100644
index 000000000000..1a54d3b2c694
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_Chrono.c
@@ -0,0 +1,2032 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-----------------------------------------------------------------------+
+ | Project : API APCI1710 | Compiler : gcc |
+ | Module name : CHRONO.C | Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-----------------------------------------------------------------------+
+ | Description : APCI-1710 chronometer module |
+ | |
+ | |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +-----------------------------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | 29/06/98 | S. Weber | Digital input / output implementation |
+ |----------|-----------|------------------------------------------------|
+ | 08/05/00 | Guinot C | - 0400/0228 All Function in RING 0 |
+ | | | available |
+ +-----------------------------------------------------------------------+
+ | | | |
+ | | | |
+ +-----------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+#include "APCI1710_Chrono.h"
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_InitChrono |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_ChronoMode, |
+| BYTE_ b_PCIInputClock, |
+| BYTE_ b_TimingUnit, |
+| ULONG_ ul_TimingInterval, |
+| PULONG_ pul_RealTimingInterval)
+
++----------------------------------------------------------------------------+
+| Task : Configure the chronometer operating mode (b_ChronoMode)|
+| from selected module (b_ModulNbr). |
+| The ul_TimingInterval and ul_TimingUnit determine the |
+| timing base for the measurement. |
+| The pul_RealTimingInterval return the real timing |
+| value. You must calling this function be for you call |
+| any other function witch access of the chronometer. |
+| |
+| Witch this functionality from the APCI-1710 you have |
+| the possibility to measure the timing witch two event. |
+| |
+| The mode 0 and 1 is appropriate for period measurement.|
+| The mode 2 and 3 is appropriate for frequent |
+| measurement. |
+| The mode 4 to 7 is appropriate for measuring the timing|
+| between two event. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr CR_AREF(insn->chanspec) : Module number to configure |
+| (0 to 3) |
+| BYTE_ b_ChronoMode data[0] : Chronometer action mode |
+| (0 to 7). |
+| BYTE_ b_PCIInputClock data[1] : Selection from PCI bus clock|
+| - APCI1710_30MHZ : |
+| The PC have a PCI bus |
+| clock from 30 MHz |
+| - APCI1710_33MHZ : |
+| The PC have a PCI bus |
+| clock from 33 MHz |
+| - APCI1710_40MHZ |
+| The APCI-1710 have a |
+| integrated 40Mhz |
+| quartz. |
+| BYTE_ b_TimingUnit data[2] : Base timing unity (0 to 4) |
+| 0 : ns |
+| 1 : µs |
+| 2 : ms |
+| 3 : s |
+| 4 : mn |
+| ULONG_ ul_TimingInterval : data[3] Base timing value. |
++----------------------------------------------------------------------------+
+| Output Parameters : PULONG_ pul_RealTimingInterval : Real base timing |
+| value.
+| data[0]
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a Chronometer module |
+| -4: Chronometer mode selection is wrong |
+| -5: The selected PCI input clock is wrong |
+| -6: Timing unity selection is wrong |
+| -7: Base timing selection is wrong |
+| -8: You can not used the 40MHz clock selection wich |
+| this board |
+| -9: You can not used the 40MHz clock selection wich |
+| this CHRONOS version |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnConfigInitChrono(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ ULONG ul_TimerValue = 0;
+ ULONG ul_TimingInterval = 0;
+ ULONG ul_RealTimingInterval = 0;
+ double d_RealTimingInterval = 0;
+ DWORD dw_ModeArray[8] =
+ { 0x01, 0x05, 0x00, 0x04, 0x02, 0x0E, 0x0A, 0x06 };
+ BYTE b_ModulNbr, b_ChronoMode, b_PCIInputClock, b_TimingUnit;
+
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ b_ChronoMode = (BYTE) data[0];
+ b_PCIInputClock = (BYTE) data[1];
+ b_TimingUnit = (BYTE) data[2];
+ ul_TimingInterval = (ULONG) data[3];
+ i_ReturnValue = insn->n;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***********************/
+ /* Test if chronometer */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_CHRONOMETER) {
+ /*****************************/
+ /* Test the chronometer mode */
+ /*****************************/
+
+ if (b_ChronoMode <= 7) {
+ /**************************/
+ /* Test the PCI bus clock */
+ /**************************/
+
+ if ((b_PCIInputClock == APCI1710_30MHZ) ||
+ (b_PCIInputClock == APCI1710_33MHZ) ||
+ (b_PCIInputClock == APCI1710_40MHZ)) {
+ /*************************/
+ /* Test the timing unity */
+ /*************************/
+
+ if (b_TimingUnit <= 4) {
+ /**********************************/
+ /* Test the base timing selection */
+ /**********************************/
+
+ if (((b_PCIInputClock == APCI1710_30MHZ) && (b_TimingUnit == 0) && (ul_TimingInterval >= 66) && (ul_TimingInterval <= 0xFFFFFFFFUL)) || ((b_PCIInputClock == APCI1710_30MHZ) && (b_TimingUnit == 1) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 143165576UL)) || ((b_PCIInputClock == APCI1710_30MHZ) && (b_TimingUnit == 2) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 143165UL)) || ((b_PCIInputClock == APCI1710_30MHZ) && (b_TimingUnit == 3) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 143UL)) || ((b_PCIInputClock == APCI1710_30MHZ) && (b_TimingUnit == 4) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 2UL)) || ((b_PCIInputClock == APCI1710_33MHZ) && (b_TimingUnit == 0) && (ul_TimingInterval >= 60) && (ul_TimingInterval <= 0xFFFFFFFFUL)) || ((b_PCIInputClock == APCI1710_33MHZ) && (b_TimingUnit == 1) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 130150240UL)) || ((b_PCIInputClock == APCI1710_33MHZ) && (b_TimingUnit == 2) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 130150UL)) || ((b_PCIInputClock == APCI1710_33MHZ) && (b_TimingUnit == 3) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 130UL)) || ((b_PCIInputClock == APCI1710_33MHZ) && (b_TimingUnit == 4) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 2UL)) || ((b_PCIInputClock == APCI1710_40MHZ) && (b_TimingUnit == 0) && (ul_TimingInterval >= 50) && (ul_TimingInterval <= 0xFFFFFFFFUL)) || ((b_PCIInputClock == APCI1710_40MHZ) && (b_TimingUnit == 1) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 107374182UL)) || ((b_PCIInputClock == APCI1710_40MHZ) && (b_TimingUnit == 2) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 107374UL)) || ((b_PCIInputClock == APCI1710_40MHZ) && (b_TimingUnit == 3) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 107UL)) || ((b_PCIInputClock == APCI1710_40MHZ) && (b_TimingUnit == 4) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 1UL))) {
+ /**************************/
+ /* Test the board version */
+ /**************************/
+
+ if (((b_PCIInputClock == APCI1710_40MHZ) && (devpriv->s_BoardInfos.b_BoardVersion > 0)) || (b_PCIInputClock != APCI1710_40MHZ)) {
+ /************************/
+ /* Test the TOR version */
+ /************************/
+
+ if (((b_PCIInputClock == APCI1710_40MHZ) && ((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF) >= 0x3131)) || (b_PCIInputClock != APCI1710_40MHZ)) {
+ fpu_begin
+ ();
+
+ /****************************************/
+ /* Calculate the timer 0 division fator */
+ /****************************************/
+
+ switch (b_TimingUnit) {
+ /******/
+ /* ns */
+ /******/
+
+ case 0:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_TimerValue
+ =
+ (ULONG)
+ (ul_TimingInterval
+ *
+ (0.001 * b_PCIInputClock));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_TimingInterval * (0.001 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
+ ul_TimerValue
+ =
+ ul_TimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealTimingInterval
+ =
+ (ULONG)
+ (ul_TimerValue
+ /
+ (0.001 * (double)b_PCIInputClock));
+ d_RealTimingInterval
+ =
+ (double)
+ ul_TimerValue
+ /
+ (0.001
+ *
+ (double)
+ b_PCIInputClock);
+
+ if ((double)((double)ul_TimerValue / (0.001 * (double)b_PCIInputClock)) >= (double)((double)ul_RealTimingInterval + 0.5)) {
+ ul_RealTimingInterval
+ =
+ ul_RealTimingInterval
+ +
+ 1;
+ }
+
+ ul_TimingInterval
+ =
+ ul_TimingInterval
+ -
+ 1;
+ ul_TimerValue
+ =
+ ul_TimerValue
+ -
+ 2;
+ if (b_PCIInputClock != APCI1710_40MHZ) {
+ ul_TimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_TimerValue)
+ *
+ 0.99392);
+ }
+
+ break;
+
+ /******/
+ /* æs */
+ /******/
+
+ case 1:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_TimerValue
+ =
+ (ULONG)
+ (ul_TimingInterval
+ *
+ (1.0 * b_PCIInputClock));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_TimingInterval * (1.0 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
+ ul_TimerValue
+ =
+ ul_TimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealTimingInterval
+ =
+ (ULONG)
+ (ul_TimerValue
+ /
+ (1.0 * (double)b_PCIInputClock));
+ d_RealTimingInterval
+ =
+ (double)
+ ul_TimerValue
+ /
+ (
+ (double)
+ 1.0
+ *
+ (double)
+ b_PCIInputClock);
+
+ if ((double)((double)ul_TimerValue / (1.0 * (double)b_PCIInputClock)) >= (double)((double)ul_RealTimingInterval + 0.5)) {
+ ul_RealTimingInterval
+ =
+ ul_RealTimingInterval
+ +
+ 1;
+ }
+
+ ul_TimingInterval
+ =
+ ul_TimingInterval
+ -
+ 1;
+ ul_TimerValue
+ =
+ ul_TimerValue
+ -
+ 2;
+ if (b_PCIInputClock != APCI1710_40MHZ) {
+ ul_TimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_TimerValue)
+ *
+ 0.99392);
+ }
+
+ break;
+
+ /******/
+ /* ms */
+ /******/
+
+ case 2:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_TimerValue
+ =
+ ul_TimingInterval
+ *
+ (1000
+ *
+ b_PCIInputClock);
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_TimingInterval * (1000.0 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
+ ul_TimerValue
+ =
+ ul_TimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealTimingInterval
+ =
+ (ULONG)
+ (ul_TimerValue
+ /
+ (1000.0 * (double)b_PCIInputClock));
+ d_RealTimingInterval
+ =
+ (double)
+ ul_TimerValue
+ /
+ (1000.0
+ *
+ (double)
+ b_PCIInputClock);
+
+ if ((double)((double)ul_TimerValue / (1000.0 * (double)b_PCIInputClock)) >= (double)((double)ul_RealTimingInterval + 0.5)) {
+ ul_RealTimingInterval
+ =
+ ul_RealTimingInterval
+ +
+ 1;
+ }
+
+ ul_TimingInterval
+ =
+ ul_TimingInterval
+ -
+ 1;
+ ul_TimerValue
+ =
+ ul_TimerValue
+ -
+ 2;
+ if (b_PCIInputClock != APCI1710_40MHZ) {
+ ul_TimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_TimerValue)
+ *
+ 0.99392);
+ }
+
+ break;
+
+ /*****/
+ /* s */
+ /*****/
+
+ case 3:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_TimerValue
+ =
+ (ULONG)
+ (ul_TimingInterval
+ *
+ (1000000.0
+ *
+ b_PCIInputClock));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_TimingInterval * (1000000.0 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
+ ul_TimerValue
+ =
+ ul_TimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealTimingInterval
+ =
+ (ULONG)
+ (ul_TimerValue
+ /
+ (1000000.0
+ *
+ (double)
+ b_PCIInputClock));
+ d_RealTimingInterval
+ =
+ (double)
+ ul_TimerValue
+ /
+ (1000000.0
+ *
+ (double)
+ b_PCIInputClock);
+
+ if ((double)((double)ul_TimerValue / (1000000.0 * (double)b_PCIInputClock)) >= (double)((double)ul_RealTimingInterval + 0.5)) {
+ ul_RealTimingInterval
+ =
+ ul_RealTimingInterval
+ +
+ 1;
+ }
+
+ ul_TimingInterval
+ =
+ ul_TimingInterval
+ -
+ 1;
+ ul_TimerValue
+ =
+ ul_TimerValue
+ -
+ 2;
+ if (b_PCIInputClock != APCI1710_40MHZ) {
+ ul_TimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_TimerValue)
+ *
+ 0.99392);
+ }
+
+ break;
+
+ /******/
+ /* mn */
+ /******/
+
+ case 4:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_TimerValue
+ =
+ (ULONG)
+ (
+ (ul_TimingInterval
+ *
+ 60)
+ *
+ (1000000.0
+ *
+ b_PCIInputClock));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)(ul_TimingInterval * 60.0) * (1000000.0 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
+ ul_TimerValue
+ =
+ ul_TimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealTimingInterval
+ =
+ (ULONG)
+ (ul_TimerValue
+ /
+ (1000000.0
+ *
+ (double)
+ b_PCIInputClock))
+ /
+ 60;
+ d_RealTimingInterval
+ =
+ (
+ (double)
+ ul_TimerValue
+ /
+ (0.001 * (double)b_PCIInputClock)) / 60.0;
+
+ if ((double)(((double)ul_TimerValue / (1000000.0 * (double)b_PCIInputClock)) / 60.0) >= (double)((double)ul_RealTimingInterval + 0.5)) {
+ ul_RealTimingInterval
+ =
+ ul_RealTimingInterval
+ +
+ 1;
+ }
+
+ ul_TimingInterval
+ =
+ ul_TimingInterval
+ -
+ 1;
+ ul_TimerValue
+ =
+ ul_TimerValue
+ -
+ 2;
+ if (b_PCIInputClock != APCI1710_40MHZ) {
+ ul_TimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_TimerValue)
+ *
+ 0.99392);
+ }
+
+ break;
+ }
+
+ fpu_end();
+
+ /****************************/
+ /* Save the PCI input clock */
+ /****************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_ChronoModuleInfo.
+ b_PCIInputClock
+ =
+ b_PCIInputClock;
+
+ /*************************/
+ /* Save the timing unity */
+ /*************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_ChronoModuleInfo.
+ b_TimingUnit
+ =
+ b_TimingUnit;
+
+ /************************/
+ /* Save the base timing */
+ /************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_ChronoModuleInfo.
+ d_TimingInterval
+ =
+ d_RealTimingInterval;
+
+ /****************************/
+ /* Set the chronometer mode */
+ /****************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_ChronoModuleInfo.
+ dw_ConfigReg
+ =
+ dw_ModeArray
+ [b_ChronoMode];
+
+ /***********************/
+ /* Test if 40 MHz used */
+ /***********************/
+
+ if (b_PCIInputClock == APCI1710_40MHZ) {
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_ChronoModuleInfo.
+ dw_ConfigReg
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_ChronoModuleInfo.
+ dw_ConfigReg
+ |
+ 0x80;
+ }
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].s_ChronoModuleInfo.dw_ConfigReg, devpriv->s_BoardInfos.ui_Address + 16 + (64 * b_ModulNbr));
+
+ /***********************/
+ /* Write timer 0 value */
+ /***********************/
+
+ outl(ul_TimerValue, devpriv->s_BoardInfos.ui_Address + (64 * b_ModulNbr));
+
+ /*********************/
+ /* Chronometer init. */
+ /*********************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_ChronoModuleInfo.
+ b_ChronoInit
+ =
+ 1;
+ } else {
+ /***********************************************/
+ /* TOR version error for 40MHz clock selection */
+ /***********************************************/
+
+ DPRINTK("TOR version error for 40MHz clock selection\n");
+ i_ReturnValue
+ =
+ -9;
+ }
+ } else {
+ /**************************************************************/
+ /* You can not used the 40MHz clock selection wich this board */
+ /**************************************************************/
+
+ DPRINTK("You can not used the 40MHz clock selection wich this board\n");
+ i_ReturnValue =
+ -8;
+ }
+ } else {
+ /**********************************/
+ /* Base timing selection is wrong */
+ /**********************************/
+
+ DPRINTK("Base timing selection is wrong\n");
+ i_ReturnValue = -7;
+ }
+ } // if ((b_TimingUnit >= 0) && (b_TimingUnit <= 4))
+ else {
+ /***********************************/
+ /* Timing unity selection is wrong */
+ /***********************************/
+
+ DPRINTK("Timing unity selection is wrong\n");
+ i_ReturnValue = -6;
+ } // if ((b_TimingUnit >= 0) && (b_TimingUnit <= 4))
+ } // if ((b_PCIInputClock == APCI1710_30MHZ) || (b_PCIInputClock == APCI1710_33MHZ))
+ else {
+ /*****************************************/
+ /* The selected PCI input clock is wrong */
+ /*****************************************/
+
+ DPRINTK("The selected PCI input clock is wrong\n");
+ i_ReturnValue = -5;
+ } // if ((b_PCIInputClock == APCI1710_30MHZ) || (b_PCIInputClock == APCI1710_33MHZ))
+ } // if (b_ChronoMode >= 0 && b_ChronoMode <= 7)
+ else {
+ /***************************************/
+ /* Chronometer mode selection is wrong */
+ /***************************************/
+
+ DPRINTK("Chronometer mode selection is wrong\n");
+ i_ReturnValue = -4;
+ } // if (b_ChronoMode >= 0 && b_ChronoMode <= 7)
+ } else {
+ /******************************************/
+ /* The module is not a Chronometer module */
+ /******************************************/
+
+ DPRINTK("The module is not a Chronometer module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+ data[0] = ul_RealTimingInterval;
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_EnableChrono |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_CycleMode, |
+| BYTE_ b_InterruptEnable)
+INT i_APCI1710_InsnWriteEnableDisableChrono(struct comedi_device *dev,
+struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Enable the chronometer from selected module |
+| (b_ModulNbr). You must calling the |
+| "i_APCI1710_InitChrono" function be for you call this |
+| function. |
+| If you enable the chronometer interrupt, the |
+| chronometer generate a interrupt after the stop signal.|
+| See function "i_APCI1710_SetBoardIntRoutineX" and the |
+| Interrupt mask description chapter from this manual. |
+| The b_CycleMode parameter determine if you will |
+| measured a single or more cycle.
+
+| Disable the chronometer from selected module |
+| (b_ModulNbr). If you disable the chronometer after a |
+| start signal occur and you restart the chronometer |
+| witch the " i_APCI1710_EnableChrono" function, if no |
+| stop signal occur this start signal is ignored.
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr CR_AREF(chanspec) : Selected module number (0 to 3) |
+ data[0] ENABle/Disable chrono
+| BYTE_ b_CycleMode : Selected the chronometer |
+| data[1] acquisition mode |
+| BYTE_ b_InterruptEnable : Enable or disable the |
+| data[2] chronometer interrupt. |
+| APCI1710_ENABLE: |
+| Enable the chronometer |
+| interrupt |
+| APCI1710_DISABLE: |
+| Disable the chronometer |
+| interrupt |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a Chronometer module |
+| -4: Chronometer not initialised see function |
+| "i_APCI1710_InitChrono" |
+| -5: Chronometer acquisition mode cycle is wrong |
+| -6: Interrupt parameter is wrong |
+| -7: Interrupt function not initialised. |
+| See function "i_APCI1710_SetBoardIntRoutineX"
+ -8: data[0] wrong input |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnWriteEnableDisableChrono(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ BYTE b_ModulNbr, b_CycleMode, b_InterruptEnable, b_Action;
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ b_Action = (BYTE) data[0];
+ b_CycleMode = (BYTE) data[1];
+ b_InterruptEnable = (BYTE) data[2];
+ i_ReturnValue = insn->n;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***********************/
+ /* Test if chronometer */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_CHRONOMETER) {
+ /***********************************/
+ /* Test if chronometer initialised */
+ /***********************************/
+
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_ChronoModuleInfo.b_ChronoInit == 1) {
+
+ switch (b_Action) {
+
+ case APCI1710_ENABLE:
+
+ /*********************************/
+ /* Test the cycle mode parameter */
+ /*********************************/
+
+ if ((b_CycleMode == APCI1710_SINGLE)
+ || (b_CycleMode ==
+ APCI1710_CONTINUOUS)) {
+ /***************************/
+ /* Test the interrupt flag */
+ /***************************/
+
+ if ((b_InterruptEnable ==
+ APCI1710_ENABLE)
+ || (b_InterruptEnable ==
+ APCI1710_DISABLE))
+ {
+
+ /***************************/
+ /* Save the interrupt flag */
+ /***************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_ChronoModuleInfo.
+ b_InterruptMask
+ =
+ b_InterruptEnable;
+
+ /***********************/
+ /* Save the cycle mode */
+ /***********************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_ChronoModuleInfo.
+ b_CycleMode =
+ b_CycleMode;
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_ChronoModuleInfo.
+ dw_ConfigReg =
+ (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_ChronoModuleInfo.
+ dw_ConfigReg &
+ 0x8F) | ((1 &
+ b_InterruptEnable)
+ << 5) | ((1 &
+ b_CycleMode)
+ << 6) | 0x10;
+
+ /*****************************/
+ /* Test if interrupt enabled */
+ /*****************************/
+
+ if (b_InterruptEnable ==
+ APCI1710_ENABLE)
+ {
+ /****************************/
+ /* Clear the interrupt flag */
+ /****************************/
+
+ outl(devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_ChronoModuleInfo.
+ dw_ConfigReg,
+ devpriv->
+ s_BoardInfos.
+ ui_Address
+ + 32 +
+ (64 * b_ModulNbr));
+ devpriv->tsk_Current = current; // Save the current process task structure
+ }
+
+ /***********************************/
+ /* Enable or disable the interrupt */
+ /* Enable the chronometer */
+ /***********************************/
+
+ outl(devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_ChronoModuleInfo.
+ dw_ConfigReg,
+ devpriv->
+ s_BoardInfos.
+ ui_Address +
+ 16 +
+ (64 * b_ModulNbr));
+
+ /*************************/
+ /* Clear status register */
+ /*************************/
+
+ outl(0, devpriv->
+ s_BoardInfos.
+ ui_Address +
+ 36 +
+ (64 * b_ModulNbr));
+
+ } // if ((b_InterruptEnable == APCI1710_ENABLE) || (b_InterruptEnable == APCI1710_DISABLE))
+ else {
+ /********************************/
+ /* Interrupt parameter is wrong */
+ /********************************/
+
+ DPRINTK("Interrupt parameter is wrong\n");
+ i_ReturnValue = -6;
+ } // if ((b_InterruptEnable == APCI1710_ENABLE) || (b_InterruptEnable == APCI1710_DISABLE))
+ } // if ((b_CycleMode == APCI1710_SINGLE) || (b_CycleMode == APCI1710_CONTINUOUS))
+ else {
+ /***********************************************/
+ /* Chronometer acquisition mode cycle is wrong */
+ /***********************************************/
+
+ DPRINTK("Chronometer acquisition mode cycle is wrong\n");
+ i_ReturnValue = -5;
+ } // if ((b_CycleMode == APCI1710_SINGLE) || (b_CycleMode == APCI1710_CONTINUOUS))
+ break;
+
+ case APCI1710_DISABLE:
+
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_ChronoModuleInfo.
+ b_InterruptMask = 0;
+
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_ChronoModuleInfo.
+ dw_ConfigReg =
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_ChronoModuleInfo.
+ dw_ConfigReg & 0x2F;
+
+ /***************************/
+ /* Disable the interrupt */
+ /* Disable the chronometer */
+ /***************************/
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].
+ s_ChronoModuleInfo.dw_ConfigReg,
+ devpriv->s_BoardInfos.
+ ui_Address + 16 +
+ (64 * b_ModulNbr));
+
+ /***************************/
+ /* Test if continuous mode */
+ /***************************/
+
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_ChronoModuleInfo.
+ b_CycleMode ==
+ APCI1710_CONTINUOUS) {
+ /*************************/
+ /* Clear status register */
+ /*************************/
+
+ outl(0, devpriv->s_BoardInfos.
+ ui_Address + 36 +
+ (64 * b_ModulNbr));
+ }
+ break;
+
+ default:
+ DPRINTK("Inputs wrong! Enable or Disable chrono\n");
+ i_ReturnValue = -8;
+ } // switch ENABLE/DISABLE
+ } else {
+ /*******************************/
+ /* Chronometer not initialised */
+ /*******************************/
+
+ DPRINTK("Chronometer not initialised\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /******************************************/
+ /* The module is not a Chronometer module */
+ /******************************************/
+
+ DPRINTK("The module is not a Chronometer module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name :INT i_APCI1710_InsnReadChrono(struct comedi_device *dev,struct comedi_subdevice *s,
+struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read functions for Timer |
++----------------------------------------------------------------------------+
+| Input Parameters :
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value :
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnReadChrono(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ BYTE b_ReadType;
+ INT i_ReturnValue = insn->n;
+
+ b_ReadType = CR_CHAN(insn->chanspec);
+
+ switch (b_ReadType) {
+ case APCI1710_CHRONO_PROGRESS_STATUS:
+ i_ReturnValue = i_APCI1710_GetChronoProgressStatus(dev,
+ (BYTE) CR_AREF(insn->chanspec), (PBYTE) & data[0]);
+ break;
+
+ case APCI1710_CHRONO_READVALUE:
+ i_ReturnValue = i_APCI1710_ReadChronoValue(dev,
+ (BYTE) CR_AREF(insn->chanspec),
+ (UINT) insn->unused[0],
+ (PBYTE) & data[0], (PULONG) & data[1]);
+ break;
+
+ case APCI1710_CHRONO_CONVERTVALUE:
+ i_ReturnValue = i_APCI1710_ConvertChronoValue(dev,
+ (BYTE) CR_AREF(insn->chanspec),
+ (ULONG) insn->unused[0],
+ (PULONG) & data[0],
+ (PBYTE) & data[1],
+ (PBYTE) & data[2],
+ (PUINT) & data[3],
+ (PUINT) & data[4], (PUINT) & data[5]);
+ break;
+
+ case APCI1710_CHRONO_READINTERRUPT:
+ printk("In Chrono Read Interrupt\n");
+
+ data[0] = devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.ui_Read].b_OldModuleMask;
+ data[1] = devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.ui_Read].ul_OldInterruptMask;
+ data[2] = devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.ui_Read].ul_OldCounterLatchValue;
+
+ /**************************/
+ /* Increment the read FIFO */
+ /***************************/
+
+ devpriv->
+ s_InterruptParameters.
+ ui_Read = (devpriv->
+ s_InterruptParameters.
+ ui_Read + 1) % APCI1710_SAVE_INTERRUPT;
+ break;
+
+ default:
+ printk("ReadType Parameter wrong\n");
+ }
+
+ if (i_ReturnValue >= 0)
+ i_ReturnValue = insn->n;
+ return (i_ReturnValue);
+
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_GetChronoProgressStatus |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| PBYTE_ pb_ChronoStatus) |
++----------------------------------------------------------------------------+
+| Task : Return the chronometer status (pb_ChronoStatus) from |
+| selected chronometer module (b_ModulNbr). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : PULONG_ pb_ChronoStatus : Return the chronometer |
+| status. |
+| 0 : Measurement not started.|
+| No start signal occur. |
+| 1 : Measurement started. |
+| A start signal occur. |
+| 2 : Measurement stopped. |
+| A stop signal occur. |
+| The measurement is |
+| terminate. |
+| 3: A overflow occur. You |
+| must change the base |
+| timing witch the |
+| function |
+| "i_APCI1710_InitChrono" |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a Chronometer module |
+| -4: Chronometer not initialised see function |
+| "i_APCI1710_InitChrono" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_GetChronoProgressStatus(struct comedi_device * dev,
+ BYTE b_ModulNbr, PBYTE pb_ChronoStatus)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_Status;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***********************/
+ /* Test if chronometer */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_CHRONOMETER) {
+ /***********************************/
+ /* Test if chronometer initialised */
+ /***********************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_ChronoModuleInfo.b_ChronoInit == 1) {
+
+ dw_Status = inl(devpriv->s_BoardInfos.
+ ui_Address + 8 + (64 * b_ModulNbr));
+
+ /********************/
+ /* Test if overflow */
+ /********************/
+
+ if ((dw_Status & 8) == 8) {
+ /******************/
+ /* Overflow occur */
+ /******************/
+
+ *pb_ChronoStatus = 3;
+ } // if ((dw_Status & 8) == 8)
+ else {
+ /*******************************/
+ /* Test if measurement stopped */
+ /*******************************/
+
+ if ((dw_Status & 2) == 2) {
+ /***********************/
+ /* A stop signal occur */
+ /***********************/
+
+ *pb_ChronoStatus = 2;
+ } // if ((dw_Status & 2) == 2)
+ else {
+ /*******************************/
+ /* Test if measurement started */
+ /*******************************/
+
+ if ((dw_Status & 1) == 1) {
+ /************************/
+ /* A start signal occur */
+ /************************/
+
+ *pb_ChronoStatus = 1;
+ } // if ((dw_Status & 1) == 1)
+ else {
+ /***************************/
+ /* Measurement not started */
+ /***************************/
+
+ *pb_ChronoStatus = 0;
+ } // if ((dw_Status & 1) == 1)
+ } // if ((dw_Status & 2) == 2)
+ } // if ((dw_Status & 8) == 8)
+ } else {
+ /*******************************/
+ /* Chronometer not initialised */
+ /*******************************/
+ DPRINTK("Chronometer not initialised\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /******************************************/
+ /* The module is not a Chronometer module */
+ /******************************************/
+ DPRINTK("The module is not a Chronometer module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_ReadChronoValue |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| UINT_ ui_TimeOut, |
+| PBYTE_ pb_ChronoStatus, |
+| PULONG_ pul_ChronoValue) |
++----------------------------------------------------------------------------+
+| Task : Return the chronometer status (pb_ChronoStatus) and the|
+| timing value (pul_ChronoValue) after a stop signal |
+| occur from selected chronometer module (b_ModulNbr). |
+| This function are only avaible if you have disabled |
+| the interrupt functionality. See function |
+| "i_APCI1710_EnableChrono" and the Interrupt mask |
+| description chapter. |
+| You can test the chronometer status witch the |
+| "i_APCI1710_GetChronoProgressStatus" function. |
+| |
+| The returned value from pul_ChronoValue parameter is |
+| not real measured timing. |
+| You must used the "i_APCI1710_ConvertChronoValue" |
+| function or make this operation for calculate the |
+| timing: |
+| |
+| Timing = pul_ChronoValue * pul_RealTimingInterval. |
+| |
+| pul_RealTimingInterval is the returned parameter from |
+| "i_APCI1710_InitChrono" function and the time unity is |
+| the b_TimingUnit from "i_APCI1710_InitChrono" function|
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : PULONG_ pb_ChronoStatus : Return the chronometer |
+| status. |
+| 0 : Measurement not started.|
+| No start signal occur. |
+| 1 : Measurement started. |
+| A start signal occur. |
+| 2 : Measurement stopped. |
+| A stop signal occur. |
+| The measurement is |
+| terminate. |
+| 3: A overflow occur. You |
+| must change the base |
+| timing witch the |
+| function |
+| "i_APCI1710_InitChrono" |
+| PULONG pul_ChronoValue : Chronometer timing value. |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a Chronometer module |
+| -4: Chronometer not initialised see function |
+| "i_APCI1710_InitChrono" |
+| -5: Timeout parameter is wrong (0 to 65535) |
+| -6: Interrupt routine installed. You can not read |
+| directly the chronometer measured timing. |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_ReadChronoValue(struct comedi_device * dev,
+ BYTE b_ModulNbr,
+ UINT ui_TimeOut, PBYTE pb_ChronoStatus, PULONG pul_ChronoValue)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_Status;
+ DWORD dw_TimeOut = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***********************/
+ /* Test if chronometer */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_CHRONOMETER) {
+ /***********************************/
+ /* Test if chronometer initialised */
+ /***********************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_ChronoModuleInfo.b_ChronoInit == 1) {
+ /*****************************/
+ /* Test the timout parameter */
+ /*****************************/
+
+ if ((ui_TimeOut >= 0)
+ && (ui_TimeOut <= 65535UL)) {
+
+ for (;;) {
+ /*******************/
+ /* Read the status */
+ /*******************/
+
+ dw_Status =
+ inl(devpriv->
+ s_BoardInfos.
+ ui_Address + 8 +
+ (64 * b_ModulNbr));
+
+ /********************/
+ /* Test if overflow */
+ /********************/
+
+ if ((dw_Status & 8) == 8) {
+ /******************/
+ /* Overflow occur */
+ /******************/
+
+ *pb_ChronoStatus = 3;
+
+ /***************************/
+ /* Test if continuous mode */
+ /***************************/
+
+ if (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_ChronoModuleInfo.
+ b_CycleMode ==
+ APCI1710_CONTINUOUS)
+ {
+ /*************************/
+ /* Clear status register */
+ /*************************/
+
+ outl(0, devpriv->s_BoardInfos.ui_Address + 36 + (64 * b_ModulNbr));
+ }
+
+ break;
+ } // if ((dw_Status & 8) == 8)
+ else {
+ /*******************************/
+ /* Test if measurement stopped */
+ /*******************************/
+
+ if ((dw_Status & 2) ==
+ 2) {
+ /***********************/
+ /* A stop signal occur */
+ /***********************/
+
+ *pb_ChronoStatus
+ = 2;
+
+ /***************************/
+ /* Test if continnous mode */
+ /***************************/
+
+ if (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_ChronoModuleInfo.
+ b_CycleMode
+ ==
+ APCI1710_CONTINUOUS)
+ {
+ /*************************/
+ /* Clear status register */
+ /*************************/
+
+ outl(0, devpriv->s_BoardInfos.ui_Address + 36 + (64 * b_ModulNbr));
+ }
+ break;
+ } // if ((dw_Status & 2) == 2)
+ else {
+ /*******************************/
+ /* Test if measurement started */
+ /*******************************/
+
+ if ((dw_Status & 1) == 1) {
+ /************************/
+ /* A start signal occur */
+ /************************/
+
+ *pb_ChronoStatus
+ =
+ 1;
+ } // if ((dw_Status & 1) == 1)
+ else {
+ /***************************/
+ /* Measurement not started */
+ /***************************/
+
+ *pb_ChronoStatus
+ =
+ 0;
+ } // if ((dw_Status & 1) == 1)
+ } // if ((dw_Status & 2) == 2)
+ } // if ((dw_Status & 8) == 8)
+
+ if (dw_TimeOut == ui_TimeOut) {
+ /*****************/
+ /* Timeout occur */
+ /*****************/
+
+ break;
+ } else {
+ /*************************/
+ /* Increment the timeout */
+ /*************************/
+
+ dw_TimeOut =
+ dw_TimeOut + 1;
+ mdelay(1000);
+
+ }
+ } // for (;;)
+
+ /*****************************/
+ /* Test if stop signal occur */
+ /*****************************/
+
+ if (*pb_ChronoStatus == 2) {
+ /**********************************/
+ /* Read the measured timing value */
+ /**********************************/
+
+ *pul_ChronoValue =
+ inl(devpriv->
+ s_BoardInfos.
+ ui_Address + 4 +
+ (64 * b_ModulNbr));
+
+ if (*pul_ChronoValue != 0) {
+ *pul_ChronoValue =
+ *pul_ChronoValue
+ - 1;
+ }
+ } else {
+ /*************************/
+ /* Test if timeout occur */
+ /*************************/
+
+ if ((*pb_ChronoStatus != 3)
+ && (dw_TimeOut ==
+ ui_TimeOut)
+ && (ui_TimeOut != 0)) {
+ /*****************/
+ /* Timeout occur */
+ /*****************/
+
+ *pb_ChronoStatus = 4;
+ }
+ }
+
+ } else {
+ /******************************/
+ /* Timeout parameter is wrong */
+ /******************************/
+ DPRINTK("Timeout parameter is wrong\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /*******************************/
+ /* Chronometer not initialised */
+ /*******************************/
+ DPRINTK("Chronometer not initialised\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /******************************************/
+ /* The module is not a Chronometer module */
+ /******************************************/
+ DPRINTK("The module is not a Chronometer module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_ConvertChronoValue |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| ULONG_ ul_ChronoValue, |
+| PULONG_ pul_Hour, |
+| PBYTE_ pb_Minute, |
+| PBYTE_ pb_Second, |
+| PUINT_ pui_MilliSecond, |
+| PUINT_ pui_MicroSecond, |
+| PUINT_ pui_NanoSecond) |
++----------------------------------------------------------------------------+
+| Task : Convert the chronometer measured timing |
+| (ul_ChronoValue) in to h, mn, s, ms, µs, ns. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number (0 to 3)|
+| ULONG_ ul_ChronoValue : Measured chronometer timing |
+| value. |
+| See"i_APCI1710_ReadChronoValue"|
++----------------------------------------------------------------------------+
+| Output Parameters : PULONG_ pul_Hour : Chronometer timing hour |
+| PBYTE_ pb_Minute : Chronometer timing minute |
+| PBYTE_ pb_Second : Chronometer timing second |
+| PUINT_ pui_MilliSecond : Chronometer timing mini |
+| second |
+| PUINT_ pui_MicroSecond : Chronometer timing micro |
+| second |
+| PUINT_ pui_NanoSecond : Chronometer timing nano |
+| second |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a Chronometer module |
+| -4: Chronometer not initialised see function |
+| "i_APCI1710_InitChrono" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_ConvertChronoValue(struct comedi_device * dev,
+ BYTE b_ModulNbr,
+ ULONG ul_ChronoValue,
+ PULONG pul_Hour,
+ PBYTE pb_Minute,
+ PBYTE pb_Second,
+ PUINT pui_MilliSecond, PUINT pui_MicroSecond, PUINT pui_NanoSecond)
+{
+ INT i_ReturnValue = 0;
+ double d_Hour;
+ double d_Minute;
+ double d_Second;
+ double d_MilliSecond;
+ double d_MicroSecond;
+ double d_NanoSecond;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***********************/
+ /* Test if chronometer */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_CHRONOMETER) {
+ /***********************************/
+ /* Test if chronometer initialised */
+ /***********************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_ChronoModuleInfo.b_ChronoInit == 1) {
+ fpu_begin();
+
+ d_Hour = (double)ul_ChronoValue *(double)
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_ChronoModuleInfo.d_TimingInterval;
+
+ switch (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_ChronoModuleInfo.b_TimingUnit) {
+ case 0:
+ d_Hour = d_Hour / (double)1000.0;
+
+ case 1:
+ d_Hour = d_Hour / (double)1000.0;
+
+ case 2:
+ d_Hour = d_Hour / (double)1000.0;
+
+ case 3:
+ d_Hour = d_Hour / (double)60.0;
+
+ case 4:
+ /**********************/
+ /* Calculate the hour */
+ /**********************/
+
+ d_Hour = d_Hour / (double)60.0;
+ *pul_Hour = (ULONG) d_Hour;
+
+ /************************/
+ /* Calculate the minute */
+ /************************/
+
+ d_Minute = d_Hour - *pul_Hour;
+ d_Minute = d_Minute * 60;
+ *pb_Minute = (BYTE) d_Minute;
+
+ /************************/
+ /* Calculate the second */
+ /************************/
+
+ d_Second = d_Minute - *pb_Minute;
+ d_Second = d_Second * 60;
+ *pb_Second = (BYTE) d_Second;
+
+ /*****************************/
+ /* Calculate the mini second */
+ /*****************************/
+
+ d_MilliSecond = d_Second - *pb_Second;
+ d_MilliSecond = d_MilliSecond * 1000;
+ *pui_MilliSecond = (UINT) d_MilliSecond;
+
+ /******************************/
+ /* Calculate the micro second */
+ /******************************/
+
+ d_MicroSecond =
+ d_MilliSecond -
+ *pui_MilliSecond;
+ d_MicroSecond = d_MicroSecond * 1000;
+ *pui_MicroSecond = (UINT) d_MicroSecond;
+
+ /******************************/
+ /* Calculate the micro second */
+ /******************************/
+
+ d_NanoSecond =
+ d_MicroSecond -
+ *pui_MicroSecond;
+ d_NanoSecond = d_NanoSecond * 1000;
+ *pui_NanoSecond = (UINT) d_NanoSecond;
+ break;
+ }
+
+ fpu_end();
+ } else {
+ /*******************************/
+ /* Chronometer not initialised */
+ /*******************************/
+ DPRINTK("Chronometer not initialised\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /******************************************/
+ /* The module is not a Chronometer module */
+ /******************************************/
+ DPRINTK("The module is not a Chronometer module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI1710_InsnBitsChronoDigitalIO(struct comedi_device *dev,struct comedi_subdevice *s,
+ struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Sets the output witch has been passed with the |
+| parameter b_Channel. Setting an output means setting an|
+| output high. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number (0 to 3)|
+| BYTE_ b_OutputChannel : Selection from digital output |
+| CR_CHAN() channel (0 to 2) |
+| 0 : Channel H |
+| 1 : Channel A |
+| 2 : Channel B |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a Chronometer module |
+| -4: The selected digital output is wrong |
+| -5: Chronometer not initialised see function |
+| "i_APCI1710_InitChrono" |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_SetChronoChlOff |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_OutputChannel) |
++----------------------------------------------------------------------------+
+| Task : Resets the output witch has been passed with the |
+| parameter b_Channel. Resetting an output means setting |
+| an output low. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710
+ data[0] : Chl ON, Chl OFF , Chl Read , Port Read
+
+| BYTE_ b_ModulNbr CR_AREF : Selected module number (0 to 3)|
+| BYTE_ b_OutputChannel CR_CHAN : Selection from digital output |
+| channel (0 to 2) |
+| 0 : Channel H |
+| 1 : Channel A |
+| 2 : Channel B |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a Chronometer module |
+| -4: The selected digital output is wrong |
+| -5: Chronometer not initialised see function |
+| "i_APCI1710_InitChrono" |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_ReadChronoChlValue |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_InputChannel, |
+| PBYTE_ pb_ChannelStatus) |
++----------------------------------------------------------------------------+
+| Task : Return the status from selected digital input |
+| (b_InputChannel) from selected chronometer |
+| module (b_ModulNbr). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number (0 to 3)|
+| BYTE_ b_InputChannel : Selection from digital input |
+| channel (0 to 2) |
+| CR_CHAN() 0 : Channel E |
+| 1 : Channel F |
+| 2 : Channel G |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_ChannelStatus : Digital input channel status.|
+| data[0] 0 : Channel is not active |
+| 1 : Channel is active |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a Chronometer module |
+| -4: The selected digital input is wrong |
+| -5: Chronometer not initialised see function |
+| "i_APCI1710_InitChrono" |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_ReadChronoPortValue |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| PBYTE_ pb_PortValue) |
++----------------------------------------------------------------------------+
+| Task : Return the status from digital inputs port from |
+| selected (b_ModulNbr) chronometer module. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number (0 to 3)|
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_PortValue : Digital inputs port status.
+| data[0]
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a Chronometer module |
+| -4: Chronometer not initialised see function |
+| "i_APCI1710_InitChrono" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnBitsChronoDigitalIO(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ BYTE b_ModulNbr, b_OutputChannel, b_InputChannel, b_IOType;
+ DWORD dw_Status;
+ PBYTE pb_ChannelStatus;
+ PBYTE pb_PortValue;
+
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ i_ReturnValue = insn->n;
+ b_IOType = (BYTE) data[0];
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***********************/
+ /* Test if chronometer */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_CHRONOMETER) {
+ /***********************************/
+ /* Test if chronometer initialised */
+ /***********************************/
+
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_ChronoModuleInfo.b_ChronoInit == 1) {
+ /***********************************/
+ /* Test the digital output channel */
+ /***********************************/
+ switch (b_IOType) {
+
+ case APCI1710_CHRONO_SET_CHANNELOFF:
+
+ b_OutputChannel =
+ (BYTE) CR_CHAN(insn->chanspec);
+ if (b_OutputChannel <= 2) {
+
+ outl(0, devpriv->s_BoardInfos.
+ ui_Address + 20 +
+ (b_OutputChannel * 4) +
+ (64 * b_ModulNbr));
+ } // if ((b_OutputChannel >= 0) && (b_OutputChannel <= 2))
+ else {
+ /****************************************/
+ /* The selected digital output is wrong */
+ /****************************************/
+
+ DPRINTK("The selected digital output is wrong\n");
+ i_ReturnValue = -4;
+
+ } // if ((b_OutputChannel >= 0) && (b_OutputChannel <= 2))
+
+ break;
+
+ case APCI1710_CHRONO_SET_CHANNELON:
+
+ b_OutputChannel =
+ (BYTE) CR_CHAN(insn->chanspec);
+ if (b_OutputChannel <= 2) {
+
+ outl(1, devpriv->s_BoardInfos.
+ ui_Address + 20 +
+ (b_OutputChannel * 4) +
+ (64 * b_ModulNbr));
+ } // if ((b_OutputChannel >= 0) && (b_OutputChannel <= 2))
+ else {
+ /****************************************/
+ /* The selected digital output is wrong */
+ /****************************************/
+
+ DPRINTK("The selected digital output is wrong\n");
+ i_ReturnValue = -4;
+
+ } // if ((b_OutputChannel >= 0) && (b_OutputChannel <= 2))
+
+ break;
+
+ case APCI1710_CHRONO_READ_CHANNEL:
+ /**********************************/
+ /* Test the digital input channel */
+ /**********************************/
+ pb_ChannelStatus = (PBYTE) & data[0];
+ b_InputChannel =
+ (BYTE) CR_CHAN(insn->chanspec);
+
+ if (b_InputChannel <= 2) {
+
+ dw_Status =
+ inl(devpriv->
+ s_BoardInfos.
+ ui_Address + 12 +
+ (64 * b_ModulNbr));
+
+ *pb_ChannelStatus =
+ (BYTE) (((dw_Status >>
+ b_InputChannel)
+ & 1) ^ 1);
+ } // if ((b_InputChannel >= 0) && (b_InputChannel <= 2))
+ else {
+ /***************************************/
+ /* The selected digital input is wrong */
+ /***************************************/
+
+ DPRINTK("The selected digital input is wrong\n");
+ i_ReturnValue = -4;
+ } // if ((b_InputChannel >= 0) && (b_InputChannel <= 2))
+
+ break;
+
+ case APCI1710_CHRONO_READ_PORT:
+
+ pb_PortValue = (PBYTE) & data[0];
+
+ dw_Status =
+ inl(devpriv->s_BoardInfos.
+ ui_Address + 12 +
+ (64 * b_ModulNbr));
+
+ *pb_PortValue =
+ (BYTE) ((dw_Status & 0x7) ^ 7);
+ break;
+ }
+ } else {
+ /*******************************/
+ /* Chronometer not initialised */
+ /*******************************/
+
+ DPRINTK("Chronometer not initialised\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /******************************************/
+ /* The module is not a Chronometer module */
+ /******************************************/
+
+ DPRINTK("The module is not a Chronometer module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_Chrono.h b/drivers/staging/comedi/drivers/addi-data/APCI1710_Chrono.h
new file mode 100644
index 000000000000..26b50cefee5a
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_Chrono.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+#define APCI1710_30MHZ 30
+#define APCI1710_33MHZ 33
+#define APCI1710_40MHZ 40
+
+#define APCI1710_SINGLE 0
+#define APCI1710_CONTINUOUS 1
+
+#define APCI1710_CHRONO_PROGRESS_STATUS 0
+#define APCI1710_CHRONO_READVALUE 1
+#define APCI1710_CHRONO_CONVERTVALUE 2
+#define APCI1710_CHRONO_READINTERRUPT 3
+
+#define APCI1710_CHRONO_SET_CHANNELON 0
+#define APCI1710_CHRONO_SET_CHANNELOFF 1
+#define APCI1710_CHRONO_READ_CHANNEL 2
+#define APCI1710_CHRONO_READ_PORT 3
+
+/*
+ * CHRONOMETER INISIALISATION FUNCTION
+ */
+INT i_APCI1710_InsnConfigInitChrono(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+INT i_APCI1710_InsnWriteEnableDisableChrono(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+
+/*
+ * CHRONOMETER READ FUNCTION
+ */
+INT i_APCI1710_InsnReadChrono(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+INT i_APCI1710_GetChronoProgressStatus(struct comedi_device *dev,
+ BYTE b_ModulNbr, PBYTE pb_ChronoStatus);
+
+INT i_APCI1710_ReadChronoValue(struct comedi_device *dev,
+ BYTE b_ModulNbr,
+ UINT ui_TimeOut, PBYTE pb_ChronoStatus,
+ PULONG pul_ChronoValue);
+
+INT i_APCI1710_ConvertChronoValue(struct comedi_device *dev,
+ BYTE b_ModulNbr,
+ ULONG ul_ChronoValue,
+ PULONG pul_Hour,
+ PBYTE pb_Minute,
+ PBYTE pb_Second,
+ PUINT pui_MilliSecond, PUINT pui_MicroSecond,
+ PUINT pui_NanoSecond);
+
+/*
+ * CHRONOMETER DIGITAL INPUT OUTPUT FUNCTION
+ */
+INT i_APCI1710_InsnBitsChronoDigitalIO(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn,
+ unsigned int *data);
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_Dig_io.c b/drivers/staging/comedi/drivers/addi-data/APCI1710_Dig_io.c
new file mode 100644
index 000000000000..8be27aedaaf5
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_Dig_io.c
@@ -0,0 +1,1020 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-----------------------------------------------------------------------+
+ | Project : API APCI1710 | Compiler : gcc |
+ | Module name : DIG_IO.C | Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-----------------------------------------------------------------------+
+ | Description : APCI-1710 digital I/O module |
+ | |
+ | |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +-----------------------------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | 16/06/98 | S. Weber | Digital input / output implementation |
+ |----------|-----------|------------------------------------------------|
+ | 08/05/00 | Guinot C | - 0400/0228 All Function in RING 0 |
+ | | | available |
+ +-----------------------------------------------------------------------+
+ | | | |
+ | | | |
+ +-----------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+#include "APCI1710_Dig_io.h"
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI1710_InsnConfigDigitalIO(struct comedi_device *dev, |
+| struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data)|
++----------------------------------------------------------------------------+
+| Task : Configure the digital I/O operating mode from selected |
+| module (b_ModulNbr). You must calling this function be|
+| for you call any other function witch access of digital|
+| I/O. |
++----------------------------------------------------------------------------+
+| Input Parameters : |
+| BYTE_ b_ModulNbr data[0]: Module number to |
+| configure (0 to 3) |
+| BYTE_ b_ChannelAMode data[1] : Channel A mode selection |
+| 0 : Channel used for digital |
+| input |
+| 1 : Channel used for digital |
+| output |
+| BYTE_ b_ChannelBMode data[2] : Channel B mode selection |
+| 0 : Channel used for digital |
+| input |
+| 1 : Channel used for digital |
+| output |
+ data[0] memory on/off
+Activates and deactivates the digital output memory.
+ After having |
+| called up this function with memory on,the output you have previously|
+| activated with the function are not reset
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The module parameter is wrong |
+| -3: The module is not a digital I/O module |
+| -4: Bi-directional channel A configuration error |
+| -5: Bi-directional channel B configuration error |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnConfigDigitalIO(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ BYTE b_ModulNbr, b_ChannelAMode, b_ChannelBMode;
+ BYTE b_MemoryOnOff, b_ConfigType;
+ INT i_ReturnValue = 0;
+ DWORD dw_WriteConfig = 0;
+
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_ConfigType = (BYTE) data[0]; // Memory or Init
+ b_ChannelAMode = (BYTE) data[1];
+ b_ChannelBMode = (BYTE) data[2];
+ b_MemoryOnOff = (BYTE) data[1]; // if memory operation
+ i_ReturnValue = insn->n;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr >= 4) {
+ DPRINTK("Module Number invalid\n");
+ i_ReturnValue = -2;
+ return i_ReturnValue;
+ }
+ switch (b_ConfigType) {
+ case APCI1710_DIGIO_MEMORYONOFF:
+
+ if (b_MemoryOnOff) // If Memory ON
+ {
+ /****************************/
+ /* Set the output memory on */
+ /****************************/
+
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_DigitalIOInfo.b_OutputMemoryEnabled = 1;
+
+ /***************************/
+ /* Clear the output memory */
+ /***************************/
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_DigitalIOInfo.dw_OutputMemory = 0;
+ } else // If memory off
+ {
+ /*****************************/
+ /* Set the output memory off */
+ /*****************************/
+
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_DigitalIOInfo.b_OutputMemoryEnabled = 0;
+ }
+ break;
+
+ case APCI1710_DIGIO_INIT:
+
+ /*******************************/
+ /* Test if digital I/O counter */
+ /*******************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_DIGITAL_IO) {
+
+ /***************************************************/
+ /* Test the bi-directional channel A configuration */
+ /***************************************************/
+
+ if ((b_ChannelAMode == 0) || (b_ChannelAMode == 1)) {
+ /***************************************************/
+ /* Test the bi-directional channel B configuration */
+ /***************************************************/
+
+ if ((b_ChannelBMode == 0)
+ || (b_ChannelBMode == 1)) {
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_DigitalIOInfo.b_DigitalInit =
+ 1;
+
+ /********************************/
+ /* Save channel A configuration */
+ /********************************/
+
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_DigitalIOInfo.
+ b_ChannelAMode = b_ChannelAMode;
+
+ /********************************/
+ /* Save channel B configuration */
+ /********************************/
+
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_DigitalIOInfo.
+ b_ChannelBMode = b_ChannelBMode;
+
+ /*****************************************/
+ /* Set the channel A and B configuration */
+ /*****************************************/
+
+ dw_WriteConfig =
+ (DWORD) (b_ChannelAMode |
+ (b_ChannelBMode * 2));
+
+ /***************************/
+ /* Write the configuration */
+ /***************************/
+
+ outl(dw_WriteConfig,
+ devpriv->s_BoardInfos.
+ ui_Address + 4 +
+ (64 * b_ModulNbr));
+
+ } else {
+ /************************************************/
+ /* Bi-directional channel B configuration error */
+ /************************************************/
+ DPRINTK("Bi-directional channel B configuration error\n");
+ i_ReturnValue = -5;
+ }
+
+ } else {
+ /************************************************/
+ /* Bi-directional channel A configuration error */
+ /************************************************/
+ DPRINTK("Bi-directional channel A configuration error\n");
+ i_ReturnValue = -4;
+
+ }
+
+ } else {
+ /******************************************/
+ /* The module is not a digital I/O module */
+ /******************************************/
+ DPRINTK("The module is not a digital I/O module\n");
+ i_ReturnValue = -3;
+ }
+ } // end of Switch
+ printk("Return Value %d\n", i_ReturnValue);
+ return i_ReturnValue;
+}
+
+/*
++----------------------------------------------------------------------------+
+| INPUT FUNCTIONS |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+
+|INT i_APCI1710_InsnReadDigitalIOChlValue(struct comedi_device *dev,comedi_subdevice
+*s, struct comedi_insn *insn,unsigned int *data)
+
++----------------------------------------------------------------------------+
+| Task : Read the status from selected digital I/O digital input|
+| (b_InputChannel) |
++----------------------------------------------------------------------------|
+
+
+|
+| BYTE_ b_ModulNbr CR_AREF(chanspec) : Selected module number |
+| (0 to 3) |
+| BYTE_ b_InputChannel CR_CHAN(chanspec) : Selection from digital |
+| input ( 0 to 6) |
+| 0 : Channel C |
+| 1 : Channel D |
+| 2 : Channel E |
+| 3 : Channel F |
+| 4 : Channel G |
+| 5 : Channel A |
+| 6 : Channel B
+
+
+ |
++----------------------------------------------------------------------------+
+| Output Parameters : data[0] : Digital input channel |
+| status |
+| 0 : Channle is not active|
+| 1 : Channle is active |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The module parameter is wrong |
+| -3: The module is not a digital I/O module |
+| -4: The selected digital I/O digital input is wrong |
+| -5: Digital I/O not initialised |
+| -6: The digital channel A is used for output |
+| -7: The digital channel B is used for output |
++----------------------------------------------------------------------------+
+*/
+
+//_INT_ i_APCI1710_ReadDigitalIOChlValue (BYTE_ b_BoardHandle,
+// BYTE_ b_ModulNbr,
+// BYTE_ b_InputChannel,
+//
+// PBYTE_ pb_ChannelStatus)
+INT i_APCI1710_InsnReadDigitalIOChlValue(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_StatusReg;
+ BYTE b_ModulNbr, b_InputChannel;
+ PBYTE pb_ChannelStatus;
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_InputChannel = (BYTE) CR_CHAN(insn->chanspec);
+ data[0] = 0;
+ pb_ChannelStatus = (PBYTE) & data[0];
+ i_ReturnValue = insn->n;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if digital I/O counter */
+ /*******************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_DIGITAL_IO) {
+ /******************************************/
+ /* Test the digital imnput channel number */
+ /******************************************/
+
+ if (b_InputChannel <= 6) {
+ /**********************************************/
+ /* Test if the digital I/O module initialised */
+ /**********************************************/
+
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_DigitalIOInfo.b_DigitalInit == 1) {
+ /**********************************/
+ /* Test if channel A or channel B */
+ /**********************************/
+
+ if (b_InputChannel > 4) {
+ /*********************/
+ /* Test if channel A */
+ /*********************/
+
+ if (b_InputChannel == 5) {
+ /***************************/
+ /* Test the channel A mode */
+ /***************************/
+
+ if (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ b_ChannelAMode
+ != 0) {
+ /********************************************/
+ /* The digital channel A is used for output */
+ /********************************************/
+
+ i_ReturnValue =
+ -6;
+ }
+ } // if (b_InputChannel == 5)
+ else {
+ /***************************/
+ /* Test the channel B mode */
+ /***************************/
+
+ if (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ b_ChannelBMode
+ != 0) {
+ /********************************************/
+ /* The digital channel B is used for output */
+ /********************************************/
+
+ i_ReturnValue =
+ -7;
+ }
+ } // if (b_InputChannel == 5)
+ } // if (b_InputChannel > 4)
+
+ /***********************/
+ /* Test if error occur */
+ /***********************/
+
+ if (i_ReturnValue >= 0) {
+ /**************************/
+ /* Read all digital input */
+ /**************************/
+
+ //INPDW (ps_APCI1710Variable->
+ // s_Board [b_BoardHandle].
+ // s_BoardInfos.
+ // ui_Address + (64 * b_ModulNbr),
+ // &dw_StatusReg);
+
+ dw_StatusReg =
+ inl(devpriv->
+ s_BoardInfos.
+ ui_Address +
+ (64 * b_ModulNbr));
+
+ *pb_ChannelStatus =
+ (BYTE) ((dw_StatusReg ^
+ 0x1C) >>
+ b_InputChannel) & 1;
+
+ } // if (i_ReturnValue == 0)
+ } else {
+ /*******************************/
+ /* Digital I/O not initialised */
+ /*******************************/
+ DPRINTK("Digital I/O not initialised\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /********************************/
+ /* Selected digital input error */
+ /********************************/
+ DPRINTK("Selected digital input error\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /******************************************/
+ /* The module is not a digital I/O module */
+ /******************************************/
+ DPRINTK("The module is not a digital I/O module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| OUTPUT FUNCTIONS |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI1710_InsnWriteDigitalIOChlOnOff(comedi_device
+|*dev,struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data)
+
++----------------------------------------------------------------------------+
+| Task : Sets or resets the output witch has been passed with the |
+| parameter b_Channel. Setting an output means setting |
+| an ouput high. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr (aref ) : Selected module number (0 to 3)|
+| BYTE_ b_OutputChannel (CR_CHAN) : Selection from digital output |
+| channel (0 to 2) |
+| 0 : Channel H |
+| 1 : Channel A |
+| 2 : Channel B |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The module parameter is wrong |
+| -3: The module is not a digital I/O module |
+| -4: The selected digital output is wrong |
+| -5: digital I/O not initialised see function |
+| " i_APCI1710_InitDigitalIO" |
+| -6: The digital channel A is used for input |
+| -7: The digital channel B is used for input
+ -8: Digital Output Memory OFF. |
+| Use previously the function |
+| "i_APCI1710_SetDigitalIOMemoryOn". |
++----------------------------------------------------------------------------+
+*/
+
+//_INT_ i_APCI1710_SetDigitalIOChlOn (BYTE_ b_BoardHandle,
+// BYTE_ b_ModulNbr,
+// BYTE_ b_OutputChannel)
+INT i_APCI1710_InsnWriteDigitalIOChlOnOff(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_WriteValue = 0;
+ BYTE b_ModulNbr, b_OutputChannel;
+ i_ReturnValue = insn->n;
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ b_OutputChannel = CR_CHAN(insn->chanspec);
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if digital I/O counter */
+ /*******************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_DIGITAL_IO) {
+ /**********************************************/
+ /* Test if the digital I/O module initialised */
+ /**********************************************/
+
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_DigitalIOInfo.b_DigitalInit == 1) {
+ /******************************************/
+ /* Test the digital output channel number */
+ /******************************************/
+
+ switch (b_OutputChannel) {
+ /*************/
+ /* Channel H */
+ /*************/
+
+ case 0:
+ break;
+
+ /*************/
+ /* Channel A */
+ /*************/
+
+ case 1:
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_DigitalIOInfo.
+ b_ChannelAMode != 1) {
+ /*******************************************/
+ /* The digital channel A is used for input */
+ /*******************************************/
+
+ i_ReturnValue = -6;
+ }
+ break;
+
+ /*************/
+ /* Channel B */
+ /*************/
+
+ case 2:
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_DigitalIOInfo.
+ b_ChannelBMode != 1) {
+ /*******************************************/
+ /* The digital channel B is used for input */
+ /*******************************************/
+
+ i_ReturnValue = -7;
+ }
+ break;
+
+ default:
+ /****************************************/
+ /* The selected digital output is wrong */
+ /****************************************/
+
+ i_ReturnValue = -4;
+ break;
+ }
+
+ /***********************/
+ /* Test if error occur */
+ /***********************/
+
+ if (i_ReturnValue >= 0) {
+
+ /*********************************/
+ /* Test if set channel ON */
+ /*********************************/
+ if (data[0]) {
+ /*********************************/
+ /* Test if output memory enabled */
+ /*********************************/
+
+ if (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ b_OutputMemoryEnabled ==
+ 1) {
+ dw_WriteValue =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ dw_OutputMemory
+ | (1 <<
+ b_OutputChannel);
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ dw_OutputMemory
+ = dw_WriteValue;
+ } else {
+ dw_WriteValue =
+ 1 <<
+ b_OutputChannel;
+ }
+ } // set channel off
+ else {
+ if (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ b_OutputMemoryEnabled ==
+ 1) {
+ dw_WriteValue =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ dw_OutputMemory
+ & (0xFFFFFFFFUL
+ -
+ (1 << b_OutputChannel));
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ dw_OutputMemory
+ = dw_WriteValue;
+ } else {
+ /*****************************/
+ /* Digital Output Memory OFF */
+ /*****************************/
+ // +Use previously the function "i_APCI1710_SetDigitalIOMemoryOn"
+ i_ReturnValue = -8;
+ }
+
+ }
+ /*******************/
+ /* Write the value */
+ /*******************/
+
+ //OUTPDW (ps_APCI1710Variable->
+ // s_Board [b_BoardHandle].
+ // s_BoardInfos.
+ // ui_Address + (64 * b_ModulNbr),
+ // dw_WriteValue);
+ outl(dw_WriteValue,
+ devpriv->s_BoardInfos.
+ ui_Address + (64 * b_ModulNbr));
+ }
+ } else {
+ /*******************************/
+ /* Digital I/O not initialised */
+ /*******************************/
+
+ i_ReturnValue = -5;
+ }
+ } else {
+ /******************************************/
+ /* The module is not a digital I/O module */
+ /******************************************/
+
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+
+|INT i_APCI1710_InsnBitsDigitalIOPortOnOff(struct comedi_device *dev,comedi_subdevice
+ *s, struct comedi_insn *insn,unsigned int *data)
++----------------------------------------------------------------------------+
+| Task : write:
+ Sets or resets one or several outputs from port. |
+| Setting an output means setting an output high. |
+| If you have switched OFF the digital output memory |
+| (OFF), all the other output are set to "0".
+
+| read:
+ Read the status from digital input port |
+| from selected digital I/O module (b_ModulNbr)
++----------------------------------------------------------------------------+
+| Input Parameters :
+ BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr CR_AREF(aref) : Selected module number (0 to 3)|
+| BYTE_ b_PortValue CR_CHAN(chanspec) : Output Value ( 0 To 7 )
+| data[0] read or write port
+ data[1] if write then indicate ON or OFF
+
+ if read : data[1] will return port status.
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value :
+
+ INPUT :
+
+ 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The module parameter is wrong |
+| -3: The module is not a digital I/O module |
+| -4: Digital I/O not initialised
+
+ OUTPUT: 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The module parameter is wrong |
+| -3: The module is not a digital I/O module |
+| -4: Output value wrong |
+| -5: digital I/O not initialised see function |
+| " i_APCI1710_InitDigitalIO" |
+| -6: The digital channel A is used for input |
+| -7: The digital channel B is used for input
+ -8: Digital Output Memory OFF. |
+| Use previously the function |
+| "i_APCI1710_SetDigitalIOMemoryOn". |
++----------------------------------------------------------------------------+
+*/
+
+//_INT_ i_APCI1710_SetDigitalIOPortOn (BYTE_ b_BoardHandle,
+// BYTE_ b_ModulNbr,
+// BYTE_ b_PortValue)
+INT i_APCI1710_InsnBitsDigitalIOPortOnOff(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_WriteValue = 0;
+ DWORD dw_StatusReg;
+ BYTE b_ModulNbr, b_PortValue;
+ BYTE b_PortOperation, b_PortOnOFF;
+
+ PBYTE pb_PortValue;
+
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_PortOperation = (BYTE) data[0]; // Input or output
+ b_PortOnOFF = (BYTE) data[1]; // if output then On or Off
+ b_PortValue = (BYTE) data[2]; // if out put then Value
+ i_ReturnValue = insn->n;
+ pb_PortValue = (PBYTE) & data[0];
+// if input then read value
+
+ switch (b_PortOperation) {
+ case APCI1710_INPUT:
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if digital I/O counter */
+ /*******************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_DIGITAL_IO) {
+ /**********************************************/
+ /* Test if the digital I/O module initialised */
+ /**********************************************/
+
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_DigitalIOInfo.b_DigitalInit == 1) {
+ /**************************/
+ /* Read all digital input */
+ /**************************/
+
+ //INPDW (ps_APCI1710Variable->
+ // s_Board [b_BoardHandle].
+ // s_BoardInfos.
+ // ui_Address + (64 * b_ModulNbr),
+ // &dw_StatusReg);
+
+ dw_StatusReg =
+ inl(devpriv->s_BoardInfos.
+ ui_Address + (64 * b_ModulNbr));
+ *pb_PortValue =
+ (BYTE) (dw_StatusReg ^ 0x1C);
+
+ } else {
+ /*******************************/
+ /* Digital I/O not initialised */
+ /*******************************/
+
+ i_ReturnValue = -4;
+ }
+ } else {
+ /******************************************/
+ /* The module is not a digital I/O module */
+ /******************************************/
+
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ i_ReturnValue = -2;
+ }
+
+ break;
+
+ case APCI1710_OUTPUT:
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if digital I/O counter */
+ /*******************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_DIGITAL_IO) {
+ /**********************************************/
+ /* Test if the digital I/O module initialised */
+ /**********************************************/
+
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_DigitalIOInfo.b_DigitalInit == 1) {
+ /***********************/
+ /* Test the port value */
+ /***********************/
+
+ if (b_PortValue <= 7) {
+ /***********************************/
+ /* Test the digital output channel */
+ /***********************************/
+
+ /**************************/
+ /* Test if channel A used */
+ /**************************/
+
+ if ((b_PortValue & 2) == 2) {
+ if (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ b_ChannelAMode
+ != 1) {
+ /*******************************************/
+ /* The digital channel A is used for input */
+ /*******************************************/
+
+ i_ReturnValue =
+ -6;
+ }
+ } // if ((b_PortValue & 2) == 2)
+
+ /**************************/
+ /* Test if channel B used */
+ /**************************/
+
+ if ((b_PortValue & 4) == 4) {
+ if (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ b_ChannelBMode
+ != 1) {
+ /*******************************************/
+ /* The digital channel B is used for input */
+ /*******************************************/
+
+ i_ReturnValue =
+ -7;
+ }
+ } // if ((b_PortValue & 4) == 4)
+
+ /***********************/
+ /* Test if error occur */
+ /***********************/
+
+ if (i_ReturnValue >= 0) {
+
+ //if(data[1])
+ //{
+ switch (b_PortOnOFF) {
+ /*********************************/
+ /* Test if set Port ON */
+ /*********************************/
+
+ case APCI1710_ON:
+
+ /*********************************/
+ /* Test if output memory enabled */
+ /*********************************/
+
+ if (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ b_OutputMemoryEnabled
+ == 1) {
+ dw_WriteValue
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ dw_OutputMemory
+ |
+ b_PortValue;
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ dw_OutputMemory
+ =
+ dw_WriteValue;
+ } else {
+ dw_WriteValue
+ =
+ b_PortValue;
+ }
+ break;
+
+ // If Set PORT OFF
+ case APCI1710_OFF:
+
+ /*********************************/
+ /* Test if output memory enabled */
+ /*********************************/
+
+ if (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ b_OutputMemoryEnabled
+ == 1) {
+ dw_WriteValue
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ dw_OutputMemory
+ &
+ (0xFFFFFFFFUL
+ -
+ b_PortValue);
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_DigitalIOInfo.
+ dw_OutputMemory
+ =
+ dw_WriteValue;
+ } else {
+ /*****************************/
+ /* Digital Output Memory OFF */
+ /*****************************/
+
+ i_ReturnValue
+ =
+ -8;
+ }
+ } // switch
+
+ /*******************/
+ /* Write the value */
+ /*******************/
+
+ // OUTPDW (ps_APCI1710Variable->
+ // s_Board [b_BoardHandle].
+ // s_BoardInfos.
+ // ui_Address + (64 * b_ModulNbr),
+ // dw_WriteValue);
+ outl(dw_WriteValue,
+ devpriv->
+ s_BoardInfos.
+ ui_Address +
+ (64 * b_ModulNbr));
+ }
+ } else {
+ /**********************/
+ /* Output value wrong */
+ /**********************/
+
+ i_ReturnValue = -4;
+ }
+ } else {
+ /*******************************/
+ /* Digital I/O not initialised */
+ /*******************************/
+
+ i_ReturnValue = -5;
+ }
+ } else {
+ /******************************************/
+ /* The module is not a digital I/O module */
+ /******************************************/
+
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ i_ReturnValue = -2;
+ }
+ break;
+
+ default:
+ i_ReturnValue = -9;
+ DPRINTK("NO INPUT/OUTPUT specified\n");
+ } //switch INPUT / OUTPUT
+ return (i_ReturnValue);
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_Dig_io.h b/drivers/staging/comedi/drivers/addi-data/APCI1710_Dig_io.h
new file mode 100644
index 000000000000..5ef157a55ca1
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_Dig_io.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+#define APCI1710_ON 1 /* Digital Output ON or OFF */
+#define APCI1710_OFF 0
+
+#define APCI1710_INPUT 0 /* Digital I/O */
+#define APCI1710_OUTPUT 1
+
+#define APCI1710_DIGIO_MEMORYONOFF 0x10
+#define APCI1710_DIGIO_INIT 0x11
+
+/*
+ * DIGITAL I/O INISIALISATION FUNCTION
+ */
+INT i_APCI1710_InsnConfigDigitalIO(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+/*
+ * INPUT OUTPUT FUNCTIONS
+ */
+INT i_APCI1710_InsnReadDigitalIOChlValue(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+INT i_APCI1710_InsnWriteDigitalIOChlOnOff(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+INT i_APCI1710_InsnBitsDigitalIOPortOnOff(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_INCCPT.c b/drivers/staging/comedi/drivers/addi-data/APCI1710_INCCPT.c
new file mode 100644
index 000000000000..1062f2fdec30
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_INCCPT.c
@@ -0,0 +1,5363 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-----------------------------------------------------------------------+
+ | Project : API APCI1710 | Compiler : gcc |
+ | Module name : INC_CPT.C | Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-----------------------------------------------------------------------+
+ | Description : APCI-1710 incremental counter module |
+ | |
+ | |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +-----------------------------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | | | |
+ |----------|-----------|------------------------------------------------|
+ | 08/05/00 | Guinot C | - 0400/0228 All Function in RING 0 |
+ | | | available |
+ +-----------------------------------------------------------------------+
+ | 29/06/01 | Guinot C. | - 1100/0231 -> 0701/0232 |
+ | | | See i_APCI1710_DisableFrequencyMeasurement |
+ +-----------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+
+#include "APCI1710_INCCPT.h"
+
+/*
++----------------------------------------------------------------------------+
+| INT i_APCI1710_InsnConfigINCCPT(struct comedi_device *dev,struct comedi_subdevice *s,
+struct comedi_insn *insn,unsigned int *data)
+
++----------------------------------------------------------------------------+
+| Task : Configuration function for INC_CPT |
++----------------------------------------------------------------------------+
+| Input Parameters : |
++----------------------------------------------------------------------------+
+| Output Parameters : *data
++----------------------------------------------------------------------------+
+| Return Value : |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnConfigINCCPT(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_ConfigType;
+ INT i_ReturnValue = 0;
+ ui_ConfigType = CR_CHAN(insn->chanspec);
+
+ printk("\nINC_CPT");
+
+ devpriv->tsk_Current = current; // Save the current process task structure
+ switch (ui_ConfigType) {
+ case APCI1710_INCCPT_INITCOUNTER:
+ i_ReturnValue = i_APCI1710_InitCounter(dev,
+ CR_AREF(insn->chanspec),
+ (BYTE) data[0],
+ (BYTE) data[1],
+ (BYTE) data[2], (BYTE) data[3], (BYTE) data[4]);
+ break;
+
+ case APCI1710_INCCPT_COUNTERAUTOTEST:
+ i_ReturnValue = i_APCI1710_CounterAutoTest(dev,
+ (PBYTE) & data[0]);
+ break;
+
+ case APCI1710_INCCPT_INITINDEX:
+ i_ReturnValue = i_APCI1710_InitIndex(dev,
+ CR_AREF(insn->chanspec),
+ (BYTE) data[0],
+ (BYTE) data[1], (BYTE) data[2], (BYTE) data[3]);
+ break;
+
+ case APCI1710_INCCPT_INITREFERENCE:
+ i_ReturnValue = i_APCI1710_InitReference(dev,
+ CR_AREF(insn->chanspec), (BYTE) data[0]);
+ break;
+
+ case APCI1710_INCCPT_INITEXTERNALSTROBE:
+ i_ReturnValue = i_APCI1710_InitExternalStrobe(dev,
+ CR_AREF(insn->chanspec),
+ (BYTE) data[0], (BYTE) data[1]);
+ break;
+
+ case APCI1710_INCCPT_INITCOMPARELOGIC:
+ i_ReturnValue = i_APCI1710_InitCompareLogic(dev,
+ CR_AREF(insn->chanspec), (UINT) data[0]);
+ break;
+
+ case APCI1710_INCCPT_INITFREQUENCYMEASUREMENT:
+ i_ReturnValue = i_APCI1710_InitFrequencyMeasurement(dev,
+ CR_AREF(insn->chanspec),
+ (BYTE) data[0],
+ (BYTE) data[1], (ULONG) data[2], (PULONG) & data[0]);
+ break;
+
+ default:
+ printk("Insn Config : Config Parameter Wrong\n");
+
+ }
+
+ if (i_ReturnValue >= 0)
+ i_ReturnValue = insn->n;
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_InitCounter |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_CounterRange, |
+| BYTE_ b_FirstCounterModus, |
+| BYTE_ b_FirstCounterOption, |
+| BYTE_ b_SecondCounterModus, |
+| BYTE_ b_SecondCounterOption) |
++----------------------------------------------------------------------------+
+| Task : Configure the counter operating mode from selected |
+| module (b_ModulNbr). You must calling this function be |
+| for you call any other function witch access of |
+| counters. |
+| |
+| Counter range |
+| ------------- |
+| +------------------------------------+-----------------------------------+ |
+| | Parameter Passed value | Description | |
+| |------------------------------------+-----------------------------------| |
+| |b_ModulNbr APCI1710_16BIT_COUNTER | The module is configured for | |
+| | | two 16-bit counter. | |
+| | | - b_FirstCounterModus and | |
+| | | b_FirstCounterOption | |
+| | | configure the first 16 bit | |
+| | | counter. | |
+| | | - b_SecondCounterModus and | |
+| | | b_SecondCounterOption | |
+| | | configure the second 16 bit | |
+| | | counter. | |
+| |------------------------------------+-----------------------------------| |
+| |b_ModulNbr APCI1710_32BIT_COUNTER | The module is configured for one | |
+| | | 32-bit counter. | |
+| | | - b_FirstCounterModus and | |
+| | | b_FirstCounterOption | |
+| | | configure the 32 bit counter. | |
+| | | - b_SecondCounterModus and | |
+| | | b_SecondCounterOption | |
+| | | are not used and have no | |
+| | | importance. | |
+| +------------------------------------+-----------------------------------+ |
+| |
+| Counter operating mode |
+| ---------------------- |
+| |
+| +--------------------+-------------------------+-------------------------+ |
+| | Parameter | Passed value | Description | |
+| |--------------------+-------------------------+-------------------------| |
+| |b_FirstCounterModus | APCI1710_QUADRUPLE_MODE | In the quadruple mode, | |
+| | or | | the edge analysis | |
+| |b_SecondCounterModus| | circuit generates a | |
+| | | | counting pulse from | |
+| | | | each edge of 2 signals | |
+| | | | which are phase shifted | |
+| | | | in relation to each | |
+| | | | other. | |
+| |--------------------+-------------------------+-------------------------| |
+| |b_FirstCounterModus | APCI1710_DOUBLE_MODE | Functions in the same | |
+| | or | | way as the quadruple | |
+| |b_SecondCounterModus| | mode, except that only | |
+| | | | two of the four edges | |
+| | | | are analysed per | |
+| | | | period | |
+| |--------------------+-------------------------+-------------------------| |
+| |b_FirstCounterModus | APCI1710_SIMPLE_MODE | Functions in the same | |
+| | or | | way as the quadruple | |
+| |b_SecondCounterModus| | mode, except that only | |
+| | | | one of the four edges | |
+| | | | is analysed per | |
+| | | | period. | |
+| |--------------------+-------------------------+-------------------------| |
+| |b_FirstCounterModus | APCI1710_DIRECT_MODE | In the direct mode the | |
+| | or | | both edge analysis | |
+| |b_SecondCounterModus| | circuits are inactive. | |
+| | | | The inputs A, B in the | |
+| | | | 32-bit mode or A, B and | |
+| | | | C, D in the 16-bit mode | |
+| | | | represent, each, one | |
+| | | | clock pulse gate circuit| |
+| | | | There by frequency and | |
+| | | | pulse duration | |
+| | | | measurements can be | |
+| | | | performed. | |
+| +--------------------+-------------------------+-------------------------+ |
+| |
+| |
+| IMPORTANT! |
+| If you have configured the module for two 16-bit counter, a mixed |
+| mode with a counter in quadruple/double/single mode |
+| and the other counter in direct mode is not possible! |
+| |
+| |
+| Counter operating option for quadruple/double/simple mode |
+| --------------------------------------------------------- |
+| |
+| +----------------------+-------------------------+------------------------+|
+| | Parameter | Passed value | Description ||
+| |----------------------+-------------------------+------------------------||
+| |b_FirstCounterOption | APCI1710_HYSTERESIS_ON | In both edge analysis ||
+| | or | | circuits is available ||
+| |b_SecondCounterOption | | one hysteresis circuit.||
+| | | | It suppresses each ||
+| | | | time the first counting||
+| | | | pulse after a change ||
+| | | | of rotation. ||
+| |----------------------+-------------------------+------------------------||
+| |b_FirstCounterOption | APCI1710_HYSTERESIS_OFF | The first counting ||
+| | or | | pulse is not suppress ||
+| |b_SecondCounterOption | | after a change of ||
+| | | | rotation. ||
+| +----------------------+-------------------------+------------------------+|
+| |
+| |
+| IMPORTANT! |
+| This option are only avaible if you have selected the direct mode. |
+| |
+| |
+| Counter operating option for direct mode |
+| ---------------------------------------- |
+| |
+| +----------------------+--------------------+----------------------------+ |
+| | Parameter | Passed value | Description | |
+| |----------------------+--------------------+----------------------------| |
+| |b_FirstCounterOption | APCI1710_INCREMENT | The counter increment for | |
+| | or | | each counting pulse | |
+| |b_SecondCounterOption | | | |
+| |----------------------+--------------------+----------------------------| |
+| |b_FirstCounterOption | APCI1710_DECREMENT | The counter decrement for | |
+| | or | | each counting pulse | |
+| |b_SecondCounterOption | | | |
+| +----------------------+--------------------+----------------------------+ |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710|
+| BYTE_ b_ModulNbr : Module number to |
+| configure (0 to 3) |
+| BYTE_ b_CounterRange : Selection form counter |
+| range. |
+| BYTE_ b_FirstCounterModus : First counter operating |
+| mode. |
+| BYTE_ b_FirstCounterOption : First counter option. |
+| BYTE_ b_SecondCounterModus : Second counter operating |
+| mode. |
+| BYTE_ b_SecondCounterOption : Second counter option. |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The module is not a counter module |
+| -3: The selected counter range is wrong. |
+| -4: The selected first counter operating mode is wrong. |
+| -5: The selected first counter operating option is wrong|
+| -6: The selected second counter operating mode is wrong.|
+| -7: The selected second counter operating option is |
+| wrong. |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InitCounter(struct comedi_device * dev,
+ BYTE b_ModulNbr,
+ BYTE b_CounterRange,
+ BYTE b_FirstCounterModus,
+ BYTE b_FirstCounterOption,
+ BYTE b_SecondCounterModus, BYTE b_SecondCounterOption)
+{
+ INT i_ReturnValue = 0;
+
+ /*******************************/
+ /* Test if incremental counter */
+ /*******************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF0000UL) ==
+ APCI1710_INCREMENTAL_COUNTER) {
+ /**************************/
+ /* Test the counter range */
+ /**************************/
+
+ if (b_CounterRange == APCI1710_16BIT_COUNTER
+ || b_CounterRange == APCI1710_32BIT_COUNTER) {
+ /********************************/
+ /* Test the first counter modus */
+ /********************************/
+
+ if (b_FirstCounterModus == APCI1710_QUADRUPLE_MODE ||
+ b_FirstCounterModus == APCI1710_DOUBLE_MODE ||
+ b_FirstCounterModus == APCI1710_SIMPLE_MODE ||
+ b_FirstCounterModus == APCI1710_DIRECT_MODE) {
+ /*********************************/
+ /* Test the first counter option */
+ /*********************************/
+
+ if ((b_FirstCounterModus == APCI1710_DIRECT_MODE
+ && (b_FirstCounterOption ==
+ APCI1710_INCREMENT
+ || b_FirstCounterOption
+ == APCI1710_DECREMENT))
+ || (b_FirstCounterModus !=
+ APCI1710_DIRECT_MODE
+ && (b_FirstCounterOption ==
+ APCI1710_HYSTERESIS_ON
+ || b_FirstCounterOption
+ ==
+ APCI1710_HYSTERESIS_OFF)))
+ {
+ /**************************/
+ /* Test if 16-bit counter */
+ /**************************/
+
+ if (b_CounterRange ==
+ APCI1710_16BIT_COUNTER) {
+ /*********************************/
+ /* Test the second counter modus */
+ /*********************************/
+
+ if ((b_FirstCounterModus !=
+ APCI1710_DIRECT_MODE
+ &&
+ (b_SecondCounterModus
+ ==
+ APCI1710_QUADRUPLE_MODE
+ ||
+ b_SecondCounterModus
+ ==
+ APCI1710_DOUBLE_MODE
+ ||
+ b_SecondCounterModus
+ ==
+ APCI1710_SIMPLE_MODE))
+ || (b_FirstCounterModus
+ ==
+ APCI1710_DIRECT_MODE
+ &&
+ b_SecondCounterModus
+ ==
+ APCI1710_DIRECT_MODE))
+ {
+ /**********************************/
+ /* Test the second counter option */
+ /**********************************/
+
+ if ((b_SecondCounterModus == APCI1710_DIRECT_MODE && (b_SecondCounterOption == APCI1710_INCREMENT || b_SecondCounterOption == APCI1710_DECREMENT)) || (b_SecondCounterModus != APCI1710_DIRECT_MODE && (b_SecondCounterOption == APCI1710_HYSTERESIS_ON || b_SecondCounterOption == APCI1710_HYSTERESIS_OFF))) {
+ i_ReturnValue =
+ 0;
+ } else {
+ /*********************************************************/
+ /* The selected second counter operating option is wrong */
+ /*********************************************************/
+
+ DPRINTK("The selected second counter operating option is wrong\n");
+ i_ReturnValue =
+ -7;
+ }
+ } else {
+ /*******************************************************/
+ /* The selected second counter operating mode is wrong */
+ /*******************************************************/
+
+ DPRINTK("The selected second counter operating mode is wrong\n");
+ i_ReturnValue = -6;
+ }
+ }
+ } else {
+ /********************************************************/
+ /* The selected first counter operating option is wrong */
+ /********************************************************/
+
+ DPRINTK("The selected first counter operating option is wrong\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /******************************************************/
+ /* The selected first counter operating mode is wrong */
+ /******************************************************/
+ DPRINTK("The selected first counter operating mode is wrong\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /***************************************/
+ /* The selected counter range is wrong */
+ /***************************************/
+
+ DPRINTK("The selected counter range is wrong\n");
+ i_ReturnValue = -3;
+ }
+
+ /*************************/
+ /* Test if a error occur */
+ /*************************/
+
+ if (i_ReturnValue == 0) {
+ /**************************/
+ /* Test if 16-Bit counter */
+ /**************************/
+
+ if (b_CounterRange == APCI1710_32BIT_COUNTER) {
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister1 = b_CounterRange |
+ b_FirstCounterModus |
+ b_FirstCounterOption;
+ } else {
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister1 = b_CounterRange |
+ (b_FirstCounterModus & 0x5) |
+ (b_FirstCounterOption & 0x20) |
+ (b_SecondCounterModus & 0xA) |
+ (b_SecondCounterOption & 0x40);
+
+ /***********************/
+ /* Test if direct mode */
+ /***********************/
+
+ if (b_FirstCounterModus == APCI1710_DIRECT_MODE) {
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister1 = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister1 |
+ APCI1710_DIRECT_MODE;
+ }
+ }
+
+ /***************************/
+ /* Write the configuration */
+ /***************************/
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ dw_ModeRegister1_2_3_4,
+ devpriv->s_BoardInfos.
+ ui_Address + 20 + (64 * b_ModulNbr));
+
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.b_CounterInit = 1;
+ }
+ } else {
+ /**************************************/
+ /* The module is not a counter module */
+ /**************************************/
+
+ DPRINTK("The module is not a counter module\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_CounterAutoTest |
+| (BYTE_ b_BoardHandle, |
+| PBYTE_ pb_TestStatus) |
++----------------------------------------------------------------------------+
+| Task : A test mode is intended for testing the component and |
+| the connected periphery. All the 8-bit counter chains |
+| are operated internally as down counters. |
+| Independently from the external signals, |
+| all the four 8-bit counter chains are decremented in |
+| parallel by each negative clock pulse edge of CLKX. |
+| |
+| Counter auto test conclusion |
+| ---------------------------- |
+| +-----------------+-----------------------------+ |
+| | pb_TestStatus | Error description | |
+| | mask | | |
+| |-----------------+-----------------------------| |
+| | 0000 | No error detected | |
+| |-----------------|-----------------------------| |
+| | 0001 | Error detected of counter 0 | |
+| |-----------------|-----------------------------| |
+| | 0010 | Error detected of counter 1 | |
+| |-----------------|-----------------------------| |
+| | 0100 | Error detected of counter 2 | |
+| |-----------------|-----------------------------| |
+| | 1000 | Error detected of counter 3 | |
+| +-----------------+-----------------------------+ |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 | |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_TestStatus : Auto test conclusion. See table|
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_CounterAutoTest(struct comedi_device * dev, PBYTE pb_TestStatus)
+{
+ BYTE b_ModulCpt = 0;
+ INT i_ReturnValue = 0;
+ DWORD dw_LathchValue;
+
+ *pb_TestStatus = 0;
+
+ /********************************/
+ /* Test if counter module found */
+ /********************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[0] & 0xFFFF0000UL) ==
+ APCI1710_INCREMENTAL_COUNTER
+ || (devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[1] & 0xFFFF0000UL) ==
+ APCI1710_INCREMENTAL_COUNTER
+ || (devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[2] & 0xFFFF0000UL) ==
+ APCI1710_INCREMENTAL_COUNTER
+ || (devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[3] & 0xFFFF0000UL) ==
+ APCI1710_INCREMENTAL_COUNTER) {
+ for (b_ModulCpt = 0; b_ModulCpt < 4; b_ModulCpt++) {
+ /*******************************/
+ /* Test if incremental counter */
+ /*******************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulCpt] &
+ 0xFFFF0000UL) ==
+ APCI1710_INCREMENTAL_COUNTER) {
+ /******************/
+ /* Start the test */
+ /******************/
+
+ outl(3, devpriv->s_BoardInfos.
+ ui_Address + 16 + (64 * b_ModulCpt));
+
+ /*********************/
+ /* Tatch the counter */
+ /*********************/
+
+ outl(1, devpriv->s_BoardInfos.
+ ui_Address + (64 * b_ModulCpt));
+
+ /************************/
+ /* Read the latch value */
+ /************************/
+
+ dw_LathchValue = inl(devpriv->s_BoardInfos.
+ ui_Address + 4 + (64 * b_ModulCpt));
+
+ if ((dw_LathchValue & 0xFF) !=
+ ((dw_LathchValue >> 8) & 0xFF)
+ && (dw_LathchValue & 0xFF) !=
+ ((dw_LathchValue >> 16) & 0xFF)
+ && (dw_LathchValue & 0xFF) !=
+ ((dw_LathchValue >> 24) & 0xFF)) {
+ *pb_TestStatus =
+ *pb_TestStatus | (1 <<
+ b_ModulCpt);
+ }
+
+ /*****************/
+ /* Stop the test */
+ /*****************/
+
+ outl(0, devpriv->s_BoardInfos.
+ ui_Address + 16 + (64 * b_ModulCpt));
+ }
+ }
+ } else {
+ /***************************/
+ /* No counter module found */
+ /***************************/
+
+ DPRINTK("No counter module found\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_InitIndex (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_ReferenceAction, |
+| BYTE_ b_IndexOperation, |
+| BYTE_ b_AutoMode, |
+| BYTE_ b_InterruptEnable) |
++----------------------------------------------------------------------------+
+| Task : Initialise the index corresponding to the selected |
+| module (b_ModulNbr). If a INDEX flag occur, you have |
+| the possibility to clear the 32-Bit counter or to latch|
+| the current 32-Bit value in to the first latch |
+| register. The b_IndexOperation parameter give the |
+| possibility to choice the INDEX action. |
+| If you have enabled the automatic mode, each INDEX |
+| action is cleared automatically, else you must read |
+| the index status ("i_APCI1710_ReadIndexStatus") |
+| after each INDEX action. |
+| |
+| |
+| Index action |
+| ------------ |
+| |
+| +------------------------+------------------------------------+ |
+| | b_IndexOperation | Operation | |
+| |------------------------+------------------------------------| |
+| |APCI1710_LATCH_COUNTER | After a index signal, the counter | |
+| | | value (32-Bit) is latched in to | |
+| | | the first latch register | |
+| |------------------------|------------------------------------| |
+| |APCI1710_CLEAR_COUNTER | After a index signal, the counter | |
+| | | value is cleared (32-Bit) | |
+| +------------------------+------------------------------------+ |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
+| BYTE_ b_ReferenceAction : Determine if the reference |
+| must set or no for the |
+| acceptance from index |
+| APCI1710_ENABLE : |
+| Reference must be set for |
+| accepted the index |
+| APCI1710_DISABLE : |
+| Reference have not |
+| importance |
+| BYTE_ b_IndexOperation : Index operating mode. |
+| See table. |
+| BYTE_ b_AutoMode : Enable or disable the |
+| automatic index reset. |
+| APCI1710_ENABLE : |
+| Enable the automatic mode |
+| APCI1710_DISABLE : |
+| Disable the automatic mode |
+| BYTE_ b_InterruptEnable : Enable or disable the |
+| interrupt. |
+| APCI1710_ENABLE : |
+| Enable the interrupt |
+| APCI1710_DISABLE : |
+| Disable the interrupt |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4 The reference action parameter is wrong |
+| -5: The index operating mode parameter is wrong |
+| -6: The auto mode parameter is wrong |
+| -7: Interrupt parameter is wrong |
+| -8: Interrupt function not initialised. |
+| See function "i_APCI1710_SetBoardIntRoutineX" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InitIndex(struct comedi_device * dev,
+ BYTE b_ModulNbr,
+ BYTE b_ReferenceAction,
+ BYTE b_IndexOperation, BYTE b_AutoMode, BYTE b_InterruptEnable)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /********************************/
+ /* Test the reference parameter */
+ /********************************/
+
+ if (b_ReferenceAction == APCI1710_ENABLE ||
+ b_ReferenceAction == APCI1710_DISABLE) {
+ /****************************/
+ /* Test the index parameter */
+ /****************************/
+
+ if (b_IndexOperation ==
+ APCI1710_HIGH_EDGE_LATCH_COUNTER
+ || b_IndexOperation ==
+ APCI1710_LOW_EDGE_LATCH_COUNTER
+ || b_IndexOperation ==
+ APCI1710_HIGH_EDGE_CLEAR_COUNTER
+ || b_IndexOperation ==
+ APCI1710_LOW_EDGE_CLEAR_COUNTER
+ || b_IndexOperation ==
+ APCI1710_HIGH_EDGE_LATCH_AND_CLEAR_COUNTER
+ || b_IndexOperation ==
+ APCI1710_LOW_EDGE_LATCH_AND_CLEAR_COUNTER)
+ {
+ /********************************/
+ /* Test the auto mode parameter */
+ /********************************/
+
+ if (b_AutoMode == APCI1710_ENABLE ||
+ b_AutoMode == APCI1710_DISABLE)
+ {
+ /***************************/
+ /* Test the interrupt mode */
+ /***************************/
+
+ if (b_InterruptEnable ==
+ APCI1710_ENABLE
+ || b_InterruptEnable ==
+ APCI1710_DISABLE) {
+
+ /************************************/
+ /* Makte the configuration commando */
+ /************************************/
+
+ if (b_ReferenceAction ==
+ APCI1710_ENABLE)
+ {
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2
+ |
+ APCI1710_ENABLE_INDEX_ACTION;
+ } else {
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2
+ &
+ APCI1710_DISABLE_INDEX_ACTION;
+ }
+
+ /****************************************/
+ /* Test if low level latch or/and clear */
+ /****************************************/
+
+ if (b_IndexOperation ==
+ APCI1710_LOW_EDGE_LATCH_COUNTER
+ ||
+ b_IndexOperation
+ ==
+ APCI1710_LOW_EDGE_CLEAR_COUNTER
+ ||
+ b_IndexOperation
+ ==
+ APCI1710_LOW_EDGE_LATCH_AND_CLEAR_COUNTER)
+ {
+ /*************************************/
+ /* Set the index level to low (DQ26) */
+ /*************************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ |
+ APCI1710_SET_LOW_INDEX_LEVEL;
+ } else {
+ /**************************************/
+ /* Set the index level to high (DQ26) */
+ /**************************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ &
+ APCI1710_SET_HIGH_INDEX_LEVEL;
+ }
+
+ /***********************************/
+ /* Test if latch and clear counter */
+ /***********************************/
+
+ if (b_IndexOperation ==
+ APCI1710_HIGH_EDGE_LATCH_AND_CLEAR_COUNTER
+ ||
+ b_IndexOperation
+ ==
+ APCI1710_LOW_EDGE_LATCH_AND_CLEAR_COUNTER)
+ {
+ /***************************************/
+ /* Set the latch and clear flag (DQ27) */
+ /***************************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ |
+ APCI1710_ENABLE_LATCH_AND_CLEAR;
+ } // if (b_IndexOperation == APCI1710_HIGH_EDGE_LATCH_AND_CLEAR_COUNTER || b_IndexOperation == APCI1710_LOW_EDGE_LATCH_AND_CLEAR_COUNTER)
+ else {
+ /*****************************************/
+ /* Clear the latch and clear flag (DQ27) */
+ /*****************************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ &
+ APCI1710_DISABLE_LATCH_AND_CLEAR;
+
+ /*************************/
+ /* Test if latch counter */
+ /*************************/
+
+ if (b_IndexOperation == APCI1710_HIGH_EDGE_LATCH_COUNTER || b_IndexOperation == APCI1710_LOW_EDGE_LATCH_COUNTER) {
+ /*********************************/
+ /* Enable the latch from counter */
+ /*********************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2
+ |
+ APCI1710_INDEX_LATCH_COUNTER;
+ } else {
+ /*********************************/
+ /* Enable the clear from counter */
+ /*********************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2
+ &
+ (~APCI1710_INDEX_LATCH_COUNTER);
+ }
+ } // // if (b_IndexOperation == APCI1710_HIGH_EDGE_LATCH_AND_CLEAR_COUNTER || b_IndexOperation == APCI1710_LOW_EDGE_LATCH_AND_CLEAR_COUNTER)
+
+ if (b_AutoMode ==
+ APCI1710_DISABLE)
+ {
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2
+ |
+ APCI1710_INDEX_AUTO_MODE;
+ } else {
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2
+ &
+ (~APCI1710_INDEX_AUTO_MODE);
+ }
+
+ if (b_InterruptEnable ==
+ APCI1710_ENABLE)
+ {
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3
+ |
+ APCI1710_ENABLE_INDEX_INT;
+ } else {
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3
+ &
+ APCI1710_DISABLE_INDEX_INT;
+ }
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.
+ b_IndexInit = 1;
+
+ } else {
+ /********************************/
+ /* Interrupt parameter is wrong */
+ /********************************/
+ DPRINTK("Interrupt parameter is wrong\n");
+ i_ReturnValue = -7;
+ }
+ } else {
+ /************************************/
+ /* The auto mode parameter is wrong */
+ /************************************/
+
+ DPRINTK("The auto mode parameter is wrong\n");
+ i_ReturnValue = -6;
+ }
+ } else {
+ /***********************************************/
+ /* The index operating mode parameter is wrong */
+ /***********************************************/
+
+ DPRINTK("The index operating mode parameter is wrong\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /*******************************************/
+ /* The reference action parameter is wrong */
+ /*******************************************/
+
+ DPRINTK("The reference action parameter is wrong\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_InitReference |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_ReferenceLevel) |
++----------------------------------------------------------------------------+
+| Task : Initialise the reference corresponding to the selected |
+| module (b_ModulNbr). |
+| |
+| Reference level |
+| --------------- |
+| +--------------------+-------------------------+ |
+| | b_ReferenceLevel | Operation | |
+| +--------------------+-------------------------+ |
+| | APCI1710_LOW | Reference occur if "0" | |
+| |--------------------|-------------------------| |
+| | APCI1710_HIGH | Reference occur if "1" | |
+| +--------------------+-------------------------+ |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
+| BYTE_ b_ReferenceLevel : Reference level. |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The selected module number parameter is wrong |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: Reference level parameter is wrong |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InitReference(struct comedi_device * dev,
+ BYTE b_ModulNbr, BYTE b_ReferenceLevel)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /**************************************/
+ /* Test the reference level parameter */
+ /**************************************/
+
+ if (b_ReferenceLevel == 0 || b_ReferenceLevel == 1) {
+ if (b_ReferenceLevel == 1) {
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2 = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2 |
+ APCI1710_REFERENCE_HIGH;
+ } else {
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2 = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2 &
+ APCI1710_REFERENCE_LOW;
+ }
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ dw_ModeRegister1_2_3_4,
+ devpriv->s_BoardInfos.ui_Address + 20 +
+ (64 * b_ModulNbr));
+
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.b_ReferenceInit = 1;
+ } else {
+ /**************************************/
+ /* Reference level parameter is wrong */
+ /**************************************/
+
+ DPRINTK("Reference level parameter is wrong\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_InitExternalStrobe |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_ExternalStrobe, |
+| BYTE_ b_ExternalStrobeLevel) |
++----------------------------------------------------------------------------+
+| Task : Initialises the external strobe level corresponding to |
+| the selected module (b_ModulNbr). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
+| BYTE_ b_ExternalStrobe : External strobe selection |
+| 0 : External strobe A |
+| 1 : External strobe B |
+| BYTE_ b_ExternalStrobeLevel : External strobe level |
+| APCI1710_LOW : |
+| External latch occurs if "0" |
+| APCI1710_HIGH : |
+| External latch occurs if "1" |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The selected module number is wrong |
+| -3: Counter not initialised. |
+| See function "i_APCI1710_InitCounter" |
+| -4: External strobe selection is wrong |
+| -5: External strobe level parameter is wrong |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InitExternalStrobe(struct comedi_device * dev,
+ BYTE b_ModulNbr, BYTE b_ExternalStrobe, BYTE b_ExternalStrobeLevel)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /**************************************/
+ /* Test the external strobe selection */
+ /**************************************/
+
+ if (b_ExternalStrobe == 0 || b_ExternalStrobe == 1) {
+ /******************/
+ /* Test the level */
+ /******************/
+
+ if ((b_ExternalStrobeLevel == APCI1710_HIGH) ||
+ ((b_ExternalStrobeLevel == APCI1710_LOW
+ && (devpriv->
+ s_BoardInfos.
+ dw_MolduleConfiguration
+ [b_ModulNbr] &
+ 0xFFFF) >=
+ 0x3135))) {
+ /*****************/
+ /* Set the level */
+ /*****************/
+
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4 = (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4 & (0xFF -
+ (0x10 << b_ExternalStrobe))) | ((b_ExternalStrobeLevel ^ 1) << (4 + b_ExternalStrobe));
+ } else {
+ /********************************************/
+ /* External strobe level parameter is wrong */
+ /********************************************/
+
+ DPRINTK("External strobe level parameter is wrong\n");
+ i_ReturnValue = -5;
+ }
+ } // if (b_ExternalStrobe == 0 || b_ExternalStrobe == 1)
+ else {
+ /**************************************/
+ /* External strobe selection is wrong */
+ /**************************************/
+
+ DPRINTK("External strobe selection is wrong\n");
+ i_ReturnValue = -4;
+ } // if (b_ExternalStrobe == 0 || b_ExternalStrobe == 1)
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+ /*
+ +----------------------------------------------------------------------------+
+ | Function Name : _INT_ i_APCI1710_InitCompareLogic |
+ | (BYTE_ b_BoardHandle, |
+ | BYTE_ b_ModulNbr, |
+ | UINT_ ui_CompareValue) |
+ +----------------------------------------------------------------------------+
+ | Task : Set the 32-Bit compare value. At that moment that the |
+ | incremental counter arrive to the compare value |
+ | (ui_CompareValue) a interrupt is generated. |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+ | BYTE_ b_ModulNbr : Module number to configure |
+ | (0 to 3) |
+ | UINT_ ui_CompareValue : 32-Bit compare value |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -
+ +----------------------------------------------------------------------------+
+ | Return Value : 0: No error |
+ | -1: The handle parameter of the board is wrong |
+ | -2: No counter module found |
+ | -3: Counter not initialised see function |
+ | "i_APCI1710_InitCounter" |
+ +----------------------------------------------------------------------------+
+ */
+
+INT i_APCI1710_InitCompareLogic(struct comedi_device * dev,
+ BYTE b_ModulNbr, UINT ui_CompareValue)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+
+ outl(ui_CompareValue, devpriv->s_BoardInfos.
+ ui_Address + 28 + (64 * b_ModulNbr));
+
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.b_CompareLogicInit = 1;
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_InitFrequencyMeasurement |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_PCIInputClock, |
+| BYTE_ b_TimingUnity, |
+| ULONG_ ul_TimingInterval, |
+| PULONG_ pul_RealTimingInterval) |
++----------------------------------------------------------------------------+
+| Task : Sets the time for the frequency measurement. |
+| Configures the selected TOR incremental counter of the |
+| selected module (b_ModulNbr). The ul_TimingInterval and|
+| ul_TimingUnity determine the time base for the |
+| measurement. The pul_RealTimingInterval returns the |
+| real time value. You must call up this function before |
+| you call up any other function which gives access to |
+| the frequency measurement. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Number of the module to be |
+| configured (0 to 3) |
+| BYTE_ b_PCIInputClock : Selection of the PCI bus |
+| clock |
+| - APCI1710_30MHZ : |
+| The PC has a PCI bus clock |
+| of 30 MHz |
+| - APCI1710_33MHZ : |
+| The PC has a PCI bus clock |
+| of 33 MHz |
+| BYTE_ b_TimingUnity : Base time unit (0 to 2) |
+| 0 : ns |
+| 1 : æs |
+| 2 : ms |
+| ULONG_ ul_TimingInterval: Base time value. |
++----------------------------------------------------------------------------+
+| Output Parameters : PULONG_ pul_RealTimingInterval : Real base time value. |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The selected module number is wrong |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: The selected PCI input clock is wrong |
+| -5: Timing unity selection is wrong |
+| -6: Base timing selection is wrong |
+| -7: 40MHz quartz not on board |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InitFrequencyMeasurement(struct comedi_device * dev,
+ BYTE b_ModulNbr,
+ BYTE b_PCIInputClock,
+ BYTE b_TimingUnity,
+ ULONG ul_TimingInterval, PULONG pul_RealTimingInterval)
+{
+ INT i_ReturnValue = 0;
+ ULONG ul_TimerValue = 0;
+ double d_RealTimingInterval;
+ DWORD dw_Status = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /**************************/
+ /* Test the PCI bus clock */
+ /**************************/
+
+ if ((b_PCIInputClock == APCI1710_30MHZ) ||
+ (b_PCIInputClock == APCI1710_33MHZ) ||
+ (b_PCIInputClock == APCI1710_40MHZ)) {
+ /************************/
+ /* Test the timing unit */
+ /************************/
+
+ if (b_TimingUnity <= 2) {
+ /**********************************/
+ /* Test the base timing selection */
+ /**********************************/
+
+ if (((b_PCIInputClock == APCI1710_30MHZ)
+ && (b_TimingUnity == 0)
+ && (ul_TimingInterval >=
+ 266)
+ && (ul_TimingInterval <=
+ 8738133UL))
+ || ((b_PCIInputClock ==
+ APCI1710_30MHZ)
+ && (b_TimingUnity == 1)
+ && (ul_TimingInterval >=
+ 1)
+ && (ul_TimingInterval <=
+ 8738UL))
+ || ((b_PCIInputClock ==
+ APCI1710_30MHZ)
+ && (b_TimingUnity == 2)
+ && (ul_TimingInterval >=
+ 1)
+ && (ul_TimingInterval <=
+ 8UL))
+ || ((b_PCIInputClock ==
+ APCI1710_33MHZ)
+ && (b_TimingUnity == 0)
+ && (ul_TimingInterval >=
+ 242)
+ && (ul_TimingInterval <=
+ 7943757UL))
+ || ((b_PCIInputClock ==
+ APCI1710_33MHZ)
+ && (b_TimingUnity == 1)
+ && (ul_TimingInterval >=
+ 1)
+ && (ul_TimingInterval <=
+ 7943UL))
+ || ((b_PCIInputClock ==
+ APCI1710_33MHZ)
+ && (b_TimingUnity == 2)
+ && (ul_TimingInterval >=
+ 1)
+ && (ul_TimingInterval <=
+ 7UL))
+ || ((b_PCIInputClock ==
+ APCI1710_40MHZ)
+ && (b_TimingUnity == 0)
+ && (ul_TimingInterval >=
+ 200)
+ && (ul_TimingInterval <=
+ 6553500UL))
+ || ((b_PCIInputClock ==
+ APCI1710_40MHZ)
+ && (b_TimingUnity == 1)
+ && (ul_TimingInterval >=
+ 1)
+ && (ul_TimingInterval <=
+ 6553UL))
+ || ((b_PCIInputClock ==
+ APCI1710_40MHZ)
+ && (b_TimingUnity == 2)
+ && (ul_TimingInterval >=
+ 1)
+ && (ul_TimingInterval <=
+ 6UL))) {
+ /**********************/
+ /* Test if 40MHz used */
+ /**********************/
+
+ if (b_PCIInputClock ==
+ APCI1710_40MHZ) {
+ /******************************/
+ /* Test if firmware >= Rev1.5 */
+ /******************************/
+
+ if ((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF) >= 0x3135) {
+ /*********************************/
+ /* Test if 40MHz quartz on board */
+ /*********************************/
+
+ /*INPDW (ps_APCI1710Variable->
+ s_Board [b_BoardHandle].
+ s_BoardInfos.
+ ui_Address + 36 + (64 * b_ModulNbr), &dw_Status); */
+ dw_Status =
+ inl
+ (devpriv->
+ s_BoardInfos.
+ ui_Address
+ + 36 +
+ (64 * b_ModulNbr));
+
+ /******************************/
+ /* Test the quartz flag (DQ0) */
+ /******************************/
+
+ if ((dw_Status & 1) != 1) {
+ /*****************************/
+ /* 40MHz quartz not on board */
+ /*****************************/
+
+ DPRINTK("40MHz quartz not on board\n");
+ i_ReturnValue
+ =
+ -7;
+ }
+ } else {
+ /*****************************/
+ /* 40MHz quartz not on board */
+ /*****************************/
+ DPRINTK("40MHz quartz not on board\n");
+ i_ReturnValue =
+ -7;
+ }
+ } // if (b_PCIInputClock == APCI1710_40MHZ)
+
+ /***************************/
+ /* Test if not error occur */
+ /***************************/
+
+ if (i_ReturnValue == 0) {
+ /****************************/
+ /* Test the INC_CPT version */
+ /****************************/
+
+ if ((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF) >= 0x3131) {
+
+ /**********************/
+ /* Test if 40MHz used */
+ /**********************/
+
+ if (b_PCIInputClock == APCI1710_40MHZ) {
+ /*********************************/
+ /* Enable the 40MHz quarz (DQ30) */
+ /*********************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ |
+ APCI1710_ENABLE_40MHZ_FREQUENCY;
+ } // if (b_PCIInputClock == APCI1710_40MHZ)
+ else {
+ /**********************************/
+ /* Disable the 40MHz quarz (DQ30) */
+ /**********************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ &
+ APCI1710_DISABLE_40MHZ_FREQUENCY;
+
+ } // if (b_PCIInputClock == APCI1710_40MHZ)
+
+ /********************************/
+ /* Calculate the division fator */
+ /********************************/
+
+ fpu_begin();
+ switch (b_TimingUnity) {
+ /******/
+ /* ns */
+ /******/
+
+ case 0:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_TimerValue
+ =
+ (ULONG)
+ (ul_TimingInterval
+ *
+ (0.00025 * b_PCIInputClock));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_TimingInterval * (0.00025 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
+ ul_TimerValue
+ =
+ ul_TimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ *pul_RealTimingInterval
+ =
+ (ULONG)
+ (ul_TimerValue
+ /
+ (0.00025 * (double)b_PCIInputClock));
+ d_RealTimingInterval
+ =
+ (double)
+ ul_TimerValue
+ /
+ (0.00025
+ *
+ (double)
+ b_PCIInputClock);
+
+ if ((double)((double)ul_TimerValue / (0.00025 * (double)b_PCIInputClock)) >= (double)((double)*pul_RealTimingInterval + 0.5)) {
+ *pul_RealTimingInterval
+ =
+ *pul_RealTimingInterval
+ +
+ 1;
+ }
+
+ ul_TimingInterval
+ =
+ ul_TimingInterval
+ -
+ 1;
+ ul_TimerValue
+ =
+ ul_TimerValue
+ -
+ 2;
+
+ break;
+
+ /******/
+ /* æs */
+ /******/
+
+ case 1:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_TimerValue
+ =
+ (ULONG)
+ (ul_TimingInterval
+ *
+ (0.25 * b_PCIInputClock));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_TimingInterval * (0.25 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
+ ul_TimerValue
+ =
+ ul_TimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ *pul_RealTimingInterval
+ =
+ (ULONG)
+ (ul_TimerValue
+ /
+ (0.25 * (double)b_PCIInputClock));
+ d_RealTimingInterval
+ =
+ (double)
+ ul_TimerValue
+ /
+ (
+ (double)
+ 0.25
+ *
+ (double)
+ b_PCIInputClock);
+
+ if ((double)((double)ul_TimerValue / (0.25 * (double)b_PCIInputClock)) >= (double)((double)*pul_RealTimingInterval + 0.5)) {
+ *pul_RealTimingInterval
+ =
+ *pul_RealTimingInterval
+ +
+ 1;
+ }
+
+ ul_TimingInterval
+ =
+ ul_TimingInterval
+ -
+ 1;
+ ul_TimerValue
+ =
+ ul_TimerValue
+ -
+ 2;
+
+ break;
+
+ /******/
+ /* ms */
+ /******/
+
+ case 2:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_TimerValue
+ =
+ ul_TimingInterval
+ *
+ (250.0
+ *
+ b_PCIInputClock);
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_TimingInterval * (250.0 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
+ ul_TimerValue
+ =
+ ul_TimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ *pul_RealTimingInterval
+ =
+ (ULONG)
+ (ul_TimerValue
+ /
+ (250.0 * (double)b_PCIInputClock));
+ d_RealTimingInterval
+ =
+ (double)
+ ul_TimerValue
+ /
+ (250.0
+ *
+ (double)
+ b_PCIInputClock);
+
+ if ((double)((double)ul_TimerValue / (250.0 * (double)b_PCIInputClock)) >= (double)((double)*pul_RealTimingInterval + 0.5)) {
+ *pul_RealTimingInterval
+ =
+ *pul_RealTimingInterval
+ +
+ 1;
+ }
+
+ ul_TimingInterval
+ =
+ ul_TimingInterval
+ -
+ 1;
+ ul_TimerValue
+ =
+ ul_TimerValue
+ -
+ 2;
+
+ break;
+ }
+
+ fpu_end();
+ /*************************/
+ /* Write the timer value */
+ /*************************/
+
+ outl(ul_TimerValue, devpriv->s_BoardInfos.ui_Address + 32 + (64 * b_ModulNbr));
+
+ /*******************************/
+ /* Set the initialisation flag */
+ /*******************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.
+ b_FrequencyMeasurementInit
+ = 1;
+ } else {
+ /***************************/
+ /* Counter not initialised */
+ /***************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue =
+ -3;
+ }
+ } // if (i_ReturnValue == 0)
+ } else {
+ /**********************************/
+ /* Base timing selection is wrong */
+ /**********************************/
+
+ DPRINTK("Base timing selection is wrong\n");
+ i_ReturnValue = -6;
+ }
+ } else {
+ /***********************************/
+ /* Timing unity selection is wrong */
+ /***********************************/
+
+ DPRINTK("Timing unity selection is wrong\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /*****************************************/
+ /* The selected PCI input clock is wrong */
+ /*****************************************/
+
+ DPRINTK("The selected PCI input clock is wrong\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*########################################################################### */
+
+ //INSN BITS
+/*########################################################################### */
+
+/*
++----------------------------------------------------------------------------+
+| Function Name :INT i_APCI1710_InsnBitsINCCPT(struct comedi_device *dev,struct comedi_subdevice *s,
+struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Set & Clear Functions for INC_CPT |
++----------------------------------------------------------------------------+
+| Input Parameters :
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value :
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnBitsINCCPT(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_BitsType;
+ INT i_ReturnValue = 0;
+ ui_BitsType = CR_CHAN(insn->chanspec);
+ devpriv->tsk_Current = current; // Save the current process task structure
+
+ switch (ui_BitsType) {
+ case APCI1710_INCCPT_CLEARCOUNTERVALUE:
+ i_ReturnValue = i_APCI1710_ClearCounterValue(dev,
+ (BYTE) CR_AREF(insn->chanspec));
+ break;
+
+ case APCI1710_INCCPT_CLEARALLCOUNTERVALUE:
+ i_ReturnValue = i_APCI1710_ClearAllCounterValue(dev);
+ break;
+
+ case APCI1710_INCCPT_SETINPUTFILTER:
+ i_ReturnValue = i_APCI1710_SetInputFilter(dev,
+ (BYTE) CR_AREF(insn->chanspec),
+ (BYTE) data[0], (BYTE) data[1]);
+ break;
+
+ case APCI1710_INCCPT_LATCHCOUNTER:
+ i_ReturnValue = i_APCI1710_LatchCounter(dev,
+ (BYTE) CR_AREF(insn->chanspec), (BYTE) data[0]);
+ break;
+
+ case APCI1710_INCCPT_SETINDEXANDREFERENCESOURCE:
+ i_ReturnValue = i_APCI1710_SetIndexAndReferenceSource(dev,
+ (BYTE) CR_AREF(insn->chanspec), (BYTE) data[0]);
+ break;
+
+ case APCI1710_INCCPT_SETDIGITALCHLON:
+ i_ReturnValue = i_APCI1710_SetDigitalChlOn(dev,
+ (BYTE) CR_AREF(insn->chanspec));
+ break;
+
+ case APCI1710_INCCPT_SETDIGITALCHLOFF:
+ i_ReturnValue = i_APCI1710_SetDigitalChlOff(dev,
+ (BYTE) CR_AREF(insn->chanspec));
+ break;
+
+ default:
+ printk("Bits Config Parameter Wrong\n");
+ }
+
+ if (i_ReturnValue >= 0)
+ i_ReturnValue = insn->n;
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_ClearCounterValue |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr) |
++----------------------------------------------------------------------------+
+| Task : Clear the counter value from selected module |
+| (b_ModulNbr). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The selected module number parameter is wrong |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_ClearCounterValue(struct comedi_device * dev, BYTE b_ModulNbr)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /*********************/
+ /* Clear the counter */
+ /*********************/
+
+ outl(1, devpriv->s_BoardInfos.
+ ui_Address + 16 + (64 * b_ModulNbr));
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_ClearAllCounterValue |
+| (BYTE_ b_BoardHandle) |
++----------------------------------------------------------------------------+
+| Task : Clear all counter value. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_ClearAllCounterValue(struct comedi_device * dev)
+{
+ BYTE b_ModulCpt = 0;
+ INT i_ReturnValue = 0;
+
+ /********************************/
+ /* Test if counter module found */
+ /********************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[0] & 0xFFFF0000UL) ==
+ APCI1710_INCREMENTAL_COUNTER
+ || (devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[1] & 0xFFFF0000UL) ==
+ APCI1710_INCREMENTAL_COUNTER
+ || (devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[2] & 0xFFFF0000UL) ==
+ APCI1710_INCREMENTAL_COUNTER
+ || (devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[3] & 0xFFFF0000UL) ==
+ APCI1710_INCREMENTAL_COUNTER) {
+ for (b_ModulCpt = 0; b_ModulCpt < 4; b_ModulCpt++) {
+ /*******************************/
+ /* Test if incremental counter */
+ /*******************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulCpt] &
+ 0xFFFF0000UL) ==
+ APCI1710_INCREMENTAL_COUNTER) {
+ /*********************/
+ /* Clear the counter */
+ /*********************/
+
+ outl(1, devpriv->s_BoardInfos.
+ ui_Address + 16 + (64 * b_ModulCpt));
+ }
+ }
+ } else {
+ /***************************/
+ /* No counter module found */
+ /***************************/
+
+ DPRINTK("No counter module found\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_SetInputFilter |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_Module, |
+| BYTE_ b_PCIInputClock, |
+| BYTE_ b_Filter) |
++----------------------------------------------------------------------------+
+| Task : Disable or enable the software filter from selected |
+| module (b_ModulNbr). b_Filter determine the filter time|
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Number of the module to be |
+| configured (0 to 3) |
+| BYTE_ b_PCIInputClock : Selection of the PCI bus |
+| clock |
+| - APCI1710_30MHZ : |
+| The PC has a PCI bus clock |
+| of 30 MHz |
+| - APCI1710_33MHZ : |
+| The PC has a PCI bus clock |
+| of 33 MHz |
+| - APCI1710_40MHZ : |
+| The APCI1710 has a 40MHz |
+| quartz |
+| BYTE_ b_Filter : Filter selection |
+| |
+| 30 MHz |
+| ------ |
+| 0: Software filter not used |
+| 1: Filter from 266ns (3.750000MHz) |
+| 2: Filter from 400ns (2.500000MHz) |
+| 3: Filter from 533ns (1.876170MHz) |
+| 4: Filter from 666ns (1.501501MHz) |
+| 5: Filter from 800ns (1.250000MHz) |
+| 6: Filter from 933ns (1.071800MHz) |
+| 7: Filter from 1066ns (0.938080MHz) |
+| 8: Filter from 1200ns (0.833333MHz) |
+| 9: Filter from 1333ns (0.750000MHz) |
+| 10: Filter from 1466ns (0.682100MHz) |
+| 11: Filter from 1600ns (0.625000MHz) |
+| 12: Filter from 1733ns (0.577777MHz) |
+| 13: Filter from 1866ns (0.535900MHz) |
+| 14: Filter from 2000ns (0.500000MHz) |
+| 15: Filter from 2133ns (0.468800MHz) |
+| |
+| 33 MHz |
+| ------ |
+| 0: Software filter not used |
+| 1: Filter from 242ns (4.125000MHz) |
+| 2: Filter from 363ns (2.754820MHz) |
+| 3: Filter from 484ns (2.066115MHz) |
+| 4: Filter from 605ns (1.652892MHz) |
+| 5: Filter from 726ns (1.357741MHz) |
+| 6: Filter from 847ns (1.180637MHz) |
+| 7: Filter from 968ns (1.033055MHz) |
+| 8: Filter from 1089ns (0.918273MHz) |
+| 9: Filter from 1210ns (0.826446MHz) |
+| 10: Filter from 1331ns (0.751314MHz) |
+| 11: Filter from 1452ns (0.688705MHz) |
+| 12: Filter from 1573ns (0.635727MHz) |
+| 13: Filter from 1694ns (0.590318MHz) |
+| 14: Filter from 1815ns (0.550964MHz) |
+| 15: Filter from 1936ns (0.516528MHz) |
+| |
+| 40 MHz |
+| ------ |
+| 0: Software filter not used |
+| 1: Filter from 200ns (5.000000MHz) |
+| 2: Filter from 300ns (3.333333MHz) |
+| 3: Filter from 400ns (2.500000MHz) |
+| 4: Filter from 500ns (2.000000MHz) |
+| 5: Filter from 600ns (1.666666MHz) |
+| 6: Filter from 700ns (1.428500MHz) |
+| 7: Filter from 800ns (1.250000MHz) |
+| 8: Filter from 900ns (1.111111MHz) |
+| 9: Filter from 1000ns (1.000000MHz) |
+| 10: Filter from 1100ns (0.909090MHz) |
+| 11: Filter from 1200ns (0.833333MHz) |
+| 12: Filter from 1300ns (0.769200MHz) |
+| 13: Filter from 1400ns (0.714200MHz) |
+| 14: Filter from 1500ns (0.666666MHz) |
+| 15: Filter from 1600ns (0.625000MHz) |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The selected module number is wrong |
+| -3: The module is not a counter module |
+| -4: The selected PCI input clock is wrong |
+| -5: The selected filter value is wrong |
+| -6: 40MHz quartz not on board |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_SetInputFilter(struct comedi_device * dev,
+ BYTE b_ModulNbr, BYTE b_PCIInputClock, BYTE b_Filter)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_Status = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if incremental counter */
+ /*******************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_INCREMENTAL_COUNTER) {
+ /******************************/
+ /* Test if firmware >= Rev1.5 */
+ /******************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF) >= 0x3135) {
+ /**************************/
+ /* Test the PCI bus clock */
+ /**************************/
+
+ if ((b_PCIInputClock == APCI1710_30MHZ) ||
+ (b_PCIInputClock == APCI1710_33MHZ) ||
+ (b_PCIInputClock == APCI1710_40MHZ)) {
+ /*************************/
+ /* Test the filter value */
+ /*************************/
+
+ if (b_Filter < 16) {
+ /**********************/
+ /* Test if 40MHz used */
+ /**********************/
+
+ if (b_PCIInputClock ==
+ APCI1710_40MHZ) {
+ /*********************************/
+ /* Test if 40MHz quartz on board */
+ /*********************************/
+
+ dw_Status =
+ inl(devpriv->
+ s_BoardInfos.
+ ui_Address +
+ 36 +
+ (64 * b_ModulNbr));
+
+ /******************************/
+ /* Test the quartz flag (DQ0) */
+ /******************************/
+
+ if ((dw_Status & 1) !=
+ 1) {
+ /*****************************/
+ /* 40MHz quartz not on board */
+ /*****************************/
+
+ DPRINTK("40MHz quartz not on board\n");
+ i_ReturnValue =
+ -6;
+ }
+ } // if (b_PCIInputClock == APCI1710_40MHZ)
+
+ /***************************/
+ /* Test if error not occur */
+ /***************************/
+
+ if (i_ReturnValue == 0) {
+ /**********************/
+ /* Test if 40MHz used */
+ /**********************/
+
+ if (b_PCIInputClock ==
+ APCI1710_40MHZ)
+ {
+ /*********************************/
+ /* Enable the 40MHz quarz (DQ31) */
+ /*********************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ |
+ APCI1710_ENABLE_40MHZ_FILTER;
+
+ } // if (b_PCIInputClock == APCI1710_40MHZ)
+ else {
+ /**********************************/
+ /* Disable the 40MHz quarz (DQ31) */
+ /**********************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ &
+ APCI1710_DISABLE_40MHZ_FILTER;
+
+ } // if (b_PCIInputClock == APCI1710_40MHZ)
+
+ /************************/
+ /* Set the filter value */
+ /************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3
+ =
+ (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3
+ & 0x1F) |
+ ((b_Filter &
+ 0x7) <<
+ 5);
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ =
+ (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4
+ & 0xFE) |
+ ((b_Filter &
+ 0x8) >>
+ 3);
+
+ /***************************/
+ /* Write the configuration */
+ /***************************/
+
+ outl(devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ dw_ModeRegister1_2_3_4,
+ devpriv->
+ s_BoardInfos.
+ ui_Address +
+ 20 +
+ (64 * b_ModulNbr));
+ } // if (i_ReturnValue == 0)
+ } // if (b_Filter < 16)
+ else {
+ /**************************************/
+ /* The selected filter value is wrong */
+ /**************************************/
+
+ DPRINTK("The selected filter value is wrong\n");
+ i_ReturnValue = -5;
+ } // if (b_Filter < 16)
+ } // if ((b_PCIInputClock == APCI1710_30MHZ) || (b_PCIInputClock == APCI1710_33MHZ) || (b_PCIInputClock == APCI1710_40MHZ))
+ else {
+ /*****************************************/
+ /* The selected PCI input clock is wrong */
+ /*****************************************/
+
+ DPRINTK("The selected PCI input clock is wrong\n");
+ i_ReturnValue = 4;
+ } // if ((b_PCIInputClock == APCI1710_30MHZ) || (b_PCIInputClock == APCI1710_33MHZ) || (b_PCIInputClock == APCI1710_40MHZ))
+ } else {
+ /**************************************/
+ /* The module is not a counter module */
+ /**************************************/
+
+ DPRINTK("The module is not a counter module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /**************************************/
+ /* The module is not a counter module */
+ /**************************************/
+
+ DPRINTK("The module is not a counter module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_LatchCounter (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_LatchReg) |
++----------------------------------------------------------------------------+
+| Task : Latch the courant value from selected module |
+| (b_ModulNbr) in to the selected latch register |
+| (b_LatchReg). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
+| BYTE_ b_LatchReg : Selected latch register |
+| 0 : for the first latch register |
+| 1 : for the second latch register |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: The selected latch register parameter is wrong |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_LatchCounter(struct comedi_device * dev,
+ BYTE b_ModulNbr, BYTE b_LatchReg)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /*************************************/
+ /* Test the latch register parameter */
+ /*************************************/
+
+ if (b_LatchReg < 2) {
+ /*********************/
+ /* Tatch the counter */
+ /*********************/
+
+ outl(1 << (b_LatchReg * 4),
+ devpriv->s_BoardInfos.ui_Address +
+ (64 * b_ModulNbr));
+ } else {
+ /**************************************************/
+ /* The selected latch register parameter is wrong */
+ /**************************************************/
+
+ DPRINTK("The selected latch register parameter is wrong\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_SetIndexAndReferenceSource |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_SourceSelection) |
++----------------------------------------------------------------------------+
+| Task : Determine the hardware source for the index and the |
+| reference logic. Per default the index logic is |
+| connected to the difference input C and the reference |
+| logic is connected to the 24V input E |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
+| BYTE_ b_SourceSelection : APCI1710_SOURCE_0 : |
+| The index logic is connected |
+| to the difference input C and|
+| the reference logic is |
+| connected to the 24V input E.|
+| This is the default |
+| configuration. |
+| APCI1710_SOURCE_1 : |
+| The reference logic is |
+| connected to the difference |
+| input C and the index logic |
+| is connected to the 24V |
+| input E |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The selected module number is wrong |
+| -3: The module is not a counter module. |
+| -4: The source selection is wrong |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_SetIndexAndReferenceSource(struct comedi_device * dev,
+ BYTE b_ModulNbr, BYTE b_SourceSelection)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if incremental counter */
+ /*******************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_INCREMENTAL_COUNTER) {
+ /******************************/
+ /* Test if firmware >= Rev1.5 */
+ /******************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF) >= 0x3135) {
+ /*****************************/
+ /* Test the source selection */
+ /*****************************/
+
+ if (b_SourceSelection == APCI1710_SOURCE_0 ||
+ b_SourceSelection == APCI1710_SOURCE_1)
+ {
+ /******************************************/
+ /* Test if invert the index and reference */
+ /******************************************/
+
+ if (b_SourceSelection ==
+ APCI1710_SOURCE_1) {
+ /********************************************/
+ /* Invert index and reference source (DQ25) */
+ /********************************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4 =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4 |
+ APCI1710_INVERT_INDEX_RFERENCE;
+ } else {
+ /****************************************/
+ /* Set the default configuration (DQ25) */
+ /****************************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4 =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister4 &
+ APCI1710_DEFAULT_INDEX_RFERENCE;
+ }
+ } // if (b_SourceSelection == APCI1710_SOURCE_0 ||b_SourceSelection == APCI1710_SOURCE_1)
+ else {
+ /*********************************/
+ /* The source selection is wrong */
+ /*********************************/
+
+ DPRINTK("The source selection is wrong\n");
+ i_ReturnValue = -4;
+ } // if (b_SourceSelection == APCI1710_SOURCE_0 ||b_SourceSelection == APCI1710_SOURCE_1)
+ } else {
+ /**************************************/
+ /* The module is not a counter module */
+ /**************************************/
+
+ DPRINTK("The module is not a counter module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /**************************************/
+ /* The module is not a counter module */
+ /**************************************/
+
+ DPRINTK("The module is not a counter module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***************************************/
+ /* The selected module number is wrong */
+ /***************************************/
+
+ DPRINTK("The selected module number is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_SetDigitalChlOn |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr) |
++----------------------------------------------------------------------------+
+| Task : Sets the digital output H Setting an output means |
+| setting an ouput high. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Number of the module to be |
+| configured (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The selected module number is wrong |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_SetDigitalChlOn(struct comedi_device * dev, BYTE b_ModulNbr)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3 = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.b_ModeRegister3 | 0x10;
+
+ /*********************/
+ /* Set the output On */
+ /*********************/
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ dw_ModeRegister1_2_3_4, devpriv->s_BoardInfos.
+ ui_Address + 20 + (64 * b_ModulNbr));
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_SetDigitalChlOff |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr) |
++----------------------------------------------------------------------------+
+| Task : Resets the digital output H. Resetting an output means |
+| setting an ouput low. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Number of the module to be |
+| configured (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The selected module number is wrong |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_SetDigitalChlOff(struct comedi_device * dev, BYTE b_ModulNbr)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3 = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.b_ModeRegister3 & 0xEF;
+
+ /**********************/
+ /* Set the output Off */
+ /**********************/
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ dw_ModeRegister1_2_3_4, devpriv->s_BoardInfos.
+ ui_Address + 20 + (64 * b_ModulNbr));
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*########################################################################### */
+
+ // INSN WRITE
+/*########################################################################### */
+
+/*
++----------------------------------------------------------------------------+
+| Function Name :INT i_APCI1710_InsnWriteINCCPT(struct comedi_device *dev,struct comedi_subdevice *s,
+struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Enable Disable functions for INC_CPT |
++----------------------------------------------------------------------------+
+| Input Parameters :
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value :
++----------------------------------------------------------------------------+
+*/
+INT i_APCI1710_InsnWriteINCCPT(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_WriteType;
+ INT i_ReturnValue = 0;
+
+ ui_WriteType = CR_CHAN(insn->chanspec);
+ devpriv->tsk_Current = current; // Save the current process task structure
+
+ switch (ui_WriteType) {
+ case APCI1710_INCCPT_ENABLELATCHINTERRUPT:
+ i_ReturnValue = i_APCI1710_EnableLatchInterrupt(dev,
+ (BYTE) CR_AREF(insn->chanspec));
+ break;
+
+ case APCI1710_INCCPT_DISABLELATCHINTERRUPT:
+ i_ReturnValue = i_APCI1710_DisableLatchInterrupt(dev,
+ (BYTE) CR_AREF(insn->chanspec));
+ break;
+
+ case APCI1710_INCCPT_WRITE16BITCOUNTERVALUE:
+ i_ReturnValue = i_APCI1710_Write16BitCounterValue(dev,
+ (BYTE) CR_AREF(insn->chanspec),
+ (BYTE) data[0], (UINT) data[1]);
+ break;
+
+ case APCI1710_INCCPT_WRITE32BITCOUNTERVALUE:
+ i_ReturnValue = i_APCI1710_Write32BitCounterValue(dev,
+ (BYTE) CR_AREF(insn->chanspec), (ULONG) data[0]);
+
+ break;
+
+ case APCI1710_INCCPT_ENABLEINDEX:
+ i_APCI1710_EnableIndex(dev, (BYTE) CR_AREF(insn->chanspec));
+ break;
+
+ case APCI1710_INCCPT_DISABLEINDEX:
+ i_ReturnValue = i_APCI1710_DisableIndex(dev,
+ (BYTE) CR_AREF(insn->chanspec));
+ break;
+
+ case APCI1710_INCCPT_ENABLECOMPARELOGIC:
+ i_ReturnValue = i_APCI1710_EnableCompareLogic(dev,
+ (BYTE) CR_AREF(insn->chanspec));
+ break;
+
+ case APCI1710_INCCPT_DISABLECOMPARELOGIC:
+ i_ReturnValue = i_APCI1710_DisableCompareLogic(dev,
+ (BYTE) CR_AREF(insn->chanspec));
+ break;
+
+ case APCI1710_INCCPT_ENABLEFREQUENCYMEASUREMENT:
+ i_ReturnValue = i_APCI1710_EnableFrequencyMeasurement(dev,
+ (BYTE) CR_AREF(insn->chanspec), (BYTE) data[0]);
+ break;
+
+ case APCI1710_INCCPT_DISABLEFREQUENCYMEASUREMENT:
+ i_ReturnValue = i_APCI1710_DisableFrequencyMeasurement(dev,
+ (BYTE) CR_AREF(insn->chanspec));
+ break;
+
+ default:
+ printk("Write Config Parameter Wrong\n");
+ }
+
+ if (i_ReturnValue >= 0)
+ i_ReturnValue = insn->n;
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_EnableLatchInterrupt |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr) |
++----------------------------------------------------------------------------+
+| Task : Enable the latch interrupt from selected module |
+| (b_ModulNbr). Each software or hardware latch occur a |
+| interrupt. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: Interrupt routine not installed see function |
+| "i_APCI1710_SetBoardIntRoutine" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_EnableLatchInterrupt(struct comedi_device * dev, BYTE b_ModulNbr)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+
+ /********************/
+ /* Enable interrupt */
+ /********************/
+
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2 = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2 | APCI1710_ENABLE_LATCH_INT;
+
+ /***************************/
+ /* Write the configuration */
+ /***************************/
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ dw_ModeRegister1_2_3_4, devpriv->s_BoardInfos.
+ ui_Address + 20 + (64 * b_ModulNbr));
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_DisableLatchInterrupt |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr) |
++----------------------------------------------------------------------------+
+| Task : Disable the latch interrupt from selected module |
+| (b_ModulNbr). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: Interrupt routine not installed see function |
+| "i_APCI1710_SetBoardIntRoutine" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_DisableLatchInterrupt(struct comedi_device * dev, BYTE b_ModulNbr)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+
+ /***************************/
+ /* Write the configuration */
+ /***************************/
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ dw_ModeRegister1_2_3_4 &
+ ((APCI1710_DISABLE_LATCH_INT << 8) | 0xFF),
+ devpriv->s_BoardInfos.ui_Address + 20 +
+ (64 * b_ModulNbr));
+
+ mdelay(1000);
+
+ /*********************/
+ /* Disable interrupt */
+ /*********************/
+
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2 = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2 & APCI1710_DISABLE_LATCH_INT;
+
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_Write16BitCounterValue |
+| (BYTE_ b_BoardHandle |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_SelectedCounter, |
+| UINT_ ui_WriteValue) |
++----------------------------------------------------------------------------+
+| Task : Write a 16-Bit value (ui_WriteValue) in to the selected|
+| 16-Bit counter (b_SelectedCounter) from selected module|
+| (b_ModulNbr). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
+| BYTE_ b_SelectedCounter : Selected 16-Bit counter |
+| (0 or 1) |
+| UINT_ ui_WriteValue : 16-Bit write value |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: The selected 16-Bit counter parameter is wrong |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_Write16BitCounterValue(struct comedi_device * dev,
+ BYTE b_ModulNbr, BYTE b_SelectedCounter, UINT ui_WriteValue)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /******************************/
+ /* Test the counter selection */
+ /******************************/
+
+ if (b_SelectedCounter < 2) {
+ /*******************/
+ /* Write the value */
+ /*******************/
+
+ outl((ULONG) ((ULONG) (ui_WriteValue) << (16 *
+ b_SelectedCounter)),
+ devpriv->s_BoardInfos.ui_Address + 8 +
+ (b_SelectedCounter * 4) +
+ (64 * b_ModulNbr));
+ } else {
+ /**************************************************/
+ /* The selected 16-Bit counter parameter is wrong */
+ /**************************************************/
+
+ DPRINTK("The selected 16-Bit counter parameter is wrong\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_Write32BitCounterValue |
+| (BYTE_ b_BoardHandle |
+| BYTE_ b_ModulNbr, |
+| ULONG_ ul_WriteValue) |
++----------------------------------------------------------------------------+
+| Task : Write a 32-Bit value (ui_WriteValue) in to the selected|
+| module (b_ModulNbr). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
+| ULONG_ ul_WriteValue : 32-Bit write value |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_Write32BitCounterValue(struct comedi_device * dev,
+ BYTE b_ModulNbr, ULONG ul_WriteValue)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /*******************/
+ /* Write the value */
+ /*******************/
+
+ outl(ul_WriteValue, devpriv->s_BoardInfos.
+ ui_Address + 4 + (64 * b_ModulNbr));
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_EnableIndex (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr) |
++----------------------------------------------------------------------------+
+| Task : Enable the INDEX actions |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: Index not initialised see function |
+| "i_APCI1710_InitIndex" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_EnableIndex(struct comedi_device * dev, BYTE b_ModulNbr)
+{
+ INT i_ReturnValue = 0;
+ ULONG ul_InterruptLatchReg;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /*****************************/
+ /* Test if index initialised */
+ /*****************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_IndexInit) {
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2 = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2 | APCI1710_ENABLE_INDEX;
+
+ ul_InterruptLatchReg =
+ inl(devpriv->s_BoardInfos.ui_Address +
+ 24 + (64 * b_ModulNbr));
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ dw_ModeRegister1_2_3_4,
+ devpriv->s_BoardInfos.ui_Address + 20 +
+ (64 * b_ModulNbr));
+ } else {
+ /*************************************************************/
+ /* Index not initialised see function "i_APCI1710_InitIndex" */
+ /*************************************************************/
+
+ DPRINTK("Index not initialised \n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_DisableIndex (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr) |
++----------------------------------------------------------------------------+
+| Task : Disable the INDEX actions |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: Index not initialised see function |
+| "i_APCI1710_InitIndex" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_DisableIndex(struct comedi_device * dev, BYTE b_ModulNbr)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /*****************************/
+ /* Test if index initialised */
+ /*****************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_IndexInit) {
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2 = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2 &
+ APCI1710_DISABLE_INDEX;
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ dw_ModeRegister1_2_3_4,
+ devpriv->s_BoardInfos.ui_Address + 20 +
+ (64 * b_ModulNbr));
+ } else {
+ /*************************************************************/
+ /* Index not initialised see function "i_APCI1710_InitIndex" */
+ /*************************************************************/
+
+ DPRINTK("Index not initialised \n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_EnableCompareLogic |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr) |
++----------------------------------------------------------------------------+
+| Task : Enable the 32-Bit compare logic. At that moment that |
+| the incremental counter arrive to the compare value a |
+| interrupt is generated. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : -
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: Compare logic not initialised. |
+| See function "i_APCI1710_InitCompareLogic" |
+| -5: Interrupt function not initialised. |
+| See function "i_APCI1710_SetBoardIntRoutineX" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_EnableCompareLogic(struct comedi_device * dev, BYTE b_ModulNbr)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /*************************************/
+ /* Test if compare logic initialised */
+ /*************************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.b_CompareLogicInit == 1) {
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3 = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3 |
+ APCI1710_ENABLE_COMPARE_INT;
+
+ /***************************/
+ /* Write the configuration */
+ /***************************/
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ dw_ModeRegister1_2_3_4,
+ devpriv->s_BoardInfos.ui_Address + 20 +
+ (64 * b_ModulNbr));
+ } else {
+ /*********************************/
+ /* Compare logic not initialised */
+ /*********************************/
+
+ DPRINTK("Compare logic not initialised\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_DisableCompareLogic |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr) |
++----------------------------------------------------------------------------+
+| Task : Disable the 32-Bit compare logic.
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : -
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: Compare logic not initialised. |
+| See function "i_APCI1710_InitCompareLogic" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_DisableCompareLogic(struct comedi_device * dev, BYTE b_ModulNbr)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /*************************************/
+ /* Test if compare logic initialised */
+ /*************************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.b_CompareLogicInit == 1) {
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3 = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3 &
+ APCI1710_DISABLE_COMPARE_INT;
+
+ /***************************/
+ /* Write the configuration */
+ /***************************/
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ dw_ModeRegister1_2_3_4,
+ devpriv->s_BoardInfos.ui_Address + 20 +
+ (64 * b_ModulNbr));
+ } else {
+ /*********************************/
+ /* Compare logic not initialised */
+ /*********************************/
+
+ DPRINTK("Compare logic not initialised\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+ /*
+ +----------------------------------------------------------------------------+
+ | Function Name : _INT_ i_APCI1710_EnableFrequencyMeasurement |
+ | (BYTE_ b_BoardHandle, |
+ | BYTE_ b_ModulNbr, |
+ | BYTE_ b_InterruptEnable) |
+ +----------------------------------------------------------------------------+
+ | Task : Enables the frequency measurement function |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+ | BYTE_ b_ModulNbr : Number of the module to be |
+ | configured (0 to 3) |
+ | BYTE_ b_InterruptEnable: Enable or disable the |
+ | interrupt. |
+ | APCI1710_ENABLE: |
+ | Enable the interrupt |
+ | APCI1710_DISABLE: |
+ | Disable the interrupt |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : - |
+ +----------------------------------------------------------------------------+
+ | Return Value : 0: No error |
+ | -1: The handle parameter of the board is wrong |
+ | -2: The selected module number is wrong |
+ | -3: Counter not initialised see function |
+ | "i_APCI1710_InitCounter" |
+ | -4: Frequency measurement logic not initialised. |
+ | See function "i_APCI1710_InitFrequencyMeasurement" |
+ | -5: Interrupt parameter is wrong |
+ | -6: Interrupt function not initialised. |
+ +----------------------------------------------------------------------------+
+ */
+
+INT i_APCI1710_EnableFrequencyMeasurement(struct comedi_device * dev,
+ BYTE b_ModulNbr, BYTE b_InterruptEnable)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /********************************************/
+ /* Test if frequency mesurement initialised */
+ /********************************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.b_FrequencyMeasurementInit == 1) {
+ /***************************/
+ /* Test the interrupt mode */
+ /***************************/
+
+ if ((b_InterruptEnable == APCI1710_DISABLE) ||
+ (b_InterruptEnable == APCI1710_ENABLE))
+ {
+
+ /************************************/
+ /* Enable the frequency measurement */
+ /************************************/
+
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3 = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3 |
+ APCI1710_ENABLE_FREQUENCY;
+
+ /*********************************************/
+ /* Disable or enable the frequency interrupt */
+ /*********************************************/
+
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3 = (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3 &
+ APCI1710_DISABLE_FREQUENCY_INT)
+ | (b_InterruptEnable << 3);
+
+ /***************************/
+ /* Write the configuration */
+ /***************************/
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ dw_ModeRegister1_2_3_4,
+ devpriv->s_BoardInfos.
+ ui_Address + 20 +
+ (64 * b_ModulNbr));
+
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.
+ b_FrequencyMeasurementEnable =
+ 1;
+ } else {
+ /********************************/
+ /* Interrupt parameter is wrong */
+ /********************************/
+
+ DPRINTK("Interrupt parameter is wrong\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /***********************************************/
+ /* Frequency measurement logic not initialised */
+ /***********************************************/
+
+ DPRINTK("Frequency measurement logic not initialised\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+ /*
+ +----------------------------------------------------------------------------+
+ | Function Name : _INT_ i_APCI1710_DisableFrequencyMeasurement |
+ | (BYTE_ b_BoardHandle, |
+ | BYTE_ b_ModulNbr) |
+ +----------------------------------------------------------------------------+
+ | Task : Disables the frequency measurement function |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+ | BYTE_ b_ModulNbr : Number of the module to be |
+ | configured (0 to 3) |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : - |
+ +----------------------------------------------------------------------------+
+ | Return Value : 0: No error |
+ | -1: The handle parameter of the board is wrong |
+ | -2: The selected module number is wrong |
+ | -3: Counter not initialised see function |
+ | "i_APCI1710_InitCounter" |
+ | -4: Frequency measurement logic not initialised. |
+ | See function "i_APCI1710_InitFrequencyMeasurement" |
+ +----------------------------------------------------------------------------+
+ */
+
+INT i_APCI1710_DisableFrequencyMeasurement(struct comedi_device * dev, BYTE b_ModulNbr)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /********************************************/
+ /* Test if frequency mesurement initialised */
+ /********************************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.b_FrequencyMeasurementInit == 1) {
+ /*************************************/
+ /* Disable the frequency measurement */
+ /*************************************/
+
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3 = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3 &
+ APCI1710_DISABLE_FREQUENCY
+ // Begin CG 29/06/01 CG 1100/0231 -> 0701/0232 Frequence measure IRQ must be cleared
+ & APCI1710_DISABLE_FREQUENCY_INT;
+ // End CG 29/06/01 CG 1100/0231 -> 0701/0232 Frequence measure IRQ must be cleared
+
+ /***************************/
+ /* Write the configuration */
+ /***************************/
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ dw_ModeRegister1_2_3_4,
+ devpriv->s_BoardInfos.ui_Address + 20 +
+ (64 * b_ModulNbr));
+
+ /*************************************/
+ /* Disable the frequency measurement */
+ /*************************************/
+
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.
+ b_FrequencyMeasurementEnable = 0;
+ } else {
+ /***********************************************/
+ /* Frequency measurement logic not initialised */
+ /***********************************************/
+
+ DPRINTK("Frequency measurement logic not initialised\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*########################################################################### */
+
+ // INSN READ
+
+/*########################################################################### */
+
+/*
++----------------------------------------------------------------------------+
+| Function Name :INT i_APCI1710_InsnWriteINCCPT(struct comedi_device *dev,struct comedi_subdevice *s,
+struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read and Get functions for INC_CPT |
++----------------------------------------------------------------------------+
+| Input Parameters :
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value :
++----------------------------------------------------------------------------+
+*/
+INT i_APCI1710_InsnReadINCCPT(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_ReadType;
+ INT i_ReturnValue = 0;
+
+ ui_ReadType = CR_CHAN(insn->chanspec);
+
+ devpriv->tsk_Current = current; // Save the current process task structure
+ switch (ui_ReadType) {
+ case APCI1710_INCCPT_READLATCHREGISTERSTATUS:
+ i_ReturnValue = i_APCI1710_ReadLatchRegisterStatus(dev,
+ (BYTE) CR_AREF(insn->chanspec),
+ (BYTE) CR_RANGE(insn->chanspec), (PBYTE) & data[0]);
+ break;
+
+ case APCI1710_INCCPT_READLATCHREGISTERVALUE:
+ i_ReturnValue = i_APCI1710_ReadLatchRegisterValue(dev,
+ (BYTE) CR_AREF(insn->chanspec),
+ (BYTE) CR_RANGE(insn->chanspec), (PULONG) & data[0]);
+ printk("Latch Register Value %d\n", data[0]);
+ break;
+
+ case APCI1710_INCCPT_READ16BITCOUNTERVALUE:
+ i_ReturnValue = i_APCI1710_Read16BitCounterValue(dev,
+ (BYTE) CR_AREF(insn->chanspec),
+ (BYTE) CR_RANGE(insn->chanspec), (PUINT) & data[0]);
+ break;
+
+ case APCI1710_INCCPT_READ32BITCOUNTERVALUE:
+ i_ReturnValue = i_APCI1710_Read32BitCounterValue(dev,
+ (BYTE) CR_AREF(insn->chanspec), (PULONG) & data[0]);
+ break;
+
+ case APCI1710_INCCPT_GETINDEXSTATUS:
+ i_ReturnValue = i_APCI1710_GetIndexStatus(dev,
+ (BYTE) CR_AREF(insn->chanspec), (PBYTE) & data[0]);
+ break;
+
+ case APCI1710_INCCPT_GETREFERENCESTATUS:
+ i_ReturnValue = i_APCI1710_GetReferenceStatus(dev,
+ (BYTE) CR_AREF(insn->chanspec), (PBYTE) & data[0]);
+ break;
+
+ case APCI1710_INCCPT_GETUASSTATUS:
+ i_ReturnValue = i_APCI1710_GetUASStatus(dev,
+ (BYTE) CR_AREF(insn->chanspec), (PBYTE) & data[0]);
+ break;
+
+ case APCI1710_INCCPT_GETCBSTATUS:
+ i_ReturnValue = i_APCI1710_GetCBStatus(dev,
+ (BYTE) CR_AREF(insn->chanspec), (PBYTE) & data[0]);
+ break;
+
+ case APCI1710_INCCPT_GET16BITCBSTATUS:
+ i_ReturnValue = i_APCI1710_Get16BitCBStatus(dev,
+ (BYTE) CR_AREF(insn->chanspec),
+ (PBYTE) & data[0], (PBYTE) & data[1]);
+ break;
+
+ case APCI1710_INCCPT_GETUDSTATUS:
+ i_ReturnValue = i_APCI1710_GetUDStatus(dev,
+ (BYTE) CR_AREF(insn->chanspec), (PBYTE) & data[0]);
+
+ break;
+
+ case APCI1710_INCCPT_GETINTERRUPTUDLATCHEDSTATUS:
+ i_ReturnValue = i_APCI1710_GetInterruptUDLatchedStatus(dev,
+ (BYTE) CR_AREF(insn->chanspec), (PBYTE) & data[0]);
+ break;
+
+ case APCI1710_INCCPT_READFREQUENCYMEASUREMENT:
+ i_ReturnValue = i_APCI1710_ReadFrequencyMeasurement(dev,
+ (BYTE) CR_AREF(insn->chanspec),
+ (PBYTE) & data[0],
+ (PBYTE) & data[1], (PULONG) & data[2]);
+ break;
+
+ case APCI1710_INCCPT_READINTERRUPT:
+ data[0] = devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.ui_Read].b_OldModuleMask;
+ data[1] = devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.ui_Read].ul_OldInterruptMask;
+ data[2] = devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.ui_Read].ul_OldCounterLatchValue;
+
+ /**************************/
+ /* Increment the read FIFO */
+ /***************************/
+
+ devpriv->
+ s_InterruptParameters.
+ ui_Read = (devpriv->s_InterruptParameters.
+ ui_Read + 1) % APCI1710_SAVE_INTERRUPT;
+
+ break;
+
+ default:
+ printk("ReadType Parameter wrong\n");
+ }
+
+ if (i_ReturnValue >= 0)
+ i_ReturnValue = insn->n;
+ return (i_ReturnValue);
+
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_ReadLatchRegisterStatus |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_LatchReg, |
+| PBYTE_ pb_LatchStatus) |
++----------------------------------------------------------------------------+
+| Task : Read the latch register status from selected module |
+| (b_ModulNbr) and selected latch register (b_LatchReg). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
+| BYTE_ b_LatchReg : Selected latch register |
+| 0 : for the first latch register |
+| 1 : for the second latch register |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_LatchStatus : Latch register status. |
+| 0 : No latch occur |
+| 1 : A software latch occur |
+| 2 : A hardware latch occur |
+| 3 : A software and hardware |
+| latch occur |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: The selected latch register parameter is wrong |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_ReadLatchRegisterStatus(struct comedi_device * dev,
+ BYTE b_ModulNbr, BYTE b_LatchReg, PBYTE pb_LatchStatus)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_LatchReg;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /*************************************/
+ /* Test the latch register parameter */
+ /*************************************/
+
+ if (b_LatchReg < 2) {
+ dw_LatchReg = inl(devpriv->s_BoardInfos.
+ ui_Address + (64 * b_ModulNbr));
+
+ *pb_LatchStatus =
+ (BYTE) ((dw_LatchReg >> (b_LatchReg *
+ 4)) & 0x3);
+ } else {
+ /**************************************************/
+ /* The selected latch register parameter is wrong */
+ /**************************************************/
+
+ DPRINTK("The selected latch register parameter is wrong\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_ReadLatchRegisterValue |
+| (BYTE_ b_BoardHandle,|
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_LatchReg, |
+| PULONG_ pul_LatchValue) |
++----------------------------------------------------------------------------+
+| Task : Read the latch register value from selected module |
+| (b_ModulNbr) and selected latch register (b_LatchReg). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
+| BYTE_ b_LatchReg : Selected latch register |
+| 0 : for the first latch register |
+| 1 : for the second latch register |
++----------------------------------------------------------------------------+
+| Output Parameters : PULONG_ pul_LatchValue : Latch register value |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: The selected latch register parameter is wrong |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_ReadLatchRegisterValue(struct comedi_device * dev,
+ BYTE b_ModulNbr, BYTE b_LatchReg, PULONG pul_LatchValue)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /*************************************/
+ /* Test the latch register parameter */
+ /*************************************/
+
+ if (b_LatchReg < 2) {
+ *pul_LatchValue = inl(devpriv->s_BoardInfos.
+ ui_Address + ((b_LatchReg + 1) * 4) +
+ (64 * b_ModulNbr));
+
+ } else {
+ /**************************************************/
+ /* The selected latch register parameter is wrong */
+ /**************************************************/
+
+ DPRINTK("The selected latch register parameter is wrong\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_Read16BitCounterValue |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_SelectedCounter, |
+| PUINT_ pui_CounterValue) |
++----------------------------------------------------------------------------+
+| Task : Latch the selected 16-Bit counter (b_SelectedCounter) |
+| from selected module (b_ModulNbr) in to the first |
+| latch register and return the latched value. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
+| BYTE_ b_SelectedCounter : Selected 16-Bit counter |
+| (0 or 1) |
++----------------------------------------------------------------------------+
+| Output Parameters : PUINT_ pui_CounterValue : 16-Bit counter value |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: The selected 16-Bit counter parameter is wrong |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_Read16BitCounterValue(struct comedi_device * dev,
+ BYTE b_ModulNbr, BYTE b_SelectedCounter, PUINT pui_CounterValue)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_LathchValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /******************************/
+ /* Test the counter selection */
+ /******************************/
+
+ if (b_SelectedCounter < 2) {
+ /*********************/
+ /* Latch the counter */
+ /*********************/
+
+ outl(1, devpriv->s_BoardInfos.
+ ui_Address + (64 * b_ModulNbr));
+
+ /************************/
+ /* Read the latch value */
+ /************************/
+
+ dw_LathchValue = inl(devpriv->s_BoardInfos.
+ ui_Address + 4 + (64 * b_ModulNbr));
+
+ *pui_CounterValue =
+ (UINT) ((dw_LathchValue >> (16 *
+ b_SelectedCounter)) &
+ 0xFFFFU);
+ } else {
+ /**************************************************/
+ /* The selected 16-Bit counter parameter is wrong */
+ /**************************************************/
+
+ DPRINTK("The selected 16-Bit counter parameter is wrong\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_Read32BitCounterValue |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| PULONG_ pul_CounterValue) |
++----------------------------------------------------------------------------+
+| Task : Latch the 32-Bit counter from selected module |
+| (b_ModulNbr) in to the first latch register and return |
+| the latched value. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : PULONG_ pul_CounterValue : 32-Bit counter value |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_Read32BitCounterValue(struct comedi_device * dev,
+ BYTE b_ModulNbr, PULONG pul_CounterValue)
+{
+ INT i_ReturnValue = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /*********************/
+ /* Tatch the counter */
+ /*********************/
+
+ outl(1, devpriv->s_BoardInfos.
+ ui_Address + (64 * b_ModulNbr));
+
+ /************************/
+ /* Read the latch value */
+ /************************/
+
+ *pul_CounterValue = inl(devpriv->s_BoardInfos.
+ ui_Address + 4 + (64 * b_ModulNbr));
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_GetIndexStatus (BYTE_ b_BoardHandle,|
+| BYTE_ b_ModulNbr, |
+| PBYTE_ pb_IndexStatus)|
++----------------------------------------------------------------------------+
+| Task : Return the index status |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_IndexStatus : 0 : No INDEX occur |
+| 1 : A INDEX occur |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: Index not initialised see function |
+| "i_APCI1710_InitIndex" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_GetIndexStatus(struct comedi_device * dev,
+ BYTE b_ModulNbr, PBYTE pb_IndexStatus)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_StatusReg = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /*****************************/
+ /* Test if index initialised */
+ /*****************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_IndexInit) {
+ dw_StatusReg = inl(devpriv->s_BoardInfos.
+ ui_Address + 12 + (64 * b_ModulNbr));
+
+ *pb_IndexStatus = (BYTE) (dw_StatusReg & 1);
+ } else {
+ /*************************************************************/
+ /* Index not initialised see function "i_APCI1710_InitIndex" */
+ /*************************************************************/
+
+ DPRINTK("Index not initialised\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_GetReferenceStatus |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| PBYTE_ pb_ReferenceStatus) |
++----------------------------------------------------------------------------+
+| Task : Return the reference status |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_ReferenceStatus : 0 : No REFERENCE occur |
+| 1 : A REFERENCE occur |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: Reference not initialised see function |
+| "i_APCI1710_InitReference" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_GetReferenceStatus(struct comedi_device * dev,
+ BYTE b_ModulNbr, PBYTE pb_ReferenceStatus)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_StatusReg = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /*********************************/
+ /* Test if reference initialised */
+ /*********************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.b_ReferenceInit) {
+ dw_StatusReg = inl(devpriv->s_BoardInfos.
+ ui_Address + 24 + (64 * b_ModulNbr));
+
+ *pb_ReferenceStatus =
+ (BYTE) (~dw_StatusReg & 1);
+ } else {
+ /*********************************************************************/
+ /* Reference not initialised see function "i_APCI1710_InitReference" */
+ /*********************************************************************/
+
+ DPRINTK("Reference not initialised\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_GetUASStatus |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| PBYTE_ pb_UASStatus) |
++----------------------------------------------------------------------------+
+| Task : Return the error signal (UAS) status |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_UASStatus : 0 : UAS is low "0" |
+| 1 : UAS is high "1" |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_GetUASStatus(struct comedi_device * dev,
+ BYTE b_ModulNbr, PBYTE pb_UASStatus)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_StatusReg = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ dw_StatusReg = inl(devpriv->s_BoardInfos.
+ ui_Address + 24 + (64 * b_ModulNbr));
+
+ *pb_UASStatus = (BYTE) ((dw_StatusReg >> 1) & 1);
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_GetCBStatus |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| PBYTE_ pb_CBStatus) |
++----------------------------------------------------------------------------+
+| Task : Return the counter overflow status |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_CBStatus : 0 : Counter no overflow |
+| 1 : Counter overflow |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_GetCBStatus(struct comedi_device * dev,
+ BYTE b_ModulNbr, PBYTE pb_CBStatus)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_StatusReg = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ dw_StatusReg = inl(devpriv->s_BoardInfos.
+ ui_Address + 16 + (64 * b_ModulNbr));
+
+ *pb_CBStatus = (BYTE) (dw_StatusReg & 1);
+
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_Get16BitCBStatus |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| PBYTE_ pb_CBStatusCounter0, |
+| PBYTE_ pb_CBStatusCounter1) |
++----------------------------------------------------------------------------+
+| Task : Returns the counter overflow (counter initialised to |
+| 2*16-bit) status from selected incremental counter |
+| module |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_CBStatusCounter0 : 0 : No overflow occur for |
+| the first 16-bit |
+| counter |
+| 1 : Overflow occur for the|
+| first 16-bit counter |
+| PBYTE_ pb_CBStatusCounter1 : 0 : No overflow occur for |
+| the second 16-bit |
+| counter |
+| 1 : Overflow occur for the|
+| second 16-bit counter |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: Counter not initialised to 2*16-bit mode. |
+| See function "i_APCI1710_InitCounter" |
+| -5: Firmware revision error |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_Get16BitCBStatus(struct comedi_device * dev,
+ BYTE b_ModulNbr, PBYTE pb_CBStatusCounter0, PBYTE pb_CBStatusCounter1)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_StatusReg = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /*************************/
+ /* Test if 2*16-Bit mode */
+ /*************************/
+
+ if ((devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister1 & 0x10) == 0x10) {
+ /*****************************/
+ /* Test the Firmware version */
+ /*****************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration
+ [b_ModulNbr] & 0xFFFF) >=
+ 0x3136) {
+ dw_StatusReg =
+ inl(devpriv->s_BoardInfos.
+ ui_Address + 16 +
+ (64 * b_ModulNbr));
+
+ *pb_CBStatusCounter1 =
+ (BYTE) ((dw_StatusReg >> 0) &
+ 1);
+ *pb_CBStatusCounter0 =
+ (BYTE) ((dw_StatusReg >> 1) &
+ 1);
+ } // if ((ps_APCI1710Variable->s_Board [b_BoardHandle].s_BoardInfos.dw_MolduleConfiguration [b_ModulNbr] & 0xFFFF) >= 0x3136)
+ else {
+ /****************************/
+ /* Firmware revision error */
+ /****************************/
+
+ i_ReturnValue = -5;
+ } // if ((ps_APCI1710Variable->s_Board [b_BoardHandle].s_BoardInfos.dw_MolduleConfiguration [b_ModulNbr] & 0xFFFF) >= 0x3136)
+ } // if ((ps_APCI1710Variable->s_Board [b_BoardHandle].s_ModuleInfo [b_ModulNbr].s_SiemensCounterInfo.s_ModeRegister.s_ByteModeRegister.b_ModeRegister1 & 0x10) == 0x10)
+ else {
+ /********************************************/
+ /* Counter not initialised to 2*16-bit mode */
+ /* "i_APCI1710_InitCounter" */
+ /********************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -4;
+ } // if ((ps_APCI1710Variable->s_Board [b_BoardHandle].s_ModuleInfo [b_ModulNbr].s_SiemensCounterInfo.s_ModeRegister.s_ByteModeRegister.b_ModeRegister1 & 0x10) == 0x10)
+ } // if (ps_APCI1710Variable->s_Board [b_BoardHandle].s_ModuleInfo [b_ModulNbr].s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1)
+ else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ } // if (ps_APCI1710Variable->s_Board [b_BoardHandle].s_ModuleInfo [b_ModulNbr].s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1)
+ } // if (b_ModulNbr < 4)
+ else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ } // if (b_ModulNbr < 4)
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_GetUDStatus |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| PBYTE_ pb_UDStatus) |
++----------------------------------------------------------------------------+
+| Task : Return the counter progress status |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_UDStatus : 0 : Counter progress in the |
+| selected mode down |
+| 1 : Counter progress in the |
+| selected mode up |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_GetUDStatus(struct comedi_device * dev,
+ BYTE b_ModulNbr, PBYTE pb_UDStatus)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_StatusReg = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ dw_StatusReg = inl(devpriv->s_BoardInfos.
+ ui_Address + 24 + (64 * b_ModulNbr));
+
+ *pb_UDStatus = (BYTE) ((dw_StatusReg >> 2) & 1);
+
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_GetInterruptUDLatchedStatus |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| PBYTE_ pb_UDStatus) |
++----------------------------------------------------------------------------+
+| Task : Return the counter progress latched status after a |
+| index interrupt occur. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_UDStatus : 0 : Counter progress in the |
+| selected mode down |
+| 1 : Counter progress in the |
+| selected mode up |
+| 2 : No index interrupt occur |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: No counter module found |
+| -3: Counter not initialised see function |
+| "i_APCI1710_InitCounter" |
+| -4: Interrupt function not initialised. |
+| See function "i_APCI1710_SetBoardIntRoutineX" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_GetInterruptUDLatchedStatus(struct comedi_device * dev,
+ BYTE b_ModulNbr, PBYTE pb_UDStatus)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_StatusReg = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /*********************************/
+ /* Test if index interrupt occur */
+ /*********************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.b_IndexInterruptOccur == 1) {
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.b_IndexInterruptOccur = 0;
+
+ dw_StatusReg = inl(devpriv->s_BoardInfos.
+ ui_Address + 12 + (64 * b_ModulNbr));
+
+ *pb_UDStatus = (BYTE) ((dw_StatusReg >> 1) & 1);
+ } else {
+ /****************************/
+ /* No index interrupt occur */
+ /****************************/
+
+ *pb_UDStatus = 2;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+ /*
+ +----------------------------------------------------------------------------+
+ | Function Name : _INT_ i_APCI1710_ReadFrequencyMeasurement |
+ | (BYTE_ b_BoardHandle, |
+ | BYTE_ b_ModulNbr, |
+ | PBYTE_ pb_Status, |
+ | PULONG_ pul_ReadValue) |
+ +----------------------------------------------------------------------------+
+ | Task : Returns the status (pb_Status) and the number of |
+ | increments in the set time. |
+ | See function " i_APCI1710_InitFrequencyMeasurement " |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+ | BYTE_ b_ModulNbr : Number of the module to be |
+ | configured (0 to 3) |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : PBYTE_ pb_Status : Returns the frequency |
+ | measurement status |
+ | 0 : Counting cycle not |
+ | started. |
+ | 1 : Counting cycle started. |
+ | 2 : Counting cycle stopped. |
+ | The measurement cycle is |
+ | completed. |
+ | PBYTE_ pb_UDStatus : 0 : Counter progress in the |
+ | selected mode down |
+ | 1 : Counter progress in the |
+ | selected mode up |
+ | PULONG_ pul_ReadValue : Return the number of |
+ | increments in the defined |
+ | time base. |
+ +----------------------------------------------------------------------------+
+ | Return Value : 0: No error |
+ | -1: The handle parameter of the board is wrong |
+ | -2: The selected module number is wrong |
+ | -3: Counter not initialised see function |
+ | "i_APCI1710_InitCounter" |
+ | -4: Frequency measurement logic not initialised. |
+ | See function "i_APCI1710_InitFrequencyMeasurement" |
+ +----------------------------------------------------------------------------+
+ */
+
+INT i_APCI1710_ReadFrequencyMeasurement(struct comedi_device * dev,
+ BYTE b_ModulNbr,
+ PBYTE pb_Status, PBYTE pb_UDStatus, PULONG pul_ReadValue)
+{
+ INT i_ReturnValue = 0;
+ UINT ui_16BitValue;
+ DWORD dw_StatusReg;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.s_InitFlag.b_CounterInit == 1) {
+ /********************************************/
+ /* Test if frequency mesurement initialised */
+ /********************************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.b_FrequencyMeasurementInit == 1) {
+ /******************/
+ /* Test if enable */
+ /******************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SiemensCounterInfo.
+ s_InitFlag.
+ b_FrequencyMeasurementEnable == 1) {
+ /*******************/
+ /* Read the status */
+ /*******************/
+
+ dw_StatusReg =
+ inl(devpriv->s_BoardInfos.
+ ui_Address + 32 +
+ (64 * b_ModulNbr));
+
+ /**************************/
+ /* Test if frequency stop */
+ /**************************/
+
+ if (dw_StatusReg & 1) {
+ *pb_Status = 2;
+ *pb_UDStatus =
+ (BYTE) ((dw_StatusReg >>
+ 1) & 3);
+
+ /******************/
+ /* Read the value */
+ /******************/
+
+ *pul_ReadValue =
+ inl(devpriv->
+ s_BoardInfos.
+ ui_Address + 28 +
+ (64 * b_ModulNbr));
+
+ if (*pb_UDStatus == 0) {
+ /*************************/
+ /* Test the counter mode */
+ /*************************/
+
+ if ((devpriv->s_ModuleInfo[b_ModulNbr].s_SiemensCounterInfo.s_ModeRegister.s_ByteModeRegister.b_ModeRegister1 & APCI1710_16BIT_COUNTER) == APCI1710_16BIT_COUNTER) {
+ /****************************************/
+ /* Test if 16-bit counter 1 pulse occur */
+ /****************************************/
+
+ if ((*pul_ReadValue & 0xFFFFU) != 0) {
+ ui_16BitValue
+ =
+ (UINT)
+ *
+ pul_ReadValue
+ &
+ 0xFFFFU;
+ *pul_ReadValue
+ =
+ (*pul_ReadValue
+ &
+ 0xFFFF0000UL)
+ |
+ (0xFFFFU
+ -
+ ui_16BitValue);
+ }
+
+ /****************************************/
+ /* Test if 16-bit counter 2 pulse occur */
+ /****************************************/
+
+ if ((*pul_ReadValue & 0xFFFF0000UL) != 0) {
+ ui_16BitValue
+ =
+ (UINT)
+ (
+ (*pul_ReadValue
+ >>
+ 16)
+ &
+ 0xFFFFU);
+ *pul_ReadValue
+ =
+ (*pul_ReadValue
+ &
+ 0xFFFFUL)
+ |
+ (
+ (0xFFFFU - ui_16BitValue) << 16);
+ }
+ } else {
+ if (*pul_ReadValue != 0) {
+ *pul_ReadValue
+ =
+ 0xFFFFFFFFUL
+ -
+ *pul_ReadValue;
+ }
+ }
+ } else {
+ if (*pb_UDStatus == 1) {
+ /****************************************/
+ /* Test if 16-bit counter 2 pulse occur */
+ /****************************************/
+
+ if ((*pul_ReadValue & 0xFFFF0000UL) != 0) {
+ ui_16BitValue
+ =
+ (UINT)
+ (
+ (*pul_ReadValue
+ >>
+ 16)
+ &
+ 0xFFFFU);
+ *pul_ReadValue
+ =
+ (*pul_ReadValue
+ &
+ 0xFFFFUL)
+ |
+ (
+ (0xFFFFU - ui_16BitValue) << 16);
+ }
+ } else {
+ if (*pb_UDStatus
+ == 2) {
+ /****************************************/
+ /* Test if 16-bit counter 1 pulse occur */
+ /****************************************/
+
+ if ((*pul_ReadValue & 0xFFFFU) != 0) {
+ ui_16BitValue
+ =
+ (UINT)
+ *
+ pul_ReadValue
+ &
+ 0xFFFFU;
+ *pul_ReadValue
+ =
+ (*pul_ReadValue
+ &
+ 0xFFFF0000UL)
+ |
+ (0xFFFFU
+ -
+ ui_16BitValue);
+ }
+ }
+ }
+ }
+ } else {
+ *pb_Status = 1;
+ *pb_UDStatus = 0;
+ }
+ } else {
+ *pb_Status = 0;
+ *pb_UDStatus = 0;
+ }
+ } else {
+ /***********************************************/
+ /* Frequency measurement logic not initialised */
+ /***********************************************/
+
+ DPRINTK("Frequency measurement logic not initialised\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /****************************************/
+ /* Counter not initialised see function */
+ /* "i_APCI1710_InitCounter" */
+ /****************************************/
+
+ DPRINTK("Counter not initialised\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*************************************************/
+ /* The selected module number parameter is wrong */
+ /*************************************************/
+
+ DPRINTK("The selected module number parameter is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_INCCPT.h b/drivers/staging/comedi/drivers/addi-data/APCI1710_INCCPT.h
new file mode 100644
index 000000000000..5153cf686658
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_INCCPT.h
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+#define APCI1710_16BIT_COUNTER 0x10
+#define APCI1710_32BIT_COUNTER 0x0
+#define APCI1710_QUADRUPLE_MODE 0x0
+#define APCI1710_DOUBLE_MODE 0x3
+#define APCI1710_SIMPLE_MODE 0xF
+#define APCI1710_DIRECT_MODE 0x80
+#define APCI1710_HYSTERESIS_ON 0x60
+#define APCI1710_HYSTERESIS_OFF 0x0
+#define APCI1710_INCREMENT 0x60
+#define APCI1710_DECREMENT 0x0
+#define APCI1710_LATCH_COUNTER 0x1
+#define APCI1710_CLEAR_COUNTER 0x0
+#define APCI1710_LOW 0x0
+#define APCI1710_HIGH 0x1
+
+/*********************/
+/* Version 0600-0229 */
+/*********************/
+#define APCI1710_HIGH_EDGE_CLEAR_COUNTER 0x0
+#define APCI1710_HIGH_EDGE_LATCH_COUNTER 0x1
+#define APCI1710_LOW_EDGE_CLEAR_COUNTER 0x2
+#define APCI1710_LOW_EDGE_LATCH_COUNTER 0x3
+#define APCI1710_HIGH_EDGE_LATCH_AND_CLEAR_COUNTER 0x4
+#define APCI1710_LOW_EDGE_LATCH_AND_CLEAR_COUNTER 0x5
+#define APCI1710_SOURCE_0 0x0
+#define APCI1710_SOURCE_1 0x1
+
+#define APCI1710_30MHZ 30
+#define APCI1710_33MHZ 33
+#define APCI1710_40MHZ 40
+
+#define APCI1710_ENABLE_LATCH_INT 0x80
+#define APCI1710_DISABLE_LATCH_INT (~APCI1710_ENABLE_LATCH_INT)
+
+#define APCI1710_INDEX_LATCH_COUNTER 0x10
+#define APCI1710_INDEX_AUTO_MODE 0x8
+#define APCI1710_ENABLE_INDEX 0x4
+#define APCI1710_DISABLE_INDEX (~APCI1710_ENABLE_INDEX)
+#define APCI1710_ENABLE_LATCH_AND_CLEAR 0x8
+#define APCI1710_DISABLE_LATCH_AND_CLEAR (~APCI1710_ENABLE_LATCH_AND_CLEAR)
+#define APCI1710_SET_LOW_INDEX_LEVEL 0x4
+#define APCI1710_SET_HIGH_INDEX_LEVEL (~APCI1710_SET_LOW_INDEX_LEVEL)
+#define APCI1710_INVERT_INDEX_RFERENCE 0x2
+#define APCI1710_DEFAULT_INDEX_RFERENCE (~APCI1710_INVERT_INDEX_RFERENCE)
+
+#define APCI1710_ENABLE_INDEX_INT 0x1
+#define APCI1710_DISABLE_INDEX_INT (~APCI1710_ENABLE_INDEX_INT)
+
+#define APCI1710_ENABLE_FREQUENCY 0x4
+#define APCI1710_DISABLE_FREQUENCY (~APCI1710_ENABLE_FREQUENCY)
+
+#define APCI1710_ENABLE_FREQUENCY_INT 0x8
+#define APCI1710_DISABLE_FREQUENCY_INT (~APCI1710_ENABLE_FREQUENCY_INT)
+
+#define APCI1710_ENABLE_40MHZ_FREQUENCY 0x40
+#define APCI1710_DISABLE_40MHZ_FREQUENCY (~APCI1710_ENABLE_40MHZ_FREQUENCY)
+
+#define APCI1710_ENABLE_40MHZ_FILTER 0x80
+#define APCI1710_DISABLE_40MHZ_FILTER (~APCI1710_ENABLE_40MHZ_FILTER)
+
+#define APCI1710_ENABLE_COMPARE_INT 0x2
+#define APCI1710_DISABLE_COMPARE_INT (~APCI1710_ENABLE_COMPARE_INT)
+
+#define APCI1710_ENABLE_INDEX_ACTION 0x20
+#define APCI1710_DISABLE_INDEX_ACTION (~APCI1710_ENABLE_INDEX_ACTION)
+#define APCI1710_REFERENCE_HIGH 0x40
+#define APCI1710_REFERENCE_LOW (~APCI1710_REFERENCE_HIGH)
+
+#define APCI1710_TOR_GATE_LOW 0x40
+#define APCI1710_TOR_GATE_HIGH (~APCI1710_TOR_GATE_LOW)
+
+/* INSN CONFIG */
+#define APCI1710_INCCPT_INITCOUNTER 100
+#define APCI1710_INCCPT_COUNTERAUTOTEST 101
+#define APCI1710_INCCPT_INITINDEX 102
+#define APCI1710_INCCPT_INITREFERENCE 103
+#define APCI1710_INCCPT_INITEXTERNALSTROBE 104
+#define APCI1710_INCCPT_INITCOMPARELOGIC 105
+#define APCI1710_INCCPT_INITFREQUENCYMEASUREMENT 106
+
+/* INSN READ */
+#define APCI1710_INCCPT_READLATCHREGISTERSTATUS 200
+#define APCI1710_INCCPT_READLATCHREGISTERVALUE 201
+#define APCI1710_INCCPT_READ16BITCOUNTERVALUE 202
+#define APCI1710_INCCPT_READ32BITCOUNTERVALUE 203
+#define APCI1710_INCCPT_GETINDEXSTATUS 204
+#define APCI1710_INCCPT_GETREFERENCESTATUS 205
+#define APCI1710_INCCPT_GETUASSTATUS 206
+#define APCI1710_INCCPT_GETCBSTATUS 207
+#define APCI1710_INCCPT_GET16BITCBSTATUS 208
+#define APCI1710_INCCPT_GETUDSTATUS 209
+#define APCI1710_INCCPT_GETINTERRUPTUDLATCHEDSTATUS 210
+#define APCI1710_INCCPT_READFREQUENCYMEASUREMENT 211
+#define APCI1710_INCCPT_READINTERRUPT 212
+
+/* INSN BITS */
+#define APCI1710_INCCPT_CLEARCOUNTERVALUE 300
+#define APCI1710_INCCPT_CLEARALLCOUNTERVALUE 301
+#define APCI1710_INCCPT_SETINPUTFILTER 302
+#define APCI1710_INCCPT_LATCHCOUNTER 303
+#define APCI1710_INCCPT_SETINDEXANDREFERENCESOURCE 304
+#define APCI1710_INCCPT_SETDIGITALCHLON 305
+#define APCI1710_INCCPT_SETDIGITALCHLOFF 306
+
+/* INSN WRITE */
+#define APCI1710_INCCPT_ENABLELATCHINTERRUPT 400
+#define APCI1710_INCCPT_DISABLELATCHINTERRUPT 401
+#define APCI1710_INCCPT_WRITE16BITCOUNTERVALUE 402
+#define APCI1710_INCCPT_WRITE32BITCOUNTERVALUE 403
+#define APCI1710_INCCPT_ENABLEINDEX 404
+#define APCI1710_INCCPT_DISABLEINDEX 405
+#define APCI1710_INCCPT_ENABLECOMPARELOGIC 406
+#define APCI1710_INCCPT_DISABLECOMPARELOGIC 407
+#define APCI1710_INCCPT_ENABLEFREQUENCYMEASUREMENT 408
+#define APCI1710_INCCPT_DISABLEFREQUENCYMEASUREMENT 409
+
+/************ Main Functions *************/
+INT i_APCI1710_InsnConfigINCCPT(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int * data);
+
+INT i_APCI1710_InsnBitsINCCPT(struct comedi_device *dev, struct comedi_subdevice * s,
+ struct comedi_insn *insn, unsigned int * data);
+
+INT i_APCI1710_InsnWriteINCCPT(struct comedi_device *dev, struct comedi_subdevice * s,
+ struct comedi_insn *insn, unsigned int * data);
+
+INT i_APCI1710_InsnReadINCCPT(struct comedi_device *dev, struct comedi_subdevice * s,
+ struct comedi_insn *insn, unsigned int * data);
+
+/*********** Supplementary Functions********/
+
+/* INSN CONFIG */
+INT i_APCI1710_InitCounter(struct comedi_device *dev,
+ BYTE b_ModulNbr,
+ BYTE b_CounterRange,
+ BYTE b_FirstCounterModus,
+ BYTE b_FirstCounterOption,
+ BYTE b_SecondCounterModus,
+ BYTE b_SecondCounterOption);
+
+INT i_APCI1710_CounterAutoTest(struct comedi_device *dev, PBYTE pb_TestStatus);
+
+INT i_APCI1710_InitIndex(struct comedi_device *dev,
+ BYTE b_ModulNbr,
+ BYTE b_ReferenceAction,
+ BYTE b_IndexOperation, BYTE b_AutoMode,
+ BYTE b_InterruptEnable);
+
+INT i_APCI1710_InitReference(struct comedi_device *dev,
+ BYTE b_ModulNbr, BYTE b_ReferenceLevel);
+
+INT i_APCI1710_InitExternalStrobe(struct comedi_device *dev,
+ BYTE b_ModulNbr, BYTE b_ExternalStrobe,
+ BYTE b_ExternalStrobeLevel);
+
+INT i_APCI1710_InitCompareLogic(struct comedi_device *dev,
+ BYTE b_ModulNbr, UINT ui_CompareValue);
+
+INT i_APCI1710_InitFrequencyMeasurement(struct comedi_device *dev,
+ BYTE b_ModulNbr,
+ BYTE b_PCIInputClock,
+ BYTE b_TimingUnity,
+ ULONG ul_TimingInterval,
+ PULONG pul_RealTimingInterval);
+
+/* INSN BITS */
+INT i_APCI1710_ClearCounterValue(struct comedi_device *dev, BYTE b_ModulNbr);
+
+INT i_APCI1710_ClearAllCounterValue(struct comedi_device *dev);
+
+INT i_APCI1710_SetInputFilter(struct comedi_device *dev,
+ BYTE b_ModulNbr, BYTE b_PCIInputClock,
+ BYTE b_Filter);
+
+INT i_APCI1710_LatchCounter(struct comedi_device *dev,
+ BYTE b_ModulNbr, BYTE b_LatchReg);
+
+INT i_APCI1710_SetIndexAndReferenceSource(struct comedi_device *dev,
+ BYTE b_ModulNbr,
+ BYTE b_SourceSelection);
+
+INT i_APCI1710_SetDigitalChlOn(struct comedi_device *dev, BYTE b_ModulNbr);
+
+INT i_APCI1710_SetDigitalChlOff(struct comedi_device *dev, BYTE b_ModulNbr);
+
+/* INSN WRITE */
+INT i_APCI1710_EnableLatchInterrupt(struct comedi_device *dev, BYTE b_ModulNbr);
+
+INT i_APCI1710_DisableLatchInterrupt(struct comedi_device *dev, BYTE b_ModulNbr);
+
+INT i_APCI1710_Write16BitCounterValue(struct comedi_device *dev,
+ BYTE b_ModulNbr, BYTE b_SelectedCounter,
+ UINT ui_WriteValue);
+
+INT i_APCI1710_Write32BitCounterValue(struct comedi_device *dev,
+ BYTE b_ModulNbr, ULONG ul_WriteValue);
+
+INT i_APCI1710_EnableIndex(struct comedi_device *dev, BYTE b_ModulNbr);
+
+INT i_APCI1710_DisableIndex(struct comedi_device *dev, BYTE b_ModulNbr);
+
+INT i_APCI1710_EnableCompareLogic(struct comedi_device *dev, BYTE b_ModulNbr);
+
+INT i_APCI1710_DisableCompareLogic(struct comedi_device *dev, BYTE b_ModulNbr);
+
+INT i_APCI1710_EnableFrequencyMeasurement(struct comedi_device *dev,
+ BYTE b_ModulNbr,
+ BYTE b_InterruptEnable);
+
+INT i_APCI1710_DisableFrequencyMeasurement(struct comedi_device *dev,
+ BYTE b_ModulNbr);
+
+/* INSN READ */
+INT i_APCI1710_ReadLatchRegisterStatus(struct comedi_device *dev,
+ BYTE b_ModulNbr, BYTE b_LatchReg,
+ PBYTE pb_LatchStatus);
+
+INT i_APCI1710_ReadLatchRegisterValue(struct comedi_device *dev,
+ BYTE b_ModulNbr, BYTE b_LatchReg,
+ PULONG pul_LatchValue);
+
+INT i_APCI1710_Read16BitCounterValue(struct comedi_device *dev,
+ BYTE b_ModulNbr, BYTE b_SelectedCounter,
+ PUINT pui_CounterValue);
+
+INT i_APCI1710_Read32BitCounterValue(struct comedi_device *dev,
+ BYTE b_ModulNbr, PULONG pul_CounterValue);
+
+INT i_APCI1710_GetIndexStatus(struct comedi_device *dev,
+ BYTE b_ModulNbr, PBYTE pb_IndexStatus);
+
+INT i_APCI1710_GetReferenceStatus(struct comedi_device *dev,
+ BYTE b_ModulNbr, PBYTE pb_ReferenceStatus);
+
+INT i_APCI1710_GetUASStatus(struct comedi_device *dev,
+ BYTE b_ModulNbr, PBYTE pb_UASStatus);
+
+INT i_APCI1710_GetCBStatus(struct comedi_device *dev,
+ BYTE b_ModulNbr, PBYTE pb_CBStatus);
+
+INT i_APCI1710_Get16BitCBStatus(struct comedi_device *dev,
+ BYTE b_ModulNbr, PBYTE pb_CBStatusCounter0,
+ PBYTE pb_CBStatusCounter1);
+
+INT i_APCI1710_GetUDStatus(struct comedi_device *dev,
+ BYTE b_ModulNbr, PBYTE pb_UDStatus);
+
+INT i_APCI1710_GetInterruptUDLatchedStatus(struct comedi_device *dev,
+ BYTE b_ModulNbr, PBYTE pb_UDStatus);
+
+INT i_APCI1710_ReadFrequencyMeasurement(struct comedi_device *dev,
+ BYTE b_ModulNbr,
+ PBYTE pb_Status, PBYTE pb_UDStatus,
+ PULONG pul_ReadValue);
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_Inp_cpt.c b/drivers/staging/comedi/drivers/addi-data/APCI1710_Inp_cpt.c
new file mode 100644
index 000000000000..3ac6a264ef54
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_Inp_cpt.c
@@ -0,0 +1,861 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-----------------------------------------------------------------------+
+ | Project : API APCI1710 | Compiler : gcc |
+ | Module name : Inp_CPT.C | Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-----------------------------------------------------------------------+
+ | Description : APCI-1710 pulse encoder module |
+ | |
+ | |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +-----------------------------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | | | |
+ |----------|-----------|------------------------------------------------|
+ | 08/05/00 | Guinot C | - 0400/0228 All Function in RING 0 |
+ | | | available |
+ +-----------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+
+#include "APCI1710_Inp_cpt.h"
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_InitPulseEncoder |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_PulseEncoderNbr, |
+| BYTE_ b_InputLevelSelection, |
+| BYTE_ b_TriggerOutputAction, |
+| ULONG_ ul_StartValue) |
++----------------------------------------------------------------------------+
+| Task : Configure the pulse encoder operating mode selected via|
+| b_ModulNbr and b_PulseEncoderNbr. The pulse encoder |
+| after each pulse decrement the counter value from 1. |
+| |
+| You must calling this function be for you call any |
+| other function witch access of pulse encoders. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710|
+| BYTE_ b_ModulNbr : Module number to |
+| configure (0 to 3) |
+| BYTE_ b_PulseEncoderNbr : Pulse encoder selection |
+| (0 to 3) |
+| BYTE_ b_InputLevelSelection : Input level selection |
+| (0 or 1) |
+| 0 : Set pulse encoder|
+| count the the low|
+| level pulse. |
+| 1 : Set pulse encoder|
+| count the the |
+| high level pulse.|
+| BYTE_ b_TriggerOutputAction : Digital TRIGGER output |
+| action |
+| 0 : No action |
+| 1 : Set the trigger |
+| output to "1" |
+| (high) after the |
+| passage from 1 to|
+| 0 from pulse |
+| encoder. |
+| 2 : Set the trigger |
+| output to "0" |
+| (low) after the |
+| passage from 1 to|
+| 0 from pulse |
+| encoder |
+| ULONG_ ul_StartValue : Pulse encoder start value|
+| (1 to 4294967295)
+ b_ModulNbr =(BYTE) CR_AREF(insn->chanspec);
+ b_PulseEncoderNbr =(BYTE) data[0];
+ b_InputLevelSelection =(BYTE) data[1];
+ b_TriggerOutputAction =(BYTE) data[2];
+ ul_StartValue =(ULONG) data[3];
+ |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The module is not a pulse encoder module |
+| -3: Pulse encoder selection is wrong |
+| -4: Input level selection is wrong |
+| -5: Digital TRIGGER output action selection is wrong |
+| -6: Pulse encoder start value is wrong |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnConfigInitPulseEncoder(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_IntRegister;
+
+ BYTE b_ModulNbr;
+ BYTE b_PulseEncoderNbr;
+ BYTE b_InputLevelSelection;
+ BYTE b_TriggerOutputAction;
+ ULONG ul_StartValue;
+
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_PulseEncoderNbr = (BYTE) data[0];
+ b_InputLevelSelection = (BYTE) data[1];
+ b_TriggerOutputAction = (BYTE) data[2];
+ ul_StartValue = (ULONG) data[3];
+
+ i_ReturnValue = insn->n;
+
+ /***********************************/
+ /* Test the selected module number */
+ /***********************************/
+
+ if (b_ModulNbr <= 3) {
+ /*************************/
+ /* Test if pulse encoder */
+ /*************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ APCI1710_PULSE_ENCODER) ==
+ APCI1710_PULSE_ENCODER) {
+ /******************************************/
+ /* Test the selected pulse encoder number */
+ /******************************************/
+
+ if (b_PulseEncoderNbr <= 3) {
+ /************************/
+ /* Test the input level */
+ /************************/
+
+ if ((b_InputLevelSelection == 0)
+ || (b_InputLevelSelection == 1)) {
+ /*******************************************/
+ /* Test the ouput TRIGGER action selection */
+ /*******************************************/
+
+ if ((b_TriggerOutputAction <= 2)
+ || (b_PulseEncoderNbr > 0)) {
+ if (ul_StartValue > 1) {
+
+ dw_IntRegister =
+ inl(devpriv->
+ s_BoardInfos.
+ ui_Address +
+ 20 +
+ (64 * b_ModulNbr));
+
+ /***********************/
+ /* Set the start value */
+ /***********************/
+
+ outl(ul_StartValue,
+ devpriv->
+ s_BoardInfos.
+ ui_Address +
+ (b_PulseEncoderNbr
+ * 4) +
+ (64 * b_ModulNbr));
+
+ /***********************/
+ /* Set the input level */
+ /***********************/
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_SetRegister =
+ (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_SetRegister &
+ (0xFFFFFFFFUL -
+ (1UL << (8 + b_PulseEncoderNbr)))) | ((1UL & (~b_InputLevelSelection)) << (8 + b_PulseEncoderNbr));
+
+ /*******************************/
+ /* Test if output trigger used */
+ /*******************************/
+
+ if ((b_TriggerOutputAction > 0) && (b_PulseEncoderNbr > 1)) {
+ /****************************/
+ /* Enable the output action */
+ /****************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_SetRegister
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_SetRegister
+ | (1UL
+ << (4 + b_PulseEncoderNbr));
+
+ /*********************************/
+ /* Set the output TRIGGER action */
+ /*********************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_SetRegister
+ =
+ (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_SetRegister
+ &
+ (0xFFFFFFFFUL
+ -
+ (1UL << (12 + b_PulseEncoderNbr)))) | ((1UL & (b_TriggerOutputAction - 1)) << (12 + b_PulseEncoderNbr));
+ } else {
+ /*****************************/
+ /* Disable the output action */
+ /*****************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_SetRegister
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_SetRegister
+ &
+ (0xFFFFFFFFUL
+ -
+ (1UL << (4 + b_PulseEncoderNbr)));
+ }
+
+ /*************************/
+ /* Set the configuration */
+ /*************************/
+
+ outl(devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_SetRegister,
+ devpriv->
+ s_BoardInfos.
+ ui_Address +
+ 20 +
+ (64 * b_ModulNbr));
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ s_PulseEncoderInfo
+ [b_PulseEncoderNbr].
+ b_PulseEncoderInit
+ = 1;
+ } else {
+ /**************************************/
+ /* Pulse encoder start value is wrong */
+ /**************************************/
+
+ DPRINTK("Pulse encoder start value is wrong\n");
+ i_ReturnValue = -6;
+ }
+ } else {
+ /****************************************************/
+ /* Digital TRIGGER output action selection is wrong */
+ /****************************************************/
+
+ DPRINTK("Digital TRIGGER output action selection is wrong\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /**********************************/
+ /* Input level selection is wrong */
+ /**********************************/
+
+ DPRINTK("Input level selection is wrong\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /************************************/
+ /* Pulse encoder selection is wrong */
+ /************************************/
+
+ DPRINTK("Pulse encoder selection is wrong\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /********************************************/
+ /* The module is not a pulse encoder module */
+ /********************************************/
+
+ DPRINTK("The module is not a pulse encoder module\n");
+ i_ReturnValue = -2;
+ }
+ } else {
+ /********************************************/
+ /* The module is not a pulse encoder module */
+ /********************************************/
+
+ DPRINTK("The module is not a pulse encoder module\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_EnablePulseEncoder |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_PulseEncoderNbr, |
+| BYTE_ b_CycleSelection, |
+| BYTE_ b_InterruptHandling) |
++----------------------------------------------------------------------------+
+| Task : Enableor disable the selected pulse encoder (b_PulseEncoderNbr) |
+| from selected module (b_ModulNbr). Each input pulse |
+| decrement the pulse encoder counter value from 1. |
+| If you enabled the interrupt (b_InterruptHandling), a |
+| interrupt is generated when the pulse encoder has run |
+| down. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710|
+| BYTE_ b_ModulNbr : Module number to |
+| configure (0 to 3) |
+| BYTE_ b_PulseEncoderNbr : Pulse encoder selection |
+| (0 to 3) |
+| BYTE_ b_CycleSelection : APCI1710_CONTINUOUS: |
+| Each time the |
+| counting value is set|
+| on "0", the pulse |
+| encoder load the |
+| start value after |
+| the next pulse. |
+| APCI1710_SINGLE: |
+| If the counter is set|
+| on "0", the pulse |
+| encoder is stopped. |
+| BYTE_ b_InterruptHandling : Interrupts can be |
+| generated, when the pulse|
+| encoder has run down. |
+| With this parameter the |
+| user decides if |
+| interrupts are used or |
+| not. |
+| APCI1710_ENABLE: |
+| Interrupts are enabled |
+| APCI1710_DISABLE: |
+| Interrupts are disabled
+
+ b_ModulNbr =(BYTE) CR_AREF(insn->chanspec);
+ b_Action =(BYTE) data[0];
+ b_PulseEncoderNbr =(BYTE) data[1];
+ b_CycleSelection =(BYTE) data[2];
+ b_InterruptHandling =(BYTE) data[3];|
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection is wrong |
+| -3: Pulse encoder selection is wrong |
+| -4: Pulse encoder not initialised. |
+| See function "i_APCI1710_InitPulseEncoder" |
+| -5: Cycle selection mode is wrong |
+| -6: Interrupt handling mode is wrong |
+| -7: Interrupt routine not installed. |
+| See function "i_APCI1710_SetBoardIntRoutineX" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnWriteEnableDisablePulseEncoder(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ BYTE b_ModulNbr;
+ BYTE b_PulseEncoderNbr;
+ BYTE b_CycleSelection;
+ BYTE b_InterruptHandling;
+ BYTE b_Action;
+
+ i_ReturnValue = insn->n;
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_Action = (BYTE) data[0];
+ b_PulseEncoderNbr = (BYTE) data[1];
+ b_CycleSelection = (BYTE) data[2];
+ b_InterruptHandling = (BYTE) data[3];
+
+ /***********************************/
+ /* Test the selected module number */
+ /***********************************/
+
+ if (b_ModulNbr <= 3) {
+ /******************************************/
+ /* Test the selected pulse encoder number */
+ /******************************************/
+
+ if (b_PulseEncoderNbr <= 3) {
+ /*************************************/
+ /* Test if pulse encoder initialised */
+ /*************************************/
+
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ s_PulseEncoderInfo[b_PulseEncoderNbr].
+ b_PulseEncoderInit == 1) {
+ switch (b_Action) {
+
+ case APCI1710_ENABLE:
+ /****************************/
+ /* Test the cycle selection */
+ /****************************/
+
+ if (b_CycleSelection ==
+ APCI1710_CONTINUOUS
+ || b_CycleSelection ==
+ APCI1710_SINGLE) {
+ /*******************************/
+ /* Test the interrupt handling */
+ /*******************************/
+
+ if (b_InterruptHandling ==
+ APCI1710_ENABLE
+ || b_InterruptHandling
+ == APCI1710_DISABLE) {
+ /******************************/
+ /* Test if interrupt not used */
+ /******************************/
+
+ if (b_InterruptHandling
+ ==
+ APCI1710_DISABLE)
+ {
+ /*************************/
+ /* Disable the interrupt */
+ /*************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_SetRegister
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_SetRegister
+ &
+ (0xFFFFFFFFUL
+ -
+ (1UL << b_PulseEncoderNbr));
+ } else {
+
+ /************************/
+ /* Enable the interrupt */
+ /************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_SetRegister
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_SetRegister
+ | (1UL
+ <<
+ b_PulseEncoderNbr);
+ devpriv->tsk_Current = current; // Save the current process task structure
+
+ }
+
+ if (i_ReturnValue >= 0) {
+ /***********************************/
+ /* Enable or disable the interrupt */
+ /***********************************/
+
+ outl(devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_SetRegister,
+ devpriv->
+ s_BoardInfos.
+ ui_Address
+ + 20 +
+ (64 * b_ModulNbr));
+
+ /****************************/
+ /* Enable the pulse encoder */
+ /****************************/
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_ControlRegister
+ =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_ControlRegister
+ | (1UL
+ <<
+ b_PulseEncoderNbr);
+
+ /**********************/
+ /* Set the cycle mode */
+ /**********************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_ControlRegister
+ =
+ (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_ControlRegister
+ &
+ (0xFFFFFFFFUL
+ -
+ (1 << (b_PulseEncoderNbr + 4)))) | ((b_CycleSelection & 1UL) << (4 + b_PulseEncoderNbr));
+
+ /****************************/
+ /* Enable the pulse encoder */
+ /****************************/
+
+ outl(devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_ControlRegister,
+ devpriv->
+ s_BoardInfos.
+ ui_Address
+ + 16 +
+ (64 * b_ModulNbr));
+ }
+ } else {
+ /************************************/
+ /* Interrupt handling mode is wrong */
+ /************************************/
+
+ DPRINTK("Interrupt handling mode is wrong\n");
+ i_ReturnValue = -6;
+ }
+ } else {
+ /*********************************/
+ /* Cycle selection mode is wrong */
+ /*********************************/
+
+ DPRINTK("Cycle selection mode is wrong\n");
+ i_ReturnValue = -5;
+ }
+ break;
+
+ case APCI1710_DISABLE:
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_ControlRegister =
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_ControlRegister &
+ (0xFFFFFFFFUL -
+ (1UL << b_PulseEncoderNbr));
+
+ /*****************************/
+ /* Disable the pulse encoder */
+ /*****************************/
+
+ outl(devpriv->s_ModuleInfo[b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_ControlRegister,
+ devpriv->s_BoardInfos.
+ ui_Address + 16 +
+ (64 * b_ModulNbr));
+
+ break;
+ } // switch End
+
+ } else {
+ /*********************************/
+ /* Pulse encoder not initialised */
+ /*********************************/
+
+ DPRINTK("Pulse encoder not initialised\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /************************************/
+ /* Pulse encoder selection is wrong */
+ /************************************/
+
+ DPRINTK("Pulse encoder selection is wrong\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*****************************/
+ /* Module selection is wrong */
+ /*****************************/
+
+ DPRINTK("Module selection is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_ReadPulseEncoderStatus |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_PulseEncoderNbr, |
+| PBYTE_ pb_Status) |
++----------------------------------------------------------------------------+
+| Task APCI1710_PULSEENCODER_READ : Reads the pulse encoder status
+ and valuefrom selected pulse |
+| encoder (b_PulseEncoderNbr) from selected module |
+| (b_ModulNbr). |
++----------------------------------------------------------------------------+
+ BYTE b_Type; data[0]
+ APCI1710_PULSEENCODER_WRITE
+ Writes a 32-bit value (ul_WriteValue) into the selected|
+| pulse encoder (b_PulseEncoderNbr) from selected module |
+| (b_ModulNbr). This operation set the new start pulse |
+| encoder value.
+ APCI1710_PULSEENCODER_READ
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710|
+| CRAREF() BYTE_ b_ModulNbr : Module number to |
+| configure (0 to 3) |
+| data[1] BYTE_ b_PulseEncoderNbr : Pulse encoder selection |
+| (0 to 3)
+ APCI1710_PULSEENCODER_WRITE
+ data[2] ULONG_ ul_WriteValue : 32-bit value to be |
+| written |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_Status : Pulse encoder status. |
+| 0 : No overflow occur|
+| 1 : Overflow occur
+ PULONG_ pul_ReadValue : Pulse encoder value | |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection is wrong |
+| -3: Pulse encoder selection is wrong |
+| -4: Pulse encoder not initialised. |
+| See function "i_APCI1710_InitPulseEncoder" |
++----------------------------------------------------------------------------+
+*/
+
+/*_INT_ i_APCI1710_ReadPulseEncoderStatus (BYTE_ b_BoardHandle,
+ BYTE_ b_ModulNbr,
+ BYTE_ b_PulseEncoderNbr,
+
+ PBYTE_ pb_Status)
+ */
+INT i_APCI1710_InsnBitsReadWritePulseEncoder(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_StatusRegister;
+ BYTE b_ModulNbr;
+ BYTE b_PulseEncoderNbr;
+ PBYTE pb_Status;
+ BYTE b_Type;
+ PULONG pul_ReadValue;
+ ULONG ul_WriteValue;
+
+ i_ReturnValue = insn->n;
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_Type = (BYTE) data[0];
+ b_PulseEncoderNbr = (BYTE) data[1];
+ pb_Status = (PBYTE) & data[0];
+ pul_ReadValue = (PULONG) & data[1];
+
+ /***********************************/
+ /* Test the selected module number */
+ /***********************************/
+
+ if (b_ModulNbr <= 3) {
+ /******************************************/
+ /* Test the selected pulse encoder number */
+ /******************************************/
+
+ if (b_PulseEncoderNbr <= 3) {
+ /*************************************/
+ /* Test if pulse encoder initialised */
+ /*************************************/
+
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ s_PulseEncoderInfo[b_PulseEncoderNbr].
+ b_PulseEncoderInit == 1) {
+
+ switch (b_Type) {
+ case APCI1710_PULSEENCODER_READ:
+ /****************************/
+ /* Read the status register */
+ /****************************/
+
+ dw_StatusRegister =
+ inl(devpriv->s_BoardInfos.
+ ui_Address + 16 +
+ (64 * b_ModulNbr));
+
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_StatusRegister = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_StatusRegister |
+ dw_StatusRegister;
+
+ *pb_Status =
+ (BYTE) (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_StatusRegister >> (1 +
+ b_PulseEncoderNbr)) & 1;
+
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_StatusRegister =
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_PulseEncoderModuleInfo.
+ dw_StatusRegister &
+ (0xFFFFFFFFUL - (1 << (1 +
+ b_PulseEncoderNbr)));
+
+ /******************/
+ /* Read the value */
+ /******************/
+
+ *pul_ReadValue =
+ inl(devpriv->s_BoardInfos.
+ ui_Address +
+ (4 * b_PulseEncoderNbr) +
+ (64 * b_ModulNbr));
+ break;
+
+ case APCI1710_PULSEENCODER_WRITE:
+ ul_WriteValue = (ULONG) data[2];
+ /*******************/
+ /* Write the value */
+ /*******************/
+
+ outl(ul_WriteValue,
+ devpriv->s_BoardInfos.
+ ui_Address +
+ (4 * b_PulseEncoderNbr) +
+ (64 * b_ModulNbr));
+
+ } //end of switch
+ } else {
+ /*********************************/
+ /* Pulse encoder not initialised */
+ /*********************************/
+
+ DPRINTK("Pulse encoder not initialised\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /************************************/
+ /* Pulse encoder selection is wrong */
+ /************************************/
+
+ DPRINTK("Pulse encoder selection is wrong\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*****************************/
+ /* Module selection is wrong */
+ /*****************************/
+
+ DPRINTK("Module selection is wrong\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+INT i_APCI1710_InsnReadInterruptPulseEncoder(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+
+ data[0] = devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.ui_Read].b_OldModuleMask;
+ data[1] = devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.ui_Read].ul_OldInterruptMask;
+ data[2] = devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.ui_Read].ul_OldCounterLatchValue;
+
+ /***************************/
+ /* Increment the read FIFO */
+ /***************************/
+
+ devpriv->s_InterruptParameters.
+ ui_Read = (devpriv->
+ s_InterruptParameters.ui_Read + 1) % APCI1710_SAVE_INTERRUPT;
+
+ return insn->n;
+
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_Inp_cpt.h b/drivers/staging/comedi/drivers/addi-data/APCI1710_Inp_cpt.h
new file mode 100644
index 000000000000..28252873e862
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_Inp_cpt.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+#define APCI1710_SINGLE 0
+#define APCI1710_CONTINUOUS 1
+
+#define APCI1710_PULSEENCODER_READ 0
+#define APCI1710_PULSEENCODER_WRITE 1
+
+INT i_APCI1710_InsnConfigInitPulseEncoder(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+INT i_APCI1710_InsnWriteEnableDisablePulseEncoder(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+
+/*
+ * READ PULSE ENCODER FUNCTIONS
+ */
+INT i_APCI1710_InsnReadInterruptPulseEncoder(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+
+/*
+ * WRITE PULSE ENCODER FUNCTIONS
+ */
+INT i_APCI1710_InsnBitsReadWritePulseEncoder(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_Pwm.c b/drivers/staging/comedi/drivers/addi-data/APCI1710_Pwm.c
new file mode 100644
index 000000000000..5ddd092a656a
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_Pwm.c
@@ -0,0 +1,3588 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-----------------------------------------------------------------------+
+ | Project : API APCI1710 | Compiler : gcc |
+ | Module name : PWM.C | Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-----------------------------------------------------------------------+
+ | Description : APCI-1710 Wulse wide modulation module |
+ | |
+ | |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +-----------------------------------------------------------------------+
+ | Date | Author | Description of updates |
+ +-----------------------------------------------------------------------+
+ | 08/05/00 | Guinot C | - 0400/0228 All Function in RING 0 |
+ | | | available |
+ +-----------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+
+#include "APCI1710_Pwm.h"
+
+/*
++----------------------------------------------------------------------------+
+| Function Name :INT i_APCI1710_InsnConfigPWM(struct comedi_device *dev,
+struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Pwm Init and Get Pwm Initialisation |
++----------------------------------------------------------------------------+
+| Input Parameters :
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value :
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnConfigPWM(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ BYTE b_ConfigType;
+ INT i_ReturnValue = 0;
+ b_ConfigType = CR_CHAN(insn->chanspec);
+
+ switch (b_ConfigType) {
+ case APCI1710_PWM_INIT:
+ i_ReturnValue = i_APCI1710_InitPWM(dev, (BYTE) CR_AREF(insn->chanspec), // b_ModulNbr
+ (BYTE) data[0], //b_PWM
+ (BYTE) data[1], // b_ClockSelection
+ (BYTE) data[2], // b_TimingUnit
+ (ULONG) data[3], //ul_LowTiming
+ (ULONG) data[4], //ul_HighTiming
+ (PULONG) & data[0], //pul_RealLowTiming
+ (PULONG) & data[1] //pul_RealHighTiming
+ );
+ break;
+
+ case APCI1710_PWM_GETINITDATA:
+ i_ReturnValue = i_APCI1710_GetPWMInitialisation(dev, (BYTE) CR_AREF(insn->chanspec), // b_ModulNbr
+ (BYTE) data[0], //b_PWM
+ (PBYTE) & data[0], //pb_TimingUnit
+ (PULONG) & data[1], //pul_LowTiming
+ (PULONG) & data[2], //pul_HighTiming
+ (PBYTE) & data[3], // pb_StartLevel
+ (PBYTE) & data[4], // pb_StopMode
+ (PBYTE) & data[5], // pb_StopLevel
+ (PBYTE) & data[6], // pb_ExternGate
+ (PBYTE) & data[7], // pb_InterruptEnable
+ (PBYTE) & data[8] // pb_Enable
+ );
+ break;
+
+ default:
+ printk(" Config Parameter Wrong\n");
+ }
+
+ if (i_ReturnValue >= 0)
+ i_ReturnValue = insn->n;
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_InitPWM |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_PWM, |
+| BYTE_ b_ClockSelection, |
+| BYTE_ b_TimingUnit, |
+| ULONG_ ul_LowTiming, |
+| ULONG_ ul_HighTiming, |
+| PULONG_ pul_RealLowTiming, |
+| PULONG_ pul_RealHighTiming) |
++----------------------------------------------------------------------------+
+| Task : Configure the selected PWM (b_PWM) from selected module|
+| (b_ModulNbr). The ul_LowTiming, ul_HighTiming and |
+| ul_TimingUnit determine the low/high timing base for |
+| the period. pul_RealLowTiming, pul_RealHighTiming |
+| return the real timing value. |
+| You must calling this function be for you call any |
+| other function witch access of the PWM. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure|
+| (0 to 3) |
+| BYTE_ b_PWM : Selected PWM (0 or 1). |
+| BYTE_ b_ClockSelection : Selection from PCI bus |
+| clock |
+| - APCI1710_30MHZ : |
+| The PC have a 30 MHz |
+| PCI bus clock |
+| - APCI1710_33MHZ : |
+| The PC have a 33 MHz |
+| PCI bus clock |
+| - APCI1710_40MHZ |
+| The APCI-1710 have a |
+| integrated 40Mhz |
+| quartz. |
+| BYTE_ b_TimingUnit : Base timing Unit (0 to 4) |
+| 0 : ns |
+| 1 : æs |
+| 2 : ms |
+| 3 : s |
+| 4 : mn |
+| ULONG_ ul_LowTiming : Low base timing value. |
+| ULONG_ ul_HighTiming : High base timing value. |
++----------------------------------------------------------------------------+
+| Output Parameters : PULONG_ pul_RealLowTiming : Real low base timing |
+| value. |
+| PULONG_ pul_RealHighTiming : Real high base timing |
+| value. |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a PWM module |
+| -4: PWM selection is wrong |
+| -5: The selected input clock is wrong |
+| -6: Timing Unit selection is wrong |
+| -7: Low base timing selection is wrong |
+| -8: High base timing selection is wrong |
+| -9: You can not used the 40MHz clock selection with |
+| this board |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InitPWM(struct comedi_device * dev,
+ BYTE b_ModulNbr,
+ BYTE b_PWM,
+ BYTE b_ClockSelection,
+ BYTE b_TimingUnit,
+ ULONG ul_LowTiming,
+ ULONG ul_HighTiming,
+ PULONG pul_RealLowTiming, PULONG pul_RealHighTiming)
+{
+ INT i_ReturnValue = 0;
+ ULONG ul_LowTimerValue = 0;
+ ULONG ul_HighTimerValue = 0;
+ DWORD dw_Command;
+ double d_RealLowTiming = 0;
+ double d_RealHighTiming = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***************/
+ /* Test if PWM */
+ /***************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_PWM) {
+ /**************************/
+ /* Test the PWM selection */
+ /**************************/
+
+ if (b_PWM <= 1) {
+ /******************/
+ /* Test the clock */
+ /******************/
+
+ if ((b_ClockSelection == APCI1710_30MHZ) ||
+ (b_ClockSelection == APCI1710_33MHZ) ||
+ (b_ClockSelection == APCI1710_40MHZ)) {
+ /************************/
+ /* Test the timing unit */
+ /************************/
+
+ if (b_TimingUnit <= 4) {
+ /*********************************/
+ /* Test the low timing selection */
+ /*********************************/
+
+ if (((b_ClockSelection ==
+ APCI1710_30MHZ)
+ && (b_TimingUnit
+ == 0)
+ && (ul_LowTiming
+ >= 266)
+ && (ul_LowTiming
+ <=
+ 0xFFFFFFFFUL))
+ || ((b_ClockSelection ==
+ APCI1710_30MHZ)
+ && (b_TimingUnit
+ == 1)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 571230650UL))
+ || ((b_ClockSelection ==
+ APCI1710_30MHZ)
+ && (b_TimingUnit
+ == 2)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 571230UL))
+ || ((b_ClockSelection ==
+ APCI1710_30MHZ)
+ && (b_TimingUnit
+ == 3)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 571UL))
+ || ((b_ClockSelection ==
+ APCI1710_30MHZ)
+ && (b_TimingUnit
+ == 4)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <= 9UL))
+ || ((b_ClockSelection ==
+ APCI1710_33MHZ)
+ && (b_TimingUnit
+ == 0)
+ && (ul_LowTiming
+ >= 242)
+ && (ul_LowTiming
+ <=
+ 0xFFFFFFFFUL))
+ || ((b_ClockSelection ==
+ APCI1710_33MHZ)
+ && (b_TimingUnit
+ == 1)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 519691043UL))
+ || ((b_ClockSelection ==
+ APCI1710_33MHZ)
+ && (b_TimingUnit
+ == 2)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 519691UL))
+ || ((b_ClockSelection ==
+ APCI1710_33MHZ)
+ && (b_TimingUnit
+ == 3)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 520UL))
+ || ((b_ClockSelection ==
+ APCI1710_33MHZ)
+ && (b_TimingUnit
+ == 4)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <= 8UL))
+ || ((b_ClockSelection ==
+ APCI1710_40MHZ)
+ && (b_TimingUnit
+ == 0)
+ && (ul_LowTiming
+ >= 200)
+ && (ul_LowTiming
+ <=
+ 0xFFFFFFFFUL))
+ || ((b_ClockSelection ==
+ APCI1710_40MHZ)
+ && (b_TimingUnit
+ == 1)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 429496729UL))
+ || ((b_ClockSelection ==
+ APCI1710_40MHZ)
+ && (b_TimingUnit
+ == 2)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 429496UL))
+ || ((b_ClockSelection ==
+ APCI1710_40MHZ)
+ && (b_TimingUnit
+ == 3)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 429UL))
+ || ((b_ClockSelection ==
+ APCI1710_40MHZ)
+ && (b_TimingUnit
+ == 4)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 7UL))) {
+ /**********************************/
+ /* Test the High timing selection */
+ /**********************************/
+
+ if (((b_ClockSelection == APCI1710_30MHZ) && (b_TimingUnit == 0) && (ul_HighTiming >= 266) && (ul_HighTiming <= 0xFFFFFFFFUL)) || ((b_ClockSelection == APCI1710_30MHZ) && (b_TimingUnit == 1) && (ul_HighTiming >= 1) && (ul_HighTiming <= 571230650UL)) || ((b_ClockSelection == APCI1710_30MHZ) && (b_TimingUnit == 2) && (ul_HighTiming >= 1) && (ul_HighTiming <= 571230UL)) || ((b_ClockSelection == APCI1710_30MHZ) && (b_TimingUnit == 3) && (ul_HighTiming >= 1) && (ul_HighTiming <= 571UL)) || ((b_ClockSelection == APCI1710_30MHZ) && (b_TimingUnit == 4) && (ul_HighTiming >= 1) && (ul_HighTiming <= 9UL)) || ((b_ClockSelection == APCI1710_33MHZ) && (b_TimingUnit == 0) && (ul_HighTiming >= 242) && (ul_HighTiming <= 0xFFFFFFFFUL)) || ((b_ClockSelection == APCI1710_33MHZ) && (b_TimingUnit == 1) && (ul_HighTiming >= 1) && (ul_HighTiming <= 519691043UL)) || ((b_ClockSelection == APCI1710_33MHZ) && (b_TimingUnit == 2) && (ul_HighTiming >= 1) && (ul_HighTiming <= 519691UL)) || ((b_ClockSelection == APCI1710_33MHZ) && (b_TimingUnit == 3) && (ul_HighTiming >= 1) && (ul_HighTiming <= 520UL)) || ((b_ClockSelection == APCI1710_33MHZ) && (b_TimingUnit == 4) && (ul_HighTiming >= 1) && (ul_HighTiming <= 8UL)) || ((b_ClockSelection == APCI1710_40MHZ) && (b_TimingUnit == 0) && (ul_HighTiming >= 200) && (ul_HighTiming <= 0xFFFFFFFFUL)) || ((b_ClockSelection == APCI1710_40MHZ) && (b_TimingUnit == 1) && (ul_HighTiming >= 1) && (ul_HighTiming <= 429496729UL)) || ((b_ClockSelection == APCI1710_40MHZ) && (b_TimingUnit == 2) && (ul_HighTiming >= 1) && (ul_HighTiming <= 429496UL)) || ((b_ClockSelection == APCI1710_40MHZ) && (b_TimingUnit == 3) && (ul_HighTiming >= 1) && (ul_HighTiming <= 429UL)) || ((b_ClockSelection == APCI1710_40MHZ) && (b_TimingUnit == 4) && (ul_HighTiming >= 1) && (ul_HighTiming <= 7UL))) {
+ /**************************/
+ /* Test the board version */
+ /**************************/
+
+ if (((b_ClockSelection == APCI1710_40MHZ) && (devpriv->s_BoardInfos.b_BoardVersion > 0)) || (b_ClockSelection != APCI1710_40MHZ)) {
+
+ /************************************/
+ /* Calculate the low division fator */
+ /************************************/
+
+ fpu_begin
+ ();
+
+ switch (b_TimingUnit) {
+ /******/
+ /* ns */
+ /******/
+
+ case 0:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (ul_LowTiming
+ *
+ (0.00025 * b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_LowTiming * (0.00025 * (double)b_ClockSelection)) >= ((double)((double)ul_LowTimerValue + 0.5))) {
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ *pul_RealLowTiming
+ =
+ (ULONG)
+ (ul_LowTimerValue
+ /
+ (0.00025 * (double)b_ClockSelection));
+ d_RealLowTiming
+ =
+ (double)
+ ul_LowTimerValue
+ /
+ (0.00025
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_LowTimerValue / (0.00025 * (double)b_ClockSelection)) >= (double)((double)*pul_RealLowTiming + 0.5)) {
+ *pul_RealLowTiming
+ =
+ *pul_RealLowTiming
+ +
+ 1;
+ }
+
+ ul_LowTiming
+ =
+ ul_LowTiming
+ -
+ 1;
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_LowTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /******/
+ /* æs */
+ /******/
+
+ case 1:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (ul_LowTiming
+ *
+ (0.25 * b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_LowTiming * (0.25 * (double)b_ClockSelection)) >= ((double)((double)ul_LowTimerValue + 0.5))) {
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ *pul_RealLowTiming
+ =
+ (ULONG)
+ (ul_LowTimerValue
+ /
+ (0.25 * (double)b_ClockSelection));
+ d_RealLowTiming
+ =
+ (double)
+ ul_LowTimerValue
+ /
+ (
+ (double)
+ 0.25
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_LowTimerValue / (0.25 * (double)b_ClockSelection)) >= (double)((double)*pul_RealLowTiming + 0.5)) {
+ *pul_RealLowTiming
+ =
+ *pul_RealLowTiming
+ +
+ 1;
+ }
+
+ ul_LowTiming
+ =
+ ul_LowTiming
+ -
+ 1;
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_LowTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /******/
+ /* ms */
+ /******/
+
+ case 2:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_LowTimerValue
+ =
+ ul_LowTiming
+ *
+ (250.0
+ *
+ b_ClockSelection);
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_LowTiming * (250.0 * (double)b_ClockSelection)) >= ((double)((double)ul_LowTimerValue + 0.5))) {
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ *pul_RealLowTiming
+ =
+ (ULONG)
+ (ul_LowTimerValue
+ /
+ (250.0 * (double)b_ClockSelection));
+ d_RealLowTiming
+ =
+ (double)
+ ul_LowTimerValue
+ /
+ (250.0
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_LowTimerValue / (250.0 * (double)b_ClockSelection)) >= (double)((double)*pul_RealLowTiming + 0.5)) {
+ *pul_RealLowTiming
+ =
+ *pul_RealLowTiming
+ +
+ 1;
+ }
+
+ ul_LowTiming
+ =
+ ul_LowTiming
+ -
+ 1;
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_LowTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /*****/
+ /* s */
+ /*****/
+
+ case 3:
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (ul_LowTiming
+ *
+ (250000.0
+ *
+ b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_LowTiming * (250000.0 * (double)b_ClockSelection)) >= ((double)((double)ul_LowTimerValue + 0.5))) {
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ *pul_RealLowTiming
+ =
+ (ULONG)
+ (ul_LowTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection));
+ d_RealLowTiming
+ =
+ (double)
+ ul_LowTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_LowTimerValue / (250000.0 * (double)b_ClockSelection)) >= (double)((double)*pul_RealLowTiming + 0.5)) {
+ *pul_RealLowTiming
+ =
+ *pul_RealLowTiming
+ +
+ 1;
+ }
+
+ ul_LowTiming
+ =
+ ul_LowTiming
+ -
+ 1;
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_LowTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /******/
+ /* mn */
+ /******/
+
+ case 4:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (
+ (ul_LowTiming
+ *
+ 60)
+ *
+ (250000.0
+ *
+ b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)(ul_LowTiming * 60.0) * (250000.0 * (double)b_ClockSelection)) >= ((double)((double)ul_LowTimerValue + 0.5))) {
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ *pul_RealLowTiming
+ =
+ (ULONG)
+ (ul_LowTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection))
+ /
+ 60;
+ d_RealLowTiming
+ =
+ (
+ (double)
+ ul_LowTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection))
+ /
+ 60.0;
+
+ if ((double)(((double)ul_LowTimerValue / (250000.0 * (double)b_ClockSelection)) / 60.0) >= (double)((double)*pul_RealLowTiming + 0.5)) {
+ *pul_RealLowTiming
+ =
+ *pul_RealLowTiming
+ +
+ 1;
+ }
+
+ ul_LowTiming
+ =
+ ul_LowTiming
+ -
+ 1;
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_LowTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+ }
+
+ /*************************************/
+ /* Calculate the high division fator */
+ /*************************************/
+
+ switch (b_TimingUnit) {
+ /******/
+ /* ns */
+ /******/
+
+ case 0:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (ul_HighTiming
+ *
+ (0.00025 * b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_HighTiming * (0.00025 * (double)b_ClockSelection)) >= ((double)((double)ul_HighTimerValue + 0.5))) {
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ *pul_RealHighTiming
+ =
+ (ULONG)
+ (ul_HighTimerValue
+ /
+ (0.00025 * (double)b_ClockSelection));
+ d_RealHighTiming
+ =
+ (double)
+ ul_HighTimerValue
+ /
+ (0.00025
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_HighTimerValue / (0.00025 * (double)b_ClockSelection)) >= (double)((double)*pul_RealHighTiming + 0.5)) {
+ *pul_RealHighTiming
+ =
+ *pul_RealHighTiming
+ +
+ 1;
+ }
+
+ ul_HighTiming
+ =
+ ul_HighTiming
+ -
+ 1;
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_HighTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /******/
+ /* æs */
+ /******/
+
+ case 1:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (ul_HighTiming
+ *
+ (0.25 * b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_HighTiming * (0.25 * (double)b_ClockSelection)) >= ((double)((double)ul_HighTimerValue + 0.5))) {
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ *pul_RealHighTiming
+ =
+ (ULONG)
+ (ul_HighTimerValue
+ /
+ (0.25 * (double)b_ClockSelection));
+ d_RealHighTiming
+ =
+ (double)
+ ul_HighTimerValue
+ /
+ (
+ (double)
+ 0.25
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_HighTimerValue / (0.25 * (double)b_ClockSelection)) >= (double)((double)*pul_RealHighTiming + 0.5)) {
+ *pul_RealHighTiming
+ =
+ *pul_RealHighTiming
+ +
+ 1;
+ }
+
+ ul_HighTiming
+ =
+ ul_HighTiming
+ -
+ 1;
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_HighTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /******/
+ /* ms */
+ /******/
+
+ case 2:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_HighTimerValue
+ =
+ ul_HighTiming
+ *
+ (250.0
+ *
+ b_ClockSelection);
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_HighTiming * (250.0 * (double)b_ClockSelection)) >= ((double)((double)ul_HighTimerValue + 0.5))) {
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ *pul_RealHighTiming
+ =
+ (ULONG)
+ (ul_HighTimerValue
+ /
+ (250.0 * (double)b_ClockSelection));
+ d_RealHighTiming
+ =
+ (double)
+ ul_HighTimerValue
+ /
+ (250.0
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_HighTimerValue / (250.0 * (double)b_ClockSelection)) >= (double)((double)*pul_RealHighTiming + 0.5)) {
+ *pul_RealHighTiming
+ =
+ *pul_RealHighTiming
+ +
+ 1;
+ }
+
+ ul_HighTiming
+ =
+ ul_HighTiming
+ -
+ 1;
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_HighTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /*****/
+ /* s */
+ /*****/
+
+ case 3:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (ul_HighTiming
+ *
+ (250000.0
+ *
+ b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_HighTiming * (250000.0 * (double)b_ClockSelection)) >= ((double)((double)ul_HighTimerValue + 0.5))) {
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ *pul_RealHighTiming
+ =
+ (ULONG)
+ (ul_HighTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection));
+ d_RealHighTiming
+ =
+ (double)
+ ul_HighTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_HighTimerValue / (250000.0 * (double)b_ClockSelection)) >= (double)((double)*pul_RealHighTiming + 0.5)) {
+ *pul_RealHighTiming
+ =
+ *pul_RealHighTiming
+ +
+ 1;
+ }
+
+ ul_HighTiming
+ =
+ ul_HighTiming
+ -
+ 1;
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_HighTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /******/
+ /* mn */
+ /******/
+
+ case 4:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (
+ (ul_HighTiming
+ *
+ 60)
+ *
+ (250000.0
+ *
+ b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)(ul_HighTiming * 60.0) * (250000.0 * (double)b_ClockSelection)) >= ((double)((double)ul_HighTimerValue + 0.5))) {
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ *pul_RealHighTiming
+ =
+ (ULONG)
+ (ul_HighTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection))
+ /
+ 60;
+ d_RealHighTiming
+ =
+ (
+ (double)
+ ul_HighTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection))
+ /
+ 60.0;
+
+ if ((double)(((double)ul_HighTimerValue / (250000.0 * (double)b_ClockSelection)) / 60.0) >= (double)((double)*pul_RealHighTiming + 0.5)) {
+ *pul_RealHighTiming
+ =
+ *pul_RealHighTiming
+ +
+ 1;
+ }
+
+ ul_HighTiming
+ =
+ ul_HighTiming
+ -
+ 1;
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_HighTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+ }
+
+ fpu_end();
+ /****************************/
+ /* Save the clock selection */
+ /****************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PWMModuleInfo.
+ b_ClockSelection
+ =
+ b_ClockSelection;
+
+ /************************/
+ /* Save the timing unit */
+ /************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PWMModuleInfo.
+ s_PWMInfo
+ [b_PWM].
+ b_TimingUnit
+ =
+ b_TimingUnit;
+
+ /****************************/
+ /* Save the low base timing */
+ /****************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PWMModuleInfo.
+ s_PWMInfo
+ [b_PWM].
+ d_LowTiming
+ =
+ d_RealLowTiming;
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PWMModuleInfo.
+ s_PWMInfo
+ [b_PWM].
+ ul_RealLowTiming
+ =
+ *pul_RealLowTiming;
+
+ /****************************/
+ /* Save the high base timing */
+ /****************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PWMModuleInfo.
+ s_PWMInfo
+ [b_PWM].
+ d_HighTiming
+ =
+ d_RealHighTiming;
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PWMModuleInfo.
+ s_PWMInfo
+ [b_PWM].
+ ul_RealHighTiming
+ =
+ *pul_RealHighTiming;
+
+ /************************/
+ /* Write the low timing */
+ /************************/
+
+ outl(ul_LowTimerValue, devpriv->s_BoardInfos.ui_Address + 0 + (20 * b_PWM) + (64 * b_ModulNbr));
+
+ /*************************/
+ /* Write the high timing */
+ /*************************/
+
+ outl(ul_HighTimerValue, devpriv->s_BoardInfos.ui_Address + 4 + (20 * b_PWM) + (64 * b_ModulNbr));
+
+ /***************************/
+ /* Set the clock selection */
+ /***************************/
+
+ dw_Command
+ =
+ inl
+ (devpriv->
+ s_BoardInfos.
+ ui_Address
+ +
+ 8
+ +
+ (20 * b_PWM) + (64 * b_ModulNbr));
+
+ dw_Command
+ =
+ dw_Command
+ &
+ 0x7F;
+
+ if (b_ClockSelection == APCI1710_40MHZ) {
+ dw_Command
+ =
+ dw_Command
+ |
+ 0x80;
+ }
+
+ /***************************/
+ /* Set the clock selection */
+ /***************************/
+
+ outl(dw_Command, devpriv->s_BoardInfos.ui_Address + 8 + (20 * b_PWM) + (64 * b_ModulNbr));
+
+ /*************/
+ /* PWM init. */
+ /*************/
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PWMModuleInfo.
+ s_PWMInfo
+ [b_PWM].
+ b_PWMInit
+ =
+ 1;
+ } else {
+ /***************************************************/
+ /* You can not used the 40MHz clock selection with */
+ /* this board */
+ /***************************************************/
+ DPRINTK("You can not used the 40MHz clock selection with this board\n");
+ i_ReturnValue
+ =
+ -9;
+ }
+ } else {
+ /***************************************/
+ /* High base timing selection is wrong */
+ /***************************************/
+ DPRINTK("High base timing selection is wrong\n");
+ i_ReturnValue =
+ -8;
+ }
+ } else {
+ /**************************************/
+ /* Low base timing selection is wrong */
+ /**************************************/
+ DPRINTK("Low base timing selection is wrong\n");
+ i_ReturnValue = -7;
+ }
+ } // if ((b_TimingUnit >= 0) && (b_TimingUnit <= 4))
+ else {
+ /**********************************/
+ /* Timing unit selection is wrong */
+ /**********************************/
+ DPRINTK("Timing unit selection is wrong\n");
+ i_ReturnValue = -6;
+ } // if ((b_TimingUnit >= 0) && (b_TimingUnit <= 4))
+ } // if ((b_ClockSelection == APCI1710_30MHZ) || (b_ClockSelection == APCI1710_33MHZ) || (b_ClockSelection == APCI1710_40MHZ))
+ else {
+ /*******************************/
+ /* The selected clock is wrong */
+ /*******************************/
+ DPRINTK("The selected clock is wrong\n");
+ i_ReturnValue = -5;
+ } // if ((b_ClockSelection == APCI1710_30MHZ) || (b_ClockSelection == APCI1710_33MHZ) || (b_ClockSelection == APCI1710_40MHZ))
+ } // if (b_PWM >= 0 && b_PWM <= 1)
+ else {
+ /******************************/
+ /* Tor PWM selection is wrong */
+ /******************************/
+ DPRINTK("Tor PWM selection is wrong\n");
+ i_ReturnValue = -4;
+ } // if (b_PWM >= 0 && b_PWM <= 1)
+ } else {
+ /**********************************/
+ /* The module is not a PWM module */
+ /**********************************/
+ DPRINTK("The module is not a PWM module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_GetPWMInitialisation |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_PWM, |
+| PBYTE_ pb_TimingUnit, |
+| PULONG_ pul_LowTiming, |
+| PULONG_ pul_HighTiming, |
+| PBYTE_ pb_StartLevel, |
+| PBYTE_ pb_StopMode, |
+| PBYTE_ pb_StopLevel, |
+| PBYTE_ pb_ExternGate, |
+| PBYTE_ pb_InterruptEnable, |
+| PBYTE_ pb_Enable) |
++----------------------------------------------------------------------------+
+| Task : Return the PWM (b_PWM) initialisation from selected |
+| module (b_ModulNbr). You must calling the |
+| "i_APCI1710_InitPWM" function be for you call this |
+| function. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number (0 to 3) |
+| BYTE_ b_PWM : Selected PWM (0 or 1) |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_TimingUnit : Base timing Unit (0 to 4) |
+| 0 : ns |
+| 1 : æs |
+| 2 : ms |
+| 3 : s |
+| 4 : mn |
+| PULONG_ pul_LowTiming : Low base timing value. |
+| PULONG_ pul_HighTiming : High base timing value. |
+| PBYTE_ pb_StartLevel : Start period level |
+| selection |
+| 0 : The period start |
+| with a low level |
+| 1 : The period start |
+| with a high level|
+| PBYTE_ pb_StopMode : Stop mode selection |
+| 0 : The PWM is stopped |
+| directly after the |
+| "i_APCI1710_DisablePWM"|
+| function and break the|
+| last period |
+| 1 : After the |
+| "i_APCI1710_DisablePWM"|
+| function the PWM is |
+| stopped at the end |
+| from last period cycle|
+| PBYTE_ pb_StopLevel : Stop PWM level selection |
+| 0 : The output signal |
+| keep the level after|
+| the |
+| "i_APCI1710_DisablePWM"|
+| function |
+| 1 : The output signal is|
+| set to low after the|
+| "i_APCI1710_DisablePWM"|
+| function |
+| 2 : The output signal is|
+| set to high after |
+| the |
+| "i_APCI1710_DisablePWM"|
+| function |
+| PBYTE_ pb_ExternGate : Extern gate action |
+| selection |
+| 0 : Extern gate signal |
+| not used. |
+| 1 : Extern gate signal |
+| used. |
+| PBYTE_ pb_InterruptEnable : Enable or disable the PWM |
+| interrupt. |
+| - APCI1710_ENABLE : |
+| Enable the PWM interrupt|
+| A interrupt occur after |
+| each period |
+| - APCI1710_DISABLE : |
+| Disable the PWM |
+| interrupt |
+| PBYTE_ pb_Enable : Indicate if the PWM is |
+| enabled or no |
+| 0 : PWM not enabled |
+| 1 : PWM enabled |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a PWM module |
+| -4: PWM selection is wrong |
+| -5: PWM not initialised see function |
+| "i_APCI1710_InitPWM" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_GetPWMInitialisation(struct comedi_device * dev,
+ BYTE b_ModulNbr,
+ BYTE b_PWM,
+ PBYTE pb_TimingUnit,
+ PULONG pul_LowTiming,
+ PULONG pul_HighTiming,
+ PBYTE pb_StartLevel,
+ PBYTE pb_StopMode,
+ PBYTE pb_StopLevel,
+ PBYTE pb_ExternGate, PBYTE pb_InterruptEnable, PBYTE pb_Enable)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_Status;
+ DWORD dw_Command;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***************/
+ /* Test if PWM */
+ /***************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_PWM) {
+ /**************************/
+ /* Test the PWM selection */
+ /**************************/
+
+ if (b_PWM <= 1) {
+ /***************************/
+ /* Test if PWM initialised */
+ /***************************/
+
+ dw_Status = inl(devpriv->s_BoardInfos.
+ ui_Address + 12 + (20 * b_PWM) +
+ (64 * b_ModulNbr));
+
+ if (dw_Status & 0x10) {
+ /***********************/
+ /* Read the low timing */
+ /***********************/
+
+ *pul_LowTiming =
+ inl(devpriv->s_BoardInfos.
+ ui_Address + 0 + (20 * b_PWM) +
+ (64 * b_ModulNbr));
+
+ /************************/
+ /* Read the high timing */
+ /************************/
+
+ *pul_HighTiming =
+ inl(devpriv->s_BoardInfos.
+ ui_Address + 4 + (20 * b_PWM) +
+ (64 * b_ModulNbr));
+
+ /********************/
+ /* Read the command */
+ /********************/
+
+ dw_Command = inl(devpriv->s_BoardInfos.
+ ui_Address + 8 + (20 * b_PWM) +
+ (64 * b_ModulNbr));
+
+ *pb_StartLevel =
+ (BYTE) ((dw_Command >> 5) & 1);
+ *pb_StopMode =
+ (BYTE) ((dw_Command >> 0) & 1);
+ *pb_StopLevel =
+ (BYTE) ((dw_Command >> 1) & 1);
+ *pb_ExternGate =
+ (BYTE) ((dw_Command >> 4) & 1);
+ *pb_InterruptEnable =
+ (BYTE) ((dw_Command >> 3) & 1);
+
+ if (*pb_StopLevel) {
+ *pb_StopLevel =
+ *pb_StopLevel +
+ (BYTE) ((dw_Command >>
+ 2) & 1);
+ }
+
+ /********************/
+ /* Read the command */
+ /********************/
+
+ dw_Command = inl(devpriv->s_BoardInfos.
+ ui_Address + 8 + (20 * b_PWM) +
+ (64 * b_ModulNbr));
+
+ *pb_Enable =
+ (BYTE) ((dw_Command >> 0) & 1);
+
+ *pb_TimingUnit = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_PWMModuleInfo.
+ s_PWMInfo[b_PWM].b_TimingUnit;
+ } // if (dw_Status & 0x10)
+ else {
+ /***********************/
+ /* PWM not initialised */
+ /***********************/
+ DPRINTK("PWM not initialised\n");
+ i_ReturnValue = -5;
+ } // if (dw_Status & 0x10)
+ } // if (b_PWM >= 0 && b_PWM <= 1)
+ else {
+ /******************************/
+ /* Tor PWM selection is wrong */
+ /******************************/
+ DPRINTK("Tor PWM selection is wrong\n");
+ i_ReturnValue = -4;
+ } // if (b_PWM >= 0 && b_PWM <= 1)
+ } else {
+ /**********************************/
+ /* The module is not a PWM module */
+ /**********************************/
+ DPRINTK("The module is not a PWM module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name :INT i_APCI1710_InsnWritePWM(struct comedi_device *dev,
+struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Pwm Enable Disable and Set New Timing |
++----------------------------------------------------------------------------+
+| Input Parameters :
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value :
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnWritePWM(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ BYTE b_WriteType;
+ INT i_ReturnValue = 0;
+ b_WriteType = CR_CHAN(insn->chanspec);
+
+ switch (b_WriteType) {
+ case APCI1710_PWM_ENABLE:
+ i_ReturnValue = i_APCI1710_EnablePWM(dev,
+ (BYTE) CR_AREF(insn->chanspec),
+ (BYTE) data[0],
+ (BYTE) data[1],
+ (BYTE) data[2],
+ (BYTE) data[3], (BYTE) data[4], (BYTE) data[5]);
+ break;
+
+ case APCI1710_PWM_DISABLE:
+ i_ReturnValue = i_APCI1710_DisablePWM(dev,
+ (BYTE) CR_AREF(insn->chanspec), (BYTE) data[0]);
+ break;
+
+ case APCI1710_PWM_NEWTIMING:
+ i_ReturnValue = i_APCI1710_SetNewPWMTiming(dev,
+ (BYTE) CR_AREF(insn->chanspec),
+ (BYTE) data[0],
+ (BYTE) data[1], (ULONG) data[2], (ULONG) data[3]);
+ break;
+
+ default:
+ printk("Write Config Parameter Wrong\n");
+ }
+
+ if (i_ReturnValue >= 0)
+ i_ReturnValue = insn->n;
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_EnablePWM |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_PWM, |
+| BYTE_ b_StartLevel, |
+| BYTE_ b_StopMode, |
+| BYTE_ b_StopLevel, |
+| BYTE_ b_ExternGate, |
+| BYTE_ b_InterruptEnable) |
++----------------------------------------------------------------------------+
+| Task : Enable the selected PWM (b_PWM) from selected module |
+| (b_ModulNbr). You must calling the "i_APCI1710_InitPWM"|
+| function be for you call this function. |
+| If you enable the PWM interrupt, the PWM generate a |
+| interrupt after each period. |
+| See function "i_APCI1710_SetBoardIntRoutineX" and the |
+| Interrupt mask description chapter. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number |
+| (0 to 3) |
+| BYTE_ b_PWM : Selected PWM (0 or 1) |
+| BYTE_ b_StartLevel : Start period level selection |
+| 0 : The period start with a |
+| low level |
+| 1 : The period start with a |
+| high level |
+| BYTE_ b_StopMode : Stop mode selection |
+| 0 : The PWM is stopped |
+| directly after the |
+| "i_APCI1710_DisablePWM" |
+| function and break the |
+| last period |
+| 1 : After the |
+| "i_APCI1710_DisablePWM" |
+| function the PWM is |
+| stopped at the end from|
+| last period cycle. |
+| BYTE_ b_StopLevel : Stop PWM level selection |
+| 0 : The output signal keep |
+| the level after the |
+| "i_APCI1710_DisablePWM" |
+| function |
+| 1 : The output signal is set|
+| to low after the |
+| "i_APCI1710_DisablePWM" |
+| function |
+| 2 : The output signal is set|
+| to high after the |
+| "i_APCI1710_DisablePWM" |
+| function |
+| BYTE_ b_ExternGate : Extern gate action selection |
+| 0 : Extern gate signal not |
+| used. |
+| 1 : Extern gate signal used.|
+| BYTE_ b_InterruptEnable : Enable or disable the PWM |
+| interrupt. |
+| - APCI1710_ENABLE : |
+| Enable the PWM interrupt |
+| A interrupt occur after |
+| each period |
+| - APCI1710_DISABLE : |
+| Disable the PWM interrupt |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a PWM module |
+| -4: PWM selection is wrong |
+| -5: PWM not initialised see function |
+| "i_APCI1710_InitPWM" |
+| -6: PWM start level selection is wrong |
+| -7: PWM stop mode selection is wrong |
+| -8: PWM stop level selection is wrong |
+| -9: Extern gate signal selection is wrong |
+| -10: Interrupt parameter is wrong |
+| -11: Interrupt function not initialised. |
+| See function "i_APCI1710_SetBoardIntRoutineX" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_EnablePWM(struct comedi_device * dev,
+ BYTE b_ModulNbr,
+ BYTE b_PWM,
+ BYTE b_StartLevel,
+ BYTE b_StopMode,
+ BYTE b_StopLevel, BYTE b_ExternGate, BYTE b_InterruptEnable)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_Status;
+ DWORD dw_Command;
+
+ devpriv->tsk_Current = current; // Save the current process task structure
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***************/
+ /* Test if PWM */
+ /***************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_PWM) {
+ /**************************/
+ /* Test the PWM selection */
+ /**************************/
+
+ if (b_PWM <= 1) {
+ /***************************/
+ /* Test if PWM initialised */
+ /***************************/
+
+ dw_Status = inl(devpriv->s_BoardInfos.
+ ui_Address + 12 + (20 * b_PWM) +
+ (64 * b_ModulNbr));
+
+ if (dw_Status & 0x10) {
+ /**********************************/
+ /* Test the start level selection */
+ /**********************************/
+
+ if (b_StartLevel <= 1) {
+ /**********************/
+ /* Test the stop mode */
+ /**********************/
+
+ if (b_StopMode <= 1) {
+ /***********************/
+ /* Test the stop level */
+ /***********************/
+
+ if (b_StopLevel <= 2) {
+ /*****************************/
+ /* Test the extern gate mode */
+ /*****************************/
+
+ if (b_ExternGate
+ <= 1) {
+ /*****************************/
+ /* Test the interrupt action */
+ /*****************************/
+
+ if (b_InterruptEnable == APCI1710_ENABLE || b_InterruptEnable == APCI1710_DISABLE) {
+ /******************************************/
+ /* Test if interrupt function initialised */
+ /******************************************/
+
+ /********************/
+ /* Read the command */
+ /********************/
+
+ dw_Command
+ =
+ inl
+ (devpriv->
+ s_BoardInfos.
+ ui_Address
+ +
+ 8
+ +
+ (20 * b_PWM) + (64 * b_ModulNbr));
+
+ dw_Command
+ =
+ dw_Command
+ &
+ 0x80;
+
+ /********************/
+ /* Make the command */
+ /********************/
+
+ dw_Command
+ =
+ dw_Command
+ |
+ b_StopMode
+ |
+ (b_InterruptEnable
+ <<
+ 3)
+ |
+ (b_ExternGate
+ <<
+ 4)
+ |
+ (b_StartLevel
+ <<
+ 5);
+
+ if (b_StopLevel & 3) {
+ dw_Command
+ =
+ dw_Command
+ |
+ 2;
+
+ if (b_StopLevel & 2) {
+ dw_Command
+ =
+ dw_Command
+ |
+ 4;
+ }
+ }
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PWMModuleInfo.
+ s_PWMInfo
+ [b_PWM].
+ b_InterruptEnable
+ =
+ b_InterruptEnable;
+
+ /*******************/
+ /* Set the command */
+ /*******************/
+
+ outl(dw_Command, devpriv->s_BoardInfos.ui_Address + 8 + (20 * b_PWM) + (64 * b_ModulNbr));
+
+ /******************/
+ /* Enable the PWM */
+ /******************/
+ outl(1, devpriv->s_BoardInfos.ui_Address + 12 + (20 * b_PWM) + (64 * b_ModulNbr));
+ } // if (b_InterruptEnable == APCI1710_ENABLE || b_InterruptEnable == APCI1710_DISABLE)
+ else {
+ /********************************/
+ /* Interrupt parameter is wrong */
+ /********************************/
+ DPRINTK("Interrupt parameter is wrong\n");
+ i_ReturnValue
+ =
+ -10;
+ } // if (b_InterruptEnable == APCI1710_ENABLE || b_InterruptEnable == APCI1710_DISABLE)
+ } // if (b_ExternGate >= 0 && b_ExternGate <= 1)
+ else {
+ /*****************************************/
+ /* Extern gate signal selection is wrong */
+ /*****************************************/
+ DPRINTK("Extern gate signal selection is wrong\n");
+ i_ReturnValue
+ =
+ -9;
+ } // if (b_ExternGate >= 0 && b_ExternGate <= 1)
+ } // if (b_StopLevel >= 0 && b_StopLevel <= 2)
+ else {
+ /*************************************/
+ /* PWM stop level selection is wrong */
+ /*************************************/
+ DPRINTK("PWM stop level selection is wrong\n");
+ i_ReturnValue =
+ -8;
+ } // if (b_StopLevel >= 0 && b_StopLevel <= 2)
+ } // if (b_StopMode >= 0 && b_StopMode <= 1)
+ else {
+ /************************************/
+ /* PWM stop mode selection is wrong */
+ /************************************/
+ DPRINTK("PWM stop mode selection is wrong\n");
+ i_ReturnValue = -7;
+ } // if (b_StopMode >= 0 && b_StopMode <= 1)
+ } // if (b_StartLevel >= 0 && b_StartLevel <= 1)
+ else {
+ /**************************************/
+ /* PWM start level selection is wrong */
+ /**************************************/
+ DPRINTK("PWM start level selection is wrong\n");
+ i_ReturnValue = -6;
+ } // if (b_StartLevel >= 0 && b_StartLevel <= 1)
+ } // if (dw_Status & 0x10)
+ else {
+ /***********************/
+ /* PWM not initialised */
+ /***********************/
+ DPRINTK("PWM not initialised\n");
+ i_ReturnValue = -5;
+ } // if (dw_Status & 0x10)
+ } // if (b_PWM >= 0 && b_PWM <= 1)
+ else {
+ /******************************/
+ /* Tor PWM selection is wrong */
+ /******************************/
+ DPRINTK("Tor PWM selection is wrong\n");
+ i_ReturnValue = -4;
+ } // if (b_PWM >= 0 && b_PWM <= 1)
+ } else {
+ /**********************************/
+ /* The module is not a PWM module */
+ /**********************************/
+ DPRINTK("The module is not a PWM module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_DisablePWM (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_PWM) |
++----------------------------------------------------------------------------+
+| Task : Disable the selected PWM (b_PWM) from selected module |
+| (b_ModulNbr). The output signal level depend of the |
+| initialisation by the "i_APCI1710_EnablePWM". |
+| See the b_StartLevel, b_StopMode and b_StopLevel |
+| parameters from this function. |
++----------------------------------------------------------------------------+
+| Input Parameters :BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number (0 to 3) |
+| BYTE_ b_PWM : Selected PWM (0 or 1) |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a PWM module |
+| -4: PWM selection is wrong |
+| -5: PWM not initialised see function |
+| "i_APCI1710_InitPWM" |
+| -6: PWM not enabled see function |
+| "i_APCI1710_EnablePWM" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_DisablePWM(struct comedi_device * dev, BYTE b_ModulNbr, BYTE b_PWM)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_Status;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***************/
+ /* Test if PWM */
+ /***************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_PWM) {
+ /**************************/
+ /* Test the PWM selection */
+ /**************************/
+
+ if (b_PWM <= 1) {
+ /***************************/
+ /* Test if PWM initialised */
+ /***************************/
+
+ dw_Status = inl(devpriv->s_BoardInfos.
+ ui_Address + 12 + (20 * b_PWM) +
+ (64 * b_ModulNbr));
+
+ if (dw_Status & 0x10) {
+ /***********************/
+ /* Test if PWM enabled */
+ /***********************/
+
+ if (dw_Status & 0x1) {
+ /*******************/
+ /* Disable the PWM */
+ /*******************/
+ outl(0, devpriv->s_BoardInfos.
+ ui_Address + 12 +
+ (20 * b_PWM) +
+ (64 * b_ModulNbr));
+ } // if (dw_Status & 0x1)
+ else {
+ /*******************/
+ /* PWM not enabled */
+ /*******************/
+ DPRINTK("PWM not enabled\n");
+ i_ReturnValue = -6;
+ } // if (dw_Status & 0x1)
+ } // if (dw_Status & 0x10)
+ else {
+ /***********************/
+ /* PWM not initialised */
+ /***********************/
+ DPRINTK(" PWM not initialised\n");
+ i_ReturnValue = -5;
+ } // if (dw_Status & 0x10)
+ } // if (b_PWM >= 0 && b_PWM <= 1)
+ else {
+ /******************************/
+ /* Tor PWM selection is wrong */
+ /******************************/
+ DPRINTK("Tor PWM selection is wrong\n");
+ i_ReturnValue = -4;
+ } // if (b_PWM >= 0 && b_PWM <= 1)
+ } else {
+ /**********************************/
+ /* The module is not a PWM module */
+ /**********************************/
+ DPRINTK("The module is not a PWM module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_SetNewPWMTiming |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_PWM, |
+| BYTE_ b_ClockSelection, |
+| BYTE_ b_TimingUnit, |
+| ULONG_ ul_LowTiming, |
+| ULONG_ ul_HighTiming) |
++----------------------------------------------------------------------------+
+| Task : Set a new timing. The ul_LowTiming, ul_HighTiming and |
+| ul_TimingUnit determine the low/high timing base for |
+| the period. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Module number to configure|
+| (0 to 3) |
+| BYTE_ b_PWM : Selected PWM (0 or 1). |
+| BYTE_ b_TimingUnit : Base timing Unit (0 to 4) |
+| 0 : ns |
+| 1 : æs |
+| 2 : ms |
+| 3 : s |
+| 4 : mn |
+| ULONG_ ul_LowTiming : Low base timing value. |
+| ULONG_ ul_HighTiming : High base timing value. |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a PWM module |
+| -4: PWM selection is wrong |
+| -5: PWM not initialised |
+| -6: Timing Unit selection is wrong |
+| -7: Low base timing selection is wrong |
+| -8: High base timing selection is wrong |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_SetNewPWMTiming(struct comedi_device * dev,
+ BYTE b_ModulNbr,
+ BYTE b_PWM, BYTE b_TimingUnit, ULONG ul_LowTiming, ULONG ul_HighTiming)
+{
+ BYTE b_ClockSelection;
+ INT i_ReturnValue = 0;
+ ULONG ul_LowTimerValue = 0;
+ ULONG ul_HighTimerValue = 0;
+ ULONG ul_RealLowTiming = 0;
+ ULONG ul_RealHighTiming = 0;
+ DWORD dw_Status;
+ DWORD dw_Command;
+ double d_RealLowTiming = 0;
+ double d_RealHighTiming = 0;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***************/
+ /* Test if PWM */
+ /***************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_PWM) {
+ /**************************/
+ /* Test the PWM selection */
+ /**************************/
+
+ if (b_PWM <= 1) {
+ /***************************/
+ /* Test if PWM initialised */
+ /***************************/
+
+ dw_Status = inl(devpriv->s_BoardInfos.
+ ui_Address + 12 + (20 * b_PWM) +
+ (64 * b_ModulNbr));
+
+ if (dw_Status & 0x10) {
+ b_ClockSelection = devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_PWMModuleInfo.
+ b_ClockSelection;
+
+ /************************/
+ /* Test the timing unit */
+ /************************/
+
+ if (b_TimingUnit <= 4) {
+ /*********************************/
+ /* Test the low timing selection */
+ /*********************************/
+
+ if (((b_ClockSelection ==
+ APCI1710_30MHZ)
+ && (b_TimingUnit
+ == 0)
+ && (ul_LowTiming
+ >= 266)
+ && (ul_LowTiming
+ <=
+ 0xFFFFFFFFUL))
+ || ((b_ClockSelection ==
+ APCI1710_30MHZ)
+ && (b_TimingUnit
+ == 1)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 571230650UL))
+ || ((b_ClockSelection ==
+ APCI1710_30MHZ)
+ && (b_TimingUnit
+ == 2)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 571230UL))
+ || ((b_ClockSelection ==
+ APCI1710_30MHZ)
+ && (b_TimingUnit
+ == 3)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 571UL))
+ || ((b_ClockSelection ==
+ APCI1710_30MHZ)
+ && (b_TimingUnit
+ == 4)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <= 9UL))
+ || ((b_ClockSelection ==
+ APCI1710_33MHZ)
+ && (b_TimingUnit
+ == 0)
+ && (ul_LowTiming
+ >= 242)
+ && (ul_LowTiming
+ <=
+ 0xFFFFFFFFUL))
+ || ((b_ClockSelection ==
+ APCI1710_33MHZ)
+ && (b_TimingUnit
+ == 1)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 519691043UL))
+ || ((b_ClockSelection ==
+ APCI1710_33MHZ)
+ && (b_TimingUnit
+ == 2)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 519691UL))
+ || ((b_ClockSelection ==
+ APCI1710_33MHZ)
+ && (b_TimingUnit
+ == 3)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 520UL))
+ || ((b_ClockSelection ==
+ APCI1710_33MHZ)
+ && (b_TimingUnit
+ == 4)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <= 8UL))
+ || ((b_ClockSelection ==
+ APCI1710_40MHZ)
+ && (b_TimingUnit
+ == 0)
+ && (ul_LowTiming
+ >= 200)
+ && (ul_LowTiming
+ <=
+ 0xFFFFFFFFUL))
+ || ((b_ClockSelection ==
+ APCI1710_40MHZ)
+ && (b_TimingUnit
+ == 1)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 429496729UL))
+ || ((b_ClockSelection ==
+ APCI1710_40MHZ)
+ && (b_TimingUnit
+ == 2)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 429496UL))
+ || ((b_ClockSelection ==
+ APCI1710_40MHZ)
+ && (b_TimingUnit
+ == 3)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 429UL))
+ || ((b_ClockSelection ==
+ APCI1710_40MHZ)
+ && (b_TimingUnit
+ == 4)
+ && (ul_LowTiming
+ >= 1)
+ && (ul_LowTiming
+ <=
+ 7UL))) {
+ /**********************************/
+ /* Test the High timing selection */
+ /**********************************/
+
+ if (((b_ClockSelection == APCI1710_30MHZ) && (b_TimingUnit == 0) && (ul_HighTiming >= 266) && (ul_HighTiming <= 0xFFFFFFFFUL)) || ((b_ClockSelection == APCI1710_30MHZ) && (b_TimingUnit == 1) && (ul_HighTiming >= 1) && (ul_HighTiming <= 571230650UL)) || ((b_ClockSelection == APCI1710_30MHZ) && (b_TimingUnit == 2) && (ul_HighTiming >= 1) && (ul_HighTiming <= 571230UL)) || ((b_ClockSelection == APCI1710_30MHZ) && (b_TimingUnit == 3) && (ul_HighTiming >= 1) && (ul_HighTiming <= 571UL)) || ((b_ClockSelection == APCI1710_30MHZ) && (b_TimingUnit == 4) && (ul_HighTiming >= 1) && (ul_HighTiming <= 9UL)) || ((b_ClockSelection == APCI1710_33MHZ) && (b_TimingUnit == 0) && (ul_HighTiming >= 242) && (ul_HighTiming <= 0xFFFFFFFFUL)) || ((b_ClockSelection == APCI1710_33MHZ) && (b_TimingUnit == 1) && (ul_HighTiming >= 1) && (ul_HighTiming <= 519691043UL)) || ((b_ClockSelection == APCI1710_33MHZ) && (b_TimingUnit == 2) && (ul_HighTiming >= 1) && (ul_HighTiming <= 519691UL)) || ((b_ClockSelection == APCI1710_33MHZ) && (b_TimingUnit == 3) && (ul_HighTiming >= 1) && (ul_HighTiming <= 520UL)) || ((b_ClockSelection == APCI1710_33MHZ) && (b_TimingUnit == 4) && (ul_HighTiming >= 1) && (ul_HighTiming <= 8UL)) || ((b_ClockSelection == APCI1710_40MHZ) && (b_TimingUnit == 0) && (ul_HighTiming >= 200) && (ul_HighTiming <= 0xFFFFFFFFUL)) || ((b_ClockSelection == APCI1710_40MHZ) && (b_TimingUnit == 1) && (ul_HighTiming >= 1) && (ul_HighTiming <= 429496729UL)) || ((b_ClockSelection == APCI1710_40MHZ) && (b_TimingUnit == 2) && (ul_HighTiming >= 1) && (ul_HighTiming <= 429496UL)) || ((b_ClockSelection == APCI1710_40MHZ) && (b_TimingUnit == 3) && (ul_HighTiming >= 1) && (ul_HighTiming <= 429UL)) || ((b_ClockSelection == APCI1710_40MHZ) && (b_TimingUnit == 4) && (ul_HighTiming >= 1) && (ul_HighTiming <= 7UL))) {
+ /************************************/
+ /* Calculate the low division fator */
+ /************************************/
+
+ fpu_begin();
+ switch (b_TimingUnit) {
+ /******/
+ /* ns */
+ /******/
+
+ case 0:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (ul_LowTiming
+ *
+ (0.00025 * b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_LowTiming * (0.00025 * (double)b_ClockSelection)) >= ((double)((double)ul_LowTimerValue + 0.5))) {
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealLowTiming
+ =
+ (ULONG)
+ (ul_LowTimerValue
+ /
+ (0.00025 * (double)b_ClockSelection));
+ d_RealLowTiming
+ =
+ (double)
+ ul_LowTimerValue
+ /
+ (0.00025
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_LowTimerValue / (0.00025 * (double)b_ClockSelection)) >= (double)((double)ul_RealLowTiming + 0.5)) {
+ ul_RealLowTiming
+ =
+ ul_RealLowTiming
+ +
+ 1;
+ }
+
+ ul_LowTiming
+ =
+ ul_LowTiming
+ -
+ 1;
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_LowTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /******/
+ /* æs */
+ /******/
+
+ case 1:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (ul_LowTiming
+ *
+ (0.25 * b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_LowTiming * (0.25 * (double)b_ClockSelection)) >= ((double)((double)ul_LowTimerValue + 0.5))) {
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealLowTiming
+ =
+ (ULONG)
+ (ul_LowTimerValue
+ /
+ (0.25 * (double)b_ClockSelection));
+ d_RealLowTiming
+ =
+ (double)
+ ul_LowTimerValue
+ /
+ (
+ (double)
+ 0.25
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_LowTimerValue / (0.25 * (double)b_ClockSelection)) >= (double)((double)ul_RealLowTiming + 0.5)) {
+ ul_RealLowTiming
+ =
+ ul_RealLowTiming
+ +
+ 1;
+ }
+
+ ul_LowTiming
+ =
+ ul_LowTiming
+ -
+ 1;
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_LowTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /******/
+ /* ms */
+ /******/
+
+ case 2:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_LowTimerValue
+ =
+ ul_LowTiming
+ *
+ (250.0
+ *
+ b_ClockSelection);
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_LowTiming * (250.0 * (double)b_ClockSelection)) >= ((double)((double)ul_LowTimerValue + 0.5))) {
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealLowTiming
+ =
+ (ULONG)
+ (ul_LowTimerValue
+ /
+ (250.0 * (double)b_ClockSelection));
+ d_RealLowTiming
+ =
+ (double)
+ ul_LowTimerValue
+ /
+ (250.0
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_LowTimerValue / (250.0 * (double)b_ClockSelection)) >= (double)((double)ul_RealLowTiming + 0.5)) {
+ ul_RealLowTiming
+ =
+ ul_RealLowTiming
+ +
+ 1;
+ }
+
+ ul_LowTiming
+ =
+ ul_LowTiming
+ -
+ 1;
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_LowTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /*****/
+ /* s */
+ /*****/
+
+ case 3:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (ul_LowTiming
+ *
+ (250000.0
+ *
+ b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_LowTiming * (250000.0 * (double)b_ClockSelection)) >= ((double)((double)ul_LowTimerValue + 0.5))) {
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealLowTiming
+ =
+ (ULONG)
+ (ul_LowTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection));
+ d_RealLowTiming
+ =
+ (double)
+ ul_LowTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_LowTimerValue / (250000.0 * (double)b_ClockSelection)) >= (double)((double)ul_RealLowTiming + 0.5)) {
+ ul_RealLowTiming
+ =
+ ul_RealLowTiming
+ +
+ 1;
+ }
+
+ ul_LowTiming
+ =
+ ul_LowTiming
+ -
+ 1;
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_LowTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /******/
+ /* mn */
+ /******/
+
+ case 4:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (
+ (ul_LowTiming
+ *
+ 60)
+ *
+ (250000.0
+ *
+ b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)(ul_LowTiming * 60.0) * (250000.0 * (double)b_ClockSelection)) >= ((double)((double)ul_LowTimerValue + 0.5))) {
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealLowTiming
+ =
+ (ULONG)
+ (ul_LowTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection))
+ /
+ 60;
+ d_RealLowTiming
+ =
+ (
+ (double)
+ ul_LowTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection))
+ /
+ 60.0;
+
+ if ((double)(((double)ul_LowTimerValue / (250000.0 * (double)b_ClockSelection)) / 60.0) >= (double)((double)ul_RealLowTiming + 0.5)) {
+ ul_RealLowTiming
+ =
+ ul_RealLowTiming
+ +
+ 1;
+ }
+
+ ul_LowTiming
+ =
+ ul_LowTiming
+ -
+ 1;
+ ul_LowTimerValue
+ =
+ ul_LowTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_LowTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_LowTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+ }
+
+ /*************************************/
+ /* Calculate the high division fator */
+ /*************************************/
+
+ switch (b_TimingUnit) {
+ /******/
+ /* ns */
+ /******/
+
+ case 0:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (ul_HighTiming
+ *
+ (0.00025 * b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_HighTiming * (0.00025 * (double)b_ClockSelection)) >= ((double)((double)ul_HighTimerValue + 0.5))) {
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealHighTiming
+ =
+ (ULONG)
+ (ul_HighTimerValue
+ /
+ (0.00025 * (double)b_ClockSelection));
+ d_RealHighTiming
+ =
+ (double)
+ ul_HighTimerValue
+ /
+ (0.00025
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_HighTimerValue / (0.00025 * (double)b_ClockSelection)) >= (double)((double)ul_RealHighTiming + 0.5)) {
+ ul_RealHighTiming
+ =
+ ul_RealHighTiming
+ +
+ 1;
+ }
+
+ ul_HighTiming
+ =
+ ul_HighTiming
+ -
+ 1;
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_HighTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /******/
+ /* æs */
+ /******/
+
+ case 1:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (ul_HighTiming
+ *
+ (0.25 * b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_HighTiming * (0.25 * (double)b_ClockSelection)) >= ((double)((double)ul_HighTimerValue + 0.5))) {
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealHighTiming
+ =
+ (ULONG)
+ (ul_HighTimerValue
+ /
+ (0.25 * (double)b_ClockSelection));
+ d_RealHighTiming
+ =
+ (double)
+ ul_HighTimerValue
+ /
+ (
+ (double)
+ 0.25
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_HighTimerValue / (0.25 * (double)b_ClockSelection)) >= (double)((double)ul_RealHighTiming + 0.5)) {
+ ul_RealHighTiming
+ =
+ ul_RealHighTiming
+ +
+ 1;
+ }
+
+ ul_HighTiming
+ =
+ ul_HighTiming
+ -
+ 1;
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_HighTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /******/
+ /* ms */
+ /******/
+
+ case 2:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_HighTimerValue
+ =
+ ul_HighTiming
+ *
+ (250.0
+ *
+ b_ClockSelection);
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_HighTiming * (250.0 * (double)b_ClockSelection)) >= ((double)((double)ul_HighTimerValue + 0.5))) {
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealHighTiming
+ =
+ (ULONG)
+ (ul_HighTimerValue
+ /
+ (250.0 * (double)b_ClockSelection));
+ d_RealHighTiming
+ =
+ (double)
+ ul_HighTimerValue
+ /
+ (250.0
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_HighTimerValue / (250.0 * (double)b_ClockSelection)) >= (double)((double)ul_RealHighTiming + 0.5)) {
+ ul_RealHighTiming
+ =
+ ul_RealHighTiming
+ +
+ 1;
+ }
+
+ ul_HighTiming
+ =
+ ul_HighTiming
+ -
+ 1;
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_HighTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /*****/
+ /* s */
+ /*****/
+
+ case 3:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (ul_HighTiming
+ *
+ (250000.0
+ *
+ b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_HighTiming * (250000.0 * (double)b_ClockSelection)) >= ((double)((double)ul_HighTimerValue + 0.5))) {
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealHighTiming
+ =
+ (ULONG)
+ (ul_HighTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection));
+ d_RealHighTiming
+ =
+ (double)
+ ul_HighTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection);
+
+ if ((double)((double)ul_HighTimerValue / (250000.0 * (double)b_ClockSelection)) >= (double)((double)ul_RealHighTiming + 0.5)) {
+ ul_RealHighTiming
+ =
+ ul_RealHighTiming
+ +
+ 1;
+ }
+
+ ul_HighTiming
+ =
+ ul_HighTiming
+ -
+ 1;
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_HighTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /******/
+ /* mn */
+ /******/
+
+ case 4:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (
+ (ul_HighTiming
+ *
+ 60)
+ *
+ (250000.0
+ *
+ b_ClockSelection));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)(ul_HighTiming * 60.0) * (250000.0 * (double)b_ClockSelection)) >= ((double)((double)ul_HighTimerValue + 0.5))) {
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealHighTiming
+ =
+ (ULONG)
+ (ul_HighTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection))
+ /
+ 60;
+ d_RealHighTiming
+ =
+ (
+ (double)
+ ul_HighTimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_ClockSelection))
+ /
+ 60.0;
+
+ if ((double)(((double)ul_HighTimerValue / (250000.0 * (double)b_ClockSelection)) / 60.0) >= (double)((double)ul_RealHighTiming + 0.5)) {
+ ul_RealHighTiming
+ =
+ ul_RealHighTiming
+ +
+ 1;
+ }
+
+ ul_HighTiming
+ =
+ ul_HighTiming
+ -
+ 1;
+ ul_HighTimerValue
+ =
+ ul_HighTimerValue
+ -
+ 2;
+
+ if (b_ClockSelection != APCI1710_40MHZ) {
+ ul_HighTimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_HighTimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+ }
+
+ fpu_end();
+
+ /************************/
+ /* Save the timing unit */
+ /************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PWMModuleInfo.
+ s_PWMInfo
+ [b_PWM].
+ b_TimingUnit
+ =
+ b_TimingUnit;
+
+ /****************************/
+ /* Save the low base timing */
+ /****************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PWMModuleInfo.
+ s_PWMInfo
+ [b_PWM].
+ d_LowTiming
+ =
+ d_RealLowTiming;
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PWMModuleInfo.
+ s_PWMInfo
+ [b_PWM].
+ ul_RealLowTiming
+ =
+ ul_RealLowTiming;
+
+ /****************************/
+ /* Save the high base timing */
+ /****************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PWMModuleInfo.
+ s_PWMInfo
+ [b_PWM].
+ d_HighTiming
+ =
+ d_RealHighTiming;
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_PWMModuleInfo.
+ s_PWMInfo
+ [b_PWM].
+ ul_RealHighTiming
+ =
+ ul_RealHighTiming;
+
+ /************************/
+ /* Write the low timing */
+ /************************/
+
+ outl(ul_LowTimerValue, devpriv->s_BoardInfos.ui_Address + 0 + (20 * b_PWM) + (64 * b_ModulNbr));
+
+ /*************************/
+ /* Write the high timing */
+ /*************************/
+
+ outl(ul_HighTimerValue, devpriv->s_BoardInfos.ui_Address + 4 + (20 * b_PWM) + (64 * b_ModulNbr));
+
+ /***************************/
+ /* Set the clock selection */
+ /***************************/
+
+ dw_Command =
+ inl
+ (devpriv->
+ s_BoardInfos.
+ ui_Address
+ + 8 +
+ (20 * b_PWM) + (64 * b_ModulNbr));
+
+ dw_Command =
+ dw_Command
+ & 0x7F;
+
+ if (b_ClockSelection == APCI1710_40MHZ) {
+ dw_Command
+ =
+ dw_Command
+ |
+ 0x80;
+ }
+
+ /***************************/
+ /* Set the clock selection */
+ /***************************/
+
+ outl(dw_Command,
+ devpriv->
+ s_BoardInfos.
+ ui_Address
+ + 8 +
+ (20 * b_PWM) + (64 * b_ModulNbr));
+ } else {
+ /***************************************/
+ /* High base timing selection is wrong */
+ /***************************************/
+ DPRINTK("High base timing selection is wrong\n");
+ i_ReturnValue =
+ -8;
+ }
+ } else {
+ /**************************************/
+ /* Low base timing selection is wrong */
+ /**************************************/
+ DPRINTK("Low base timing selection is wrong\n");
+ i_ReturnValue = -7;
+ }
+ } // if ((b_TimingUnit >= 0) && (b_TimingUnit <= 4))
+ else {
+ /**********************************/
+ /* Timing unit selection is wrong */
+ /**********************************/
+ DPRINTK("Timing unit selection is wrong\n");
+ i_ReturnValue = -6;
+ } // if ((b_TimingUnit >= 0) && (b_TimingUnit <= 4))
+ } // if (dw_Status & 0x10)
+ else {
+ /***********************/
+ /* PWM not initialised */
+ /***********************/
+ DPRINTK("PWM not initialised\n");
+ i_ReturnValue = -5;
+ } // if (dw_Status & 0x10)
+ } // if (b_PWM >= 0 && b_PWM <= 1)
+ else {
+ /******************************/
+ /* Tor PWM selection is wrong */
+ /******************************/
+ DPRINTK("Tor PWM selection is wrong\n");
+ i_ReturnValue = -4;
+ } // if (b_PWM >= 0 && b_PWM <= 1)
+ } else {
+ /**********************************/
+ /* The module is not a PWM module */
+ /**********************************/
+ DPRINTK("The module is not a PWM module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_GetPWMStatus |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_PWM, |
+| PBYTE_ pb_PWMOutputStatus, |
+| PBYTE_ pb_ExternGateStatus) |
++----------------------------------------------------------------------------+
+| Task : Return the status from selected PWM (b_PWM) from |
+| selected module (b_ModulNbr). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_PWM : Selected PWM (0 or 1) |
+| BYTE_ b_ModulNbr : Selected module number (0 to 3)
+ b_ModulNbr =(BYTE) CR_AREF(insn->chanspec);
+ b_PWM =(BYTE) data[0];
+
+ |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_PWMOutputStatus : Return the PWM output |
+| level status. |
+| 0 : The PWM output level|
+| is low. |
+| 1 : The PWM output level|
+| is high. |
+| PBYTE_ pb_ExternGateStatus : Return the extern gate |
+| level status. |
+| 0 : The extern gate is |
+| low. |
+| 1 : The extern gate is |
+| high.
+ pb_PWMOutputStatus =(PBYTE) data[0];
+ pb_ExternGateStatus =(PBYTE) data[1]; |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a PWM module |
+| -4: PWM selection is wrong |
+| -5: PWM not initialised see function |
+| "i_APCI1710_InitPWM" |
+| -6: PWM not enabled see function "i_APCI1710_EnablePWM"|
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnReadGetPWMStatus(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_Status;
+
+ BYTE b_ModulNbr;
+ BYTE b_PWM;
+ PBYTE pb_PWMOutputStatus;
+ PBYTE pb_ExternGateStatus;
+
+ i_ReturnValue = insn->n;
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_PWM = (BYTE) CR_CHAN(insn->chanspec);
+ pb_PWMOutputStatus = (PBYTE) & data[0];
+ pb_ExternGateStatus = (PBYTE) & data[1];
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***************/
+ /* Test if PWM */
+ /***************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_PWM) {
+ /**************************/
+ /* Test the PWM selection */
+ /**************************/
+
+ if (b_PWM <= 1) {
+ /***************************/
+ /* Test if PWM initialised */
+ /***************************/
+
+ dw_Status = inl(devpriv->s_BoardInfos.
+ ui_Address + 12 + (20 * b_PWM) +
+ (64 * b_ModulNbr));
+
+ if (dw_Status & 0x10) {
+ /***********************/
+ /* Test if PWM enabled */
+ /***********************/
+
+ if (dw_Status & 0x1) {
+ *pb_PWMOutputStatus =
+ (BYTE) ((dw_Status >> 7)
+ & 1);
+ *pb_ExternGateStatus =
+ (BYTE) ((dw_Status >> 6)
+ & 1);
+ } // if (dw_Status & 0x1)
+ else {
+ /*******************/
+ /* PWM not enabled */
+ /*******************/
+
+ DPRINTK("PWM not enabled \n");
+ i_ReturnValue = -6;
+ } // if (dw_Status & 0x1)
+ } // if (dw_Status & 0x10)
+ else {
+ /***********************/
+ /* PWM not initialised */
+ /***********************/
+
+ DPRINTK("PWM not initialised\n");
+ i_ReturnValue = -5;
+ } // if (dw_Status & 0x10)
+ } // if (b_PWM >= 0 && b_PWM <= 1)
+ else {
+ /******************************/
+ /* Tor PWM selection is wrong */
+ /******************************/
+
+ DPRINTK("Tor PWM selection is wrong\n");
+ i_ReturnValue = -4;
+ } // if (b_PWM >= 0 && b_PWM <= 1)
+ } else {
+ /**********************************/
+ /* The module is not a PWM module */
+ /**********************************/
+
+ DPRINTK("The module is not a PWM module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+INT i_APCI1710_InsnBitsReadPWMInterrupt(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.ui_Read].b_OldModuleMask;
+ data[1] = devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.ui_Read].ul_OldInterruptMask;
+ data[2] = devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.ui_Read].ul_OldCounterLatchValue;
+
+ /**************************/
+ /* Increment the read FIFO */
+ /***************************/
+
+ devpriv->
+ s_InterruptParameters.
+ ui_Read = (devpriv->
+ s_InterruptParameters.ui_Read + 1) % APCI1710_SAVE_INTERRUPT;
+
+ return insn->n;
+
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_Pwm.h b/drivers/staging/comedi/drivers/addi-data/APCI1710_Pwm.h
new file mode 100644
index 000000000000..c1b7f4ca47c3
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_Pwm.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+#define APCI1710_30MHZ 30
+#define APCI1710_33MHZ 33
+#define APCI1710_40MHZ 40
+
+#define APCI1710_PWM_INIT 0
+#define APCI1710_PWM_GETINITDATA 1
+
+#define APCI1710_PWM_DISABLE 0
+#define APCI1710_PWM_ENABLE 1
+#define APCI1710_PWM_NEWTIMING 2
+
+INT i_APCI1710_InsnConfigPWM(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+INT i_APCI1710_InitPWM(struct comedi_device *dev,
+ BYTE b_ModulNbr,
+ BYTE b_PWM,
+ BYTE b_ClockSelection,
+ BYTE b_TimingUnit,
+ ULONG ul_LowTiming,
+ ULONG ul_HighTiming,
+ PULONG pul_RealLowTiming, PULONG pul_RealHighTiming);
+
+INT i_APCI1710_GetPWMInitialisation(struct comedi_device *dev,
+ BYTE b_ModulNbr,
+ BYTE b_PWM,
+ PBYTE pb_TimingUnit,
+ PULONG pul_LowTiming,
+ PULONG pul_HighTiming,
+ PBYTE pb_StartLevel,
+ PBYTE pb_StopMode,
+ PBYTE pb_StopLevel,
+ PBYTE pb_ExternGate,
+ PBYTE pb_InterruptEnable, PBYTE pb_Enable);
+
+INT i_APCI1710_InsnWritePWM(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+INT i_APCI1710_EnablePWM(struct comedi_device *dev,
+ BYTE b_ModulNbr,
+ BYTE b_PWM,
+ BYTE b_StartLevel,
+ BYTE b_StopMode,
+ BYTE b_StopLevel, BYTE b_ExternGate,
+ BYTE b_InterruptEnable);
+
+INT i_APCI1710_SetNewPWMTiming(struct comedi_device *dev,
+ BYTE b_ModulNbr,
+ BYTE b_PWM, BYTE b_TimingUnit,
+ ULONG ul_LowTiming, ULONG ul_HighTiming);
+
+INT i_APCI1710_DisablePWM(struct comedi_device *dev, BYTE b_ModulNbr, BYTE b_PWM);
+
+INT i_APCI1710_InsnReadGetPWMStatus(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+INT i_APCI1710_InsnBitsReadPWMInterrupt(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_Ssi.c b/drivers/staging/comedi/drivers/addi-data/APCI1710_Ssi.c
new file mode 100644
index 000000000000..bb6e1622e379
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_Ssi.c
@@ -0,0 +1,848 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-----------------------------------------------------------------------+
+ | Project : API APCI1710 | Compiler : gcc |
+ | Module name : SSI.C | Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-----------------------------------------------------------------------+
+ | Description : APCI-1710 SSI counter module |
+ | |
+ | |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +-----------------------------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | 13/05/98 | S. Weber | SSI digital input / output implementation |
+ |----------|-----------|------------------------------------------------|
+ | 22/03/00 | C.Guinot | 0100/0226 -> 0200/0227 |
+ | | | Änderung in InitSSI Funktion |
+ | | | b_SSIProfile >= 2 anstatt b_SSIProfile > 2 |
+ | | | |
+ +-----------------------------------------------------------------------+
+ | 08/05/00 | Guinot C | - 0400/0228 All Function in RING 0 |
+ | | | available |
+ +-----------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+
+#include "APCI1710_Ssi.h"
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_InitSSI |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_SSIProfile, |
+| BYTE_ b_PositionTurnLength, |
+| BYTE_ b_TurnCptLength, |
+| BYTE_ b_PCIInputClock, |
+| ULONG_ ul_SSIOutputClock, |
+| BYTE_ b_SSICountingMode) |
++----------------------------------------------------------------------------+
+| Task : Configure the SSI operating mode from selected module |
+| (b_ModulNbr). You must calling this function be for you|
+| call any other function witch access of SSI. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710|
+| BYTE_ b_ModulNbr : Module number to |
+| configure (0 to 3) |
+| BYTE_ b_SSIProfile : Selection from SSI |
+| profile length (2 to 32).|
+| BYTE_ b_PositionTurnLength : Selection from SSI |
+| position data length |
+| (1 to 31). |
+| BYTE_ b_TurnCptLength : Selection from SSI turn |
+| counter data length |
+| (1 to 31). |
+| BYTE b_PCIInputClock : Selection from PCI bus |
+| clock |
+| - APCI1710_30MHZ : |
+| The PC have a PCI bus |
+| clock from 30 MHz |
+| - APCI1710_33MHZ : |
+| The PC have a PCI bus |
+| clock from 33 MHz |
+| ULONG_ ul_SSIOutputClock : Selection from SSI output|
+| clock. |
+| From 229 to 5 000 000 Hz|
+| for 30 MHz selection. |
+| From 252 to 5 000 000 Hz|
+| for 33 MHz selection. |
+| BYTE b_SSICountingMode : SSI counting mode |
+| selection |
+| - APCI1710_BINARY_MODE : |
+| Binary counting mode. |
+| - APCI1710_GRAY_MODE : |
+| Gray counting mode.
+
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ b_SSIProfile = (BYTE) data[0];
+ b_PositionTurnLength= (BYTE) data[1];
+ b_TurnCptLength = (BYTE) data[2];
+ b_PCIInputClock = (BYTE) data[3];
+ ul_SSIOutputClock = (ULONG) data[4];
+ b_SSICountingMode = (BYTE) data[5]; |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The module parameter is wrong |
+| -3: The module is not a SSI module |
+| -4: The selected SSI profile length is wrong |
+| -5: The selected SSI position data length is wrong |
+| -6: The selected SSI turn counter data length is wrong |
+| -7: The selected PCI input clock is wrong |
+| -8: The selected SSI output clock is wrong |
+| -9: The selected SSI counting mode parameter is wrong |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnConfigInitSSI(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ UINT ui_TimerValue;
+ BYTE b_ModulNbr, b_SSIProfile, b_PositionTurnLength, b_TurnCptLength,
+ b_PCIInputClock, b_SSICountingMode;
+ ULONG ul_SSIOutputClock;
+
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ b_SSIProfile = (BYTE) data[0];
+ b_PositionTurnLength = (BYTE) data[1];
+ b_TurnCptLength = (BYTE) data[2];
+ b_PCIInputClock = (BYTE) data[3];
+ ul_SSIOutputClock = (ULONG) data[4];
+ b_SSICountingMode = (BYTE) data[5];
+
+ i_ReturnValue = insn->n;
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***********************/
+ /* Test if SSI counter */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_SSI_COUNTER) {
+ /*******************************/
+ /* Test the SSI profile length */
+ /*******************************/
+
+ // CG 22/03/00 b_SSIProfile >= 2 anstatt b_SSIProfile > 2
+ if (b_SSIProfile >= 2 && b_SSIProfile < 33) {
+ /*************************************/
+ /* Test the SSI position data length */
+ /*************************************/
+
+ if (b_PositionTurnLength > 0
+ && b_PositionTurnLength < 32) {
+ /*****************************************/
+ /* Test the SSI turn counter data length */
+ /*****************************************/
+
+ if (b_TurnCptLength > 0
+ && b_TurnCptLength < 32) {
+ /***************************/
+ /* Test the profile length */
+ /***************************/
+
+ if ((b_TurnCptLength +
+ b_PositionTurnLength)
+ <= b_SSIProfile) {
+ /****************************/
+ /* Test the PCI input clock */
+ /****************************/
+
+ if (b_PCIInputClock ==
+ APCI1710_30MHZ
+ ||
+ b_PCIInputClock
+ ==
+ APCI1710_33MHZ)
+ {
+ /*************************/
+ /* Test the output clock */
+ /*************************/
+
+ if ((b_PCIInputClock == APCI1710_30MHZ && (ul_SSIOutputClock > 228 && ul_SSIOutputClock <= 5000000UL)) || (b_PCIInputClock == APCI1710_33MHZ && (ul_SSIOutputClock > 251 && ul_SSIOutputClock <= 5000000UL))) {
+ if (b_SSICountingMode == APCI1710_BINARY_MODE || b_SSICountingMode == APCI1710_GRAY_MODE) {
+ /**********************/
+ /* Save configuration */
+ /**********************/
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SSICounterInfo.
+ b_SSIProfile
+ =
+ b_SSIProfile;
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SSICounterInfo.
+ b_PositionTurnLength
+ =
+ b_PositionTurnLength;
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SSICounterInfo.
+ b_TurnCptLength
+ =
+ b_TurnCptLength;
+
+ /*********************************/
+ /* Initialise the profile length */
+ /*********************************/
+
+ if (b_SSICountingMode == APCI1710_BINARY_MODE) {
+
+ outl(b_SSIProfile + 1, devpriv->s_BoardInfos.ui_Address + 4 + (64 * b_ModulNbr));
+ } else {
+
+ outl(b_SSIProfile, devpriv->s_BoardInfos.ui_Address + 4 + (64 * b_ModulNbr));
+ }
+
+ /******************************/
+ /* Calculate the output clock */
+ /******************************/
+
+ ui_TimerValue
+ =
+ (UINT)
+ (
+ ((ULONG) (b_PCIInputClock) * 500000UL) / ul_SSIOutputClock);
+
+ /************************/
+ /* Initialise the timer */
+ /************************/
+
+ outl(ui_TimerValue, devpriv->s_BoardInfos.ui_Address + (64 * b_ModulNbr));
+
+ /********************************/
+ /* Initialise the counting mode */
+ /********************************/
+
+ outl(7 * b_SSICountingMode, devpriv->s_BoardInfos.ui_Address + 12 + (64 * b_ModulNbr));
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SSICounterInfo.
+ b_SSIInit
+ =
+ 1;
+ } else {
+ /*****************************************************/
+ /* The selected SSI counting mode parameter is wrong */
+ /*****************************************************/
+
+ DPRINTK("The selected SSI counting mode parameter is wrong\n");
+ i_ReturnValue
+ =
+ -9;
+ }
+ } else {
+ /******************************************/
+ /* The selected SSI output clock is wrong */
+ /******************************************/
+
+ DPRINTK("The selected SSI output clock is wrong\n");
+ i_ReturnValue
+ =
+ -8;
+ }
+ } else {
+ /*****************************************/
+ /* The selected PCI input clock is wrong */
+ /*****************************************/
+
+ DPRINTK("The selected PCI input clock is wrong\n");
+ i_ReturnValue =
+ -7;
+ }
+ } else {
+ /********************************************/
+ /* The selected SSI profile length is wrong */
+ /********************************************/
+
+ DPRINTK("The selected SSI profile length is wrong\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /******************************************************/
+ /* The selected SSI turn counter data length is wrong */
+ /******************************************************/
+
+ DPRINTK("The selected SSI turn counter data length is wrong\n");
+ i_ReturnValue = -6;
+ }
+ } else {
+ /**************************************************/
+ /* The selected SSI position data length is wrong */
+ /**************************************************/
+
+ DPRINTK("The selected SSI position data length is wrong\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /********************************************/
+ /* The selected SSI profile length is wrong */
+ /********************************************/
+
+ DPRINTK("The selected SSI profile length is wrong\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /**********************************/
+ /* The module is not a SSI module */
+ /**********************************/
+
+ DPRINTK("The module is not a SSI module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_Read1SSIValue |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_SelectedSSI, |
+| PULONG_ pul_Position, |
+| PULONG_ pul_TurnCpt)
+ INT i_APCI1710_ReadSSIValue(struct comedi_device *dev,struct comedi_subdevice *s,
+ struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task :
+
+
+ Read the selected SSI counter (b_SelectedSSI) from |
+| selected module (b_ModulNbr).
+ or Read all SSI counter (b_SelectedSSI) from |
+| selected module (b_ModulNbr). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710|
+| BYTE_ b_ModulNbr : Module number to |
+| configure (0 to 3) |
+| BYTE_ b_SelectedSSI : Selection from SSI |
+| counter (0 to 2)
+
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_SelectedSSI = (BYTE) CR_CHAN(insn->chanspec); (in case of single ssi)
+ b_ReadType = (BYTE) CR_RANGE(insn->chanspec);
+|
++----------------------------------------------------------------------------+
+| Output Parameters : PULONG_ pul_Position : SSI position in the turn |
+| PULONG_ pul_TurnCpt : Number of turns
+
+pul_Position = (PULONG) &data[0];
+ pul_TurnCpt = (PULONG) &data[1]; |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The module parameter is wrong |
+| -3: The module is not a SSI module |
+| -4: SSI not initialised see function |
+| "i_APCI1710_InitSSI" |
+| -5: The selected SSI is wrong |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnReadSSIValue(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ BYTE b_Cpt;
+ BYTE b_Length;
+ BYTE b_Schift;
+ BYTE b_SSICpt;
+ DWORD dw_And;
+ DWORD dw_And1;
+ DWORD dw_And2;
+ DWORD dw_StatusReg;
+ DWORD dw_CounterValue;
+ BYTE b_ModulNbr;
+ BYTE b_SelectedSSI;
+ BYTE b_ReadType;
+ PULONG pul_Position;
+ PULONG pul_TurnCpt;
+ PULONG pul_Position1;
+ PULONG pul_TurnCpt1;
+
+ i_ReturnValue = insn->n;
+ pul_Position1 = (PULONG) & data[0];
+// For Read1
+ pul_TurnCpt1 = (PULONG) & data[1];
+// For Read all
+ pul_Position = (PULONG) & data[0]; //0-2
+ pul_TurnCpt = (PULONG) & data[3]; //3-5
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_SelectedSSI = (BYTE) CR_CHAN(insn->chanspec);
+ b_ReadType = (BYTE) CR_RANGE(insn->chanspec);
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***********************/
+ /* Test if SSI counter */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_SSI_COUNTER) {
+ /***************************/
+ /* Test if SSI initialised */
+ /***************************/
+
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_SSICounterInfo.b_SSIInit == 1) {
+
+ switch (b_ReadType) {
+
+ case APCI1710_SSI_READ1VALUE:
+ /****************************************/
+ /* Test the selected SSI counter number */
+ /****************************************/
+
+ if (b_SelectedSSI < 3) {
+ /************************/
+ /* Start the conversion */
+ /************************/
+
+ outl(0, devpriv->s_BoardInfos.
+ ui_Address + 8 +
+ (64 * b_ModulNbr));
+
+ do {
+ /*******************/
+ /* Read the status */
+ /*******************/
+
+ dw_StatusReg =
+ inl(devpriv->
+ s_BoardInfos.
+ ui_Address +
+ (64 * b_ModulNbr));
+ }
+ while ((dw_StatusReg & 0x1) !=
+ 0);
+
+ /******************************/
+ /* Read the SSI counter value */
+ /******************************/
+
+ dw_CounterValue =
+ inl(devpriv->
+ s_BoardInfos.
+ ui_Address + 4 +
+ (b_SelectedSSI * 4) +
+ (64 * b_ModulNbr));
+
+ b_Length =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SSICounterInfo.
+ b_SSIProfile / 2;
+
+ if ((b_Length * 2) !=
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SSICounterInfo.
+ b_SSIProfile) {
+ b_Length++;
+ }
+
+ b_Schift =
+ b_Length -
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SSICounterInfo.
+ b_PositionTurnLength;
+
+ *pul_Position1 =
+ dw_CounterValue >>
+ b_Schift;
+
+ dw_And = 1;
+
+ for (b_Cpt = 0;
+ b_Cpt <
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SSICounterInfo.
+ b_PositionTurnLength;
+ b_Cpt++) {
+ dw_And = dw_And * 2;
+ }
+
+ *pul_Position1 =
+ *pul_Position1 &
+ ((dw_And) - 1);
+
+ *pul_TurnCpt1 =
+ dw_CounterValue >>
+ b_Length;
+
+ dw_And = 1;
+
+ for (b_Cpt = 0;
+ b_Cpt <
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SSICounterInfo.
+ b_TurnCptLength;
+ b_Cpt++) {
+ dw_And = dw_And * 2;
+ }
+
+ *pul_TurnCpt1 =
+ *pul_TurnCpt1 &
+ ((dw_And) - 1);
+ } else {
+ /*****************************/
+ /* The selected SSI is wrong */
+ /*****************************/
+
+ DPRINTK("The selected SSI is wrong\n");
+ i_ReturnValue = -5;
+ }
+ break;
+
+ case APCI1710_SSI_READALLVALUE:
+ dw_And1 = 1;
+
+ for (b_Cpt = 0;
+ b_Cpt <
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SSICounterInfo.
+ b_PositionTurnLength; b_Cpt++) {
+ dw_And1 = dw_And1 * 2;
+ }
+
+ dw_And2 = 1;
+
+ for (b_Cpt = 0;
+ b_Cpt <
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_SSICounterInfo.
+ b_TurnCptLength; b_Cpt++) {
+ dw_And2 = dw_And2 * 2;
+ }
+
+ /************************/
+ /* Start the conversion */
+ /************************/
+
+ outl(0, devpriv->s_BoardInfos.
+ ui_Address + 8 +
+ (64 * b_ModulNbr));
+
+ do {
+ /*******************/
+ /* Read the status */
+ /*******************/
+
+ dw_StatusReg =
+ inl(devpriv->
+ s_BoardInfos.
+ ui_Address +
+ (64 * b_ModulNbr));
+ }
+ while ((dw_StatusReg & 0x1) != 0);
+
+ for (b_SSICpt = 0; b_SSICpt < 3;
+ b_SSICpt++) {
+ /******************************/
+ /* Read the SSI counter value */
+ /******************************/
+
+ dw_CounterValue =
+ inl(devpriv->
+ s_BoardInfos.
+ ui_Address + 4 +
+ (b_SSICpt * 4) +
+ (64 * b_ModulNbr));
+
+ b_Length =
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SSICounterInfo.
+ b_SSIProfile / 2;
+
+ if ((b_Length * 2) !=
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SSICounterInfo.
+ b_SSIProfile) {
+ b_Length++;
+ }
+
+ b_Schift =
+ b_Length -
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_SSICounterInfo.
+ b_PositionTurnLength;
+
+ pul_Position[b_SSICpt] =
+ dw_CounterValue >>
+ b_Schift;
+ pul_Position[b_SSICpt] =
+ pul_Position[b_SSICpt] &
+ ((dw_And1) - 1);
+
+ pul_TurnCpt[b_SSICpt] =
+ dw_CounterValue >>
+ b_Length;
+ pul_TurnCpt[b_SSICpt] =
+ pul_TurnCpt[b_SSICpt] &
+ ((dw_And2) - 1);
+ }
+ break;
+
+ default:
+ printk("Read Type Inputs Wrong\n");
+
+ } // switch ending
+
+ } else {
+ /***********************/
+ /* SSI not initialised */
+ /***********************/
+
+ DPRINTK("SSI not initialised\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /**********************************/
+ /* The module is not a SSI module */
+ /**********************************/
+
+ DPRINTK("The module is not a SSI module\n");
+ i_ReturnValue = -3;
+
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_ReadSSI1DigitalInput |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_InputChannel, |
+| PBYTE_ pb_ChannelStatus) |
++----------------------------------------------------------------------------+
+| Task :
+ (0) Set the digital output from selected SSI moule |
+| (b_ModuleNbr) ON
+ (1) Set the digital output from selected SSI moule |
+| (b_ModuleNbr) OFF
+ (2)Read the status from selected SSI digital input |
+| (b_InputChannel)
+ (3)Read the status from all SSI digital inputs from |
+| selected SSI module (b_ModulNbr) |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710|
+| BYTE_ b_ModulNbr CR_AREF : Module number to |
+| configure (0 to 3) |
+| BYTE_ b_InputChannel CR_CHAN : Selection from digital |
+| data[0] which IOTYPE input ( 0 to 2) |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_ChannelStatus : Digital input channel |
+| data[0] status |
+| 0 : Channle is not active|
+| 1 : Channle is active |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The module parameter is wrong |
+| -3: The module is not a SSI module |
+| -4: The selected SSI digital input is wrong |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnBitsSSIDigitalIO(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_StatusReg;
+ BYTE b_ModulNbr;
+ BYTE b_InputChannel;
+ PBYTE pb_ChannelStatus;
+ PBYTE pb_InputStatus;
+ BYTE b_IOType;
+ i_ReturnValue = insn->n;
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_IOType = (BYTE) data[0];
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***********************/
+ /* Test if SSI counter */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_SSI_COUNTER) {
+ switch (b_IOType) {
+ case APCI1710_SSI_SET_CHANNELON:
+ /*****************************/
+ /* Set the digital output ON */
+ /*****************************/
+
+ outl(1, devpriv->s_BoardInfos.ui_Address + 16 +
+ (64 * b_ModulNbr));
+ break;
+
+ case APCI1710_SSI_SET_CHANNELOFF:
+ /******************************/
+ /* Set the digital output OFF */
+ /******************************/
+
+ outl(0, devpriv->s_BoardInfos.ui_Address + 16 +
+ (64 * b_ModulNbr));
+ break;
+
+ case APCI1710_SSI_READ_1CHANNEL:
+ /******************************************/
+ /* Test the digital imnput channel number */
+ /******************************************/
+
+ b_InputChannel = (BYTE) CR_CHAN(insn->chanspec);
+ pb_ChannelStatus = (PBYTE) & data[0];
+
+ if (b_InputChannel <= 2) {
+ /**************************/
+ /* Read all digital input */
+ /**************************/
+
+ dw_StatusReg =
+ inl(devpriv->s_BoardInfos.
+ ui_Address + (64 * b_ModulNbr));
+ *pb_ChannelStatus =
+ (BYTE) (((~dw_StatusReg) >> (4 +
+ b_InputChannel))
+ & 1);
+ } else {
+ /********************************/
+ /* Selected digital input error */
+ /********************************/
+
+ DPRINTK("Selected digital input error\n");
+ i_ReturnValue = -4;
+ }
+ break;
+
+ case APCI1710_SSI_READ_ALLCHANNEL:
+ /**************************/
+ /* Read all digital input */
+ /**************************/
+ pb_InputStatus = (PBYTE) & data[0];
+
+ dw_StatusReg =
+ inl(devpriv->s_BoardInfos.ui_Address +
+ (64 * b_ModulNbr));
+ *pb_InputStatus =
+ (BYTE) (((~dw_StatusReg) >> 4) & 7);
+ break;
+
+ default:
+ printk("IO type wrong\n");
+
+ } //switch end
+ } else {
+ /**********************************/
+ /* The module is not a SSI module */
+ /**********************************/
+
+ DPRINTK("The module is not a SSI module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_Ssi.h b/drivers/staging/comedi/drivers/addi-data/APCI1710_Ssi.h
new file mode 100644
index 000000000000..88daad1a3912
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_Ssi.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+#define APCI1710_30MHZ 30
+#define APCI1710_33MHZ 33
+#define APCI1710_40MHZ 40
+
+#define APCI1710_BINARY_MODE 0x1
+#define APCI1710_GRAY_MODE 0x0
+
+#define APCI1710_SSI_READ1VALUE 1
+#define APCI1710_SSI_READALLVALUE 2
+
+#define APCI1710_SSI_SET_CHANNELON 0
+#define APCI1710_SSI_SET_CHANNELOFF 1
+#define APCI1710_SSI_READ_1CHANNEL 2
+#define APCI1710_SSI_READ_ALLCHANNEL 3
+
+/*
+ * SSI INISIALISATION FUNCTION
+ */
+INT i_APCI1710_InsnConfigInitSSI(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+INT i_APCI1710_InsnReadSSIValue(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+INT i_APCI1710_InsnBitsSSIDigitalIO(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_Tor.c b/drivers/staging/comedi/drivers/addi-data/APCI1710_Tor.c
new file mode 100644
index 000000000000..90316e10a3bd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_Tor.c
@@ -0,0 +1,2049 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-----------------------------------------------------------------------+
+ | Project : API APCI1710 | Compiler : gcc |
+ | Module name : TOR.C | Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-----------------------------------------------------------------------+
+ | Description : APCI-1710 tor counter module |
+ | |
+ | |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +-----------------------------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | 27/01/99 | S. Weber | 40 MHz implementation |
+ +-----------------------------------------------------------------------+
+ | 28/04/00 | S. Weber | Simple,double and quadruple mode implementation|
+ | | | Extern clock implementation |
+ +-----------------------------------------------------------------------+
+ | 08/05/00 | Guinot C | - 0400/0228 All Function in RING 0 |
+ | | | available |
+ +-----------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+
+#include "APCI1710_Tor.h"
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_InitTorCounter |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_TorCounter, |
+| BYTE_ b_PCIInputClock, |
+| BYTE_ b_TimingUnit, |
+| ULONG_ ul_TimingInterval, |
+| PULONG_ pul_RealTimingInterval) |
++----------------------------------------------------------------------------+
+| Task : Configure the selected tor counter (b_TorCounter) |
+| from selected module (b_ModulNbr). |
+| The ul_TimingInterval and ul_TimingUnit determine the |
+| timing base for the measurement. |
+| The pul_RealTimingInterval return the real timing |
+| value. You must calling this function be for you call |
+| any other function witch access of the tor counter. |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : |
+|
+ CR_AREF BYTE_ b_ModulNbr : Module number to configure |
+| (0 to 3) |
+| data[0] BYTE_ b_TorCounter : Tor counter selection |
+| (0 or 1). |
+| data[1] BYTE_ b_PCIInputClock : Selection from PCI bus clock|
+| - APCI1710_30MHZ : |
+| The PC have a PCI bus |
+| clock from 30 MHz |
+| - APCI1710_33MHZ : |
+| The PC have a PCI bus |
+| clock from 33 MHz |
+| - APCI1710_40MHZ |
+| The APCI-1710 have a |
+| integrated 40Mhz |
+| quartz. |
+| - APCI1710_GATE_INPUT |
+| Used the gate input for |
+| the base clock. If you |
+| have selected this option,|
+| than it is not possibl to |
+| used the gate input for |
+| enabled the acquisition |
+| data[2] BYTE_ b_TimingUnit : Base timing unit (0 to 4) |
+| 0 : ns |
+| 1 : µs |
+| 2 : ms |
+| 3 : s |
+| 4 : mn |
+| data[3] ULONG_ ul_TimingInterval : Base timing value. |
++----------------------------------------------------------------------------+
+| Output Parameters : PULONG_ pul_RealTimingInterval : Real base timing |
+| data[0] value. |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a tor counter module |
+| -4: Tor counter selection is wrong |
+| -5: The selected PCI input clock is wrong |
+| -6: Timing unit selection is wrong |
+| -7: Base timing selection is wrong |
+| -8: You can not used the 40MHz clock selection wich |
+| this board |
+| -9: You can not used the 40MHz clock selection wich |
+| this TOR version |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnConfigInitTorCounter(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ ULONG ul_TimerValue = 0;
+ DWORD dw_Command;
+ double d_RealTimingInterval = 0;
+ BYTE b_ModulNbr;
+ BYTE b_TorCounter;
+ BYTE b_PCIInputClock;
+ BYTE b_TimingUnit;
+ ULONG ul_TimingInterval;
+ ULONG ul_RealTimingInterval = 0;
+
+ i_ReturnValue = insn->n;
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+
+ b_TorCounter = (BYTE) data[0];
+ b_PCIInputClock = (BYTE) data[1];
+ b_TimingUnit = (BYTE) data[2];
+ ul_TimingInterval = (ULONG) data[3];
+ printk("INPUT clock %d\n", b_PCIInputClock);
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***********************/
+ /* Test if tor counter */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_TOR_COUNTER) {
+ /**********************************/
+ /* Test the tor counter selection */
+ /**********************************/
+
+ if (b_TorCounter <= 1) {
+ /**************************/
+ /* Test the PCI bus clock */
+ /**************************/
+
+ if ((b_PCIInputClock == APCI1710_30MHZ) ||
+ (b_PCIInputClock == APCI1710_33MHZ) ||
+ (b_PCIInputClock == APCI1710_40MHZ) ||
+ (b_PCIInputClock ==
+ APCI1710_GATE_INPUT)) {
+ /************************/
+ /* Test the timing unit */
+ /************************/
+
+ if ((b_TimingUnit <= 4)
+ || (b_PCIInputClock ==
+ APCI1710_GATE_INPUT)) {
+ /**********************************/
+ /* Test the base timing selection */
+ /**********************************/
+
+ if (((b_PCIInputClock == APCI1710_30MHZ) && (b_TimingUnit == 0) && (ul_TimingInterval >= 133) && (ul_TimingInterval <= 0xFFFFFFFFUL)) || ((b_PCIInputClock == APCI1710_30MHZ) && (b_TimingUnit == 1) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 571230650UL)) || ((b_PCIInputClock == APCI1710_30MHZ) && (b_TimingUnit == 2) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 571230UL)) || ((b_PCIInputClock == APCI1710_30MHZ) && (b_TimingUnit == 3) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 571UL)) || ((b_PCIInputClock == APCI1710_30MHZ) && (b_TimingUnit == 4) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 9UL)) || ((b_PCIInputClock == APCI1710_33MHZ) && (b_TimingUnit == 0) && (ul_TimingInterval >= 121) && (ul_TimingInterval <= 0xFFFFFFFFUL)) || ((b_PCIInputClock == APCI1710_33MHZ) && (b_TimingUnit == 1) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 519691043UL)) || ((b_PCIInputClock == APCI1710_33MHZ) && (b_TimingUnit == 2) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 519691UL)) || ((b_PCIInputClock == APCI1710_33MHZ) && (b_TimingUnit == 3) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 520UL)) || ((b_PCIInputClock == APCI1710_33MHZ) && (b_TimingUnit == 4) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 8UL)) || ((b_PCIInputClock == APCI1710_40MHZ) && (b_TimingUnit == 0) && (ul_TimingInterval >= 100) && (ul_TimingInterval <= 0xFFFFFFFFUL)) || ((b_PCIInputClock == APCI1710_40MHZ) && (b_TimingUnit == 1) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 429496729UL)) || ((b_PCIInputClock == APCI1710_40MHZ) && (b_TimingUnit == 2) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 429496UL)) || ((b_PCIInputClock == APCI1710_40MHZ) && (b_TimingUnit == 3) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 429UL)) || ((b_PCIInputClock == APCI1710_40MHZ) && (b_TimingUnit == 4) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 7UL)) || ((b_PCIInputClock == APCI1710_GATE_INPUT) && (ul_TimingInterval >= 2))) {
+ /**************************/
+ /* Test the board version */
+ /**************************/
+
+ if (((b_PCIInputClock == APCI1710_40MHZ) && (devpriv->s_BoardInfos.b_BoardVersion > 0)) || (b_PCIInputClock != APCI1710_40MHZ)) {
+ /************************/
+ /* Test the TOR version */
+ /************************/
+
+ if (((b_PCIInputClock == APCI1710_40MHZ) && ((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF) >= 0x3131)) || ((b_PCIInputClock == APCI1710_GATE_INPUT) && ((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF) >= 0x3132)) || (b_PCIInputClock == APCI1710_30MHZ) || (b_PCIInputClock == APCI1710_33MHZ)) {
+ /*********************************/
+ /* Test if not extern clock used */
+ /*********************************/
+
+ if (b_PCIInputClock != APCI1710_GATE_INPUT) {
+ fpu_begin
+ ();
+ /****************************************/
+ /* Calculate the timer 0 division fator */
+ /****************************************/
+
+ switch (b_TimingUnit) {
+ /******/
+ /* ns */
+ /******/
+
+ case 0:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_TimerValue
+ =
+ (ULONG)
+ (ul_TimingInterval
+ *
+ (0.00025 * b_PCIInputClock));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_TimingInterval * (0.00025 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
+ ul_TimerValue
+ =
+ ul_TimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealTimingInterval
+ =
+ (ULONG)
+ (ul_TimerValue
+ /
+ (0.00025 * (double)b_PCIInputClock));
+ d_RealTimingInterval
+ =
+ (double)
+ ul_TimerValue
+ /
+ (0.00025
+ *
+ (double)
+ b_PCIInputClock);
+
+ if ((double)((double)ul_TimerValue / (0.00025 * (double)b_PCIInputClock)) >= (double)((double)ul_RealTimingInterval + 0.5)) {
+ ul_RealTimingInterval
+ =
+ ul_RealTimingInterval
+ +
+ 1;
+ }
+
+ ul_TimingInterval
+ =
+ ul_TimingInterval
+ -
+ 1;
+ ul_TimerValue
+ =
+ ul_TimerValue
+ -
+ 2;
+
+ if (b_PCIInputClock != APCI1710_40MHZ) {
+ ul_TimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_TimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /******/
+ /* æs */
+ /******/
+
+ case 1:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_TimerValue
+ =
+ (ULONG)
+ (ul_TimingInterval
+ *
+ (0.25 * b_PCIInputClock));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_TimingInterval * (0.25 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
+ ul_TimerValue
+ =
+ ul_TimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealTimingInterval
+ =
+ (ULONG)
+ (ul_TimerValue
+ /
+ (0.25 * (double)b_PCIInputClock));
+ d_RealTimingInterval
+ =
+ (double)
+ ul_TimerValue
+ /
+ (
+ (double)
+ 0.25
+ *
+ (double)
+ b_PCIInputClock);
+
+ if ((double)((double)ul_TimerValue / (0.25 * (double)b_PCIInputClock)) >= (double)((double)ul_RealTimingInterval + 0.5)) {
+ ul_RealTimingInterval
+ =
+ ul_RealTimingInterval
+ +
+ 1;
+ }
+
+ ul_TimingInterval
+ =
+ ul_TimingInterval
+ -
+ 1;
+ ul_TimerValue
+ =
+ ul_TimerValue
+ -
+ 2;
+
+ if (b_PCIInputClock != APCI1710_40MHZ) {
+ ul_TimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_TimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /******/
+ /* ms */
+ /******/
+
+ case 2:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_TimerValue
+ =
+ ul_TimingInterval
+ *
+ (250.0
+ *
+ b_PCIInputClock);
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_TimingInterval * (250.0 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
+ ul_TimerValue
+ =
+ ul_TimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealTimingInterval
+ =
+ (ULONG)
+ (ul_TimerValue
+ /
+ (250.0 * (double)b_PCIInputClock));
+ d_RealTimingInterval
+ =
+ (double)
+ ul_TimerValue
+ /
+ (250.0
+ *
+ (double)
+ b_PCIInputClock);
+
+ if ((double)((double)ul_TimerValue / (250.0 * (double)b_PCIInputClock)) >= (double)((double)ul_RealTimingInterval + 0.5)) {
+ ul_RealTimingInterval
+ =
+ ul_RealTimingInterval
+ +
+ 1;
+ }
+
+ ul_TimingInterval
+ =
+ ul_TimingInterval
+ -
+ 1;
+ ul_TimerValue
+ =
+ ul_TimerValue
+ -
+ 2;
+
+ if (b_PCIInputClock != APCI1710_40MHZ) {
+ ul_TimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_TimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /*****/
+ /* s */
+ /*****/
+
+ case 3:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_TimerValue
+ =
+ (ULONG)
+ (ul_TimingInterval
+ *
+ (250000.0
+ *
+ b_PCIInputClock));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)ul_TimingInterval * (250000.0 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
+ ul_TimerValue
+ =
+ ul_TimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealTimingInterval
+ =
+ (ULONG)
+ (ul_TimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_PCIInputClock));
+ d_RealTimingInterval
+ =
+ (double)
+ ul_TimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_PCIInputClock);
+
+ if ((double)((double)ul_TimerValue / (250000.0 * (double)b_PCIInputClock)) >= (double)((double)ul_RealTimingInterval + 0.5)) {
+ ul_RealTimingInterval
+ =
+ ul_RealTimingInterval
+ +
+ 1;
+ }
+
+ ul_TimingInterval
+ =
+ ul_TimingInterval
+ -
+ 1;
+ ul_TimerValue
+ =
+ ul_TimerValue
+ -
+ 2;
+
+ if (b_PCIInputClock != APCI1710_40MHZ) {
+ ul_TimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_TimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+
+ /******/
+ /* mn */
+ /******/
+
+ case 4:
+
+ /******************/
+ /* Timer 0 factor */
+ /******************/
+
+ ul_TimerValue
+ =
+ (ULONG)
+ (
+ (ul_TimingInterval
+ *
+ 60)
+ *
+ (250000.0
+ *
+ b_PCIInputClock));
+
+ /*******************/
+ /* Round the value */
+ /*******************/
+
+ if ((double)((double)(ul_TimingInterval * 60.0) * (250000.0 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
+ ul_TimerValue
+ =
+ ul_TimerValue
+ +
+ 1;
+ }
+
+ /*****************************/
+ /* Calculate the real timing */
+ /*****************************/
+
+ ul_RealTimingInterval
+ =
+ (ULONG)
+ (ul_TimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_PCIInputClock))
+ /
+ 60;
+ d_RealTimingInterval
+ =
+ (
+ (double)
+ ul_TimerValue
+ /
+ (250000.0
+ *
+ (double)
+ b_PCIInputClock))
+ /
+ 60.0;
+
+ if ((double)(((double)ul_TimerValue / (250000.0 * (double)b_PCIInputClock)) / 60.0) >= (double)((double)ul_RealTimingInterval + 0.5)) {
+ ul_RealTimingInterval
+ =
+ ul_RealTimingInterval
+ +
+ 1;
+ }
+
+ ul_TimingInterval
+ =
+ ul_TimingInterval
+ -
+ 1;
+ ul_TimerValue
+ =
+ ul_TimerValue
+ -
+ 2;
+
+ if (b_PCIInputClock != APCI1710_40MHZ) {
+ ul_TimerValue
+ =
+ (ULONG)
+ (
+ (double)
+ (ul_TimerValue)
+ *
+ 1.007752288);
+ }
+
+ break;
+ }
+
+ fpu_end();
+ } // if (b_PCIInputClock != APCI1710_GATE_INPUT)
+ else {
+ /*************************************************************/
+ /* 2 Clock used for the overflow and the reload from counter */
+ /*************************************************************/
+
+ ul_TimerValue
+ =
+ ul_TimingInterval
+ -
+ 2;
+ } // if (b_PCIInputClock != APCI1710_GATE_INPUT)
+
+ /****************************/
+ /* Save the PCI input clock */
+ /****************************/
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TorCounterModuleInfo.
+ b_PCIInputClock
+ =
+ b_PCIInputClock;
+
+ /************************/
+ /* Save the timing unit */
+ /************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TorCounterModuleInfo.
+ s_TorCounterInfo
+ [b_TorCounter].
+ b_TimingUnit
+ =
+ b_TimingUnit;
+
+ /************************/
+ /* Save the base timing */
+ /************************/
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TorCounterModuleInfo.
+ s_TorCounterInfo
+ [b_TorCounter].
+ d_TimingInterval
+ =
+ d_RealTimingInterval;
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TorCounterModuleInfo.
+ s_TorCounterInfo
+ [b_TorCounter].
+ ul_RealTimingInterval
+ =
+ ul_RealTimingInterval;
+
+ /*******************/
+ /* Get the command */
+ /*******************/
+
+ dw_Command
+ =
+ inl
+ (devpriv->
+ s_BoardInfos.
+ ui_Address
+ +
+ 4
+ +
+ (16 * b_TorCounter) + (64 * b_ModulNbr));
+
+ dw_Command
+ =
+ (dw_Command
+ >>
+ 4)
+ &
+ 0xF;
+
+ /******************/
+ /* Test if 40 MHz */
+ /******************/
+
+ if (b_PCIInputClock == APCI1710_40MHZ) {
+ /****************************/
+ /* Set the 40 MHz selection */
+ /****************************/
+
+ dw_Command
+ =
+ dw_Command
+ |
+ 0x10;
+ }
+
+ /*****************************/
+ /* Test if extern clock used */
+ /*****************************/
+
+ if (b_PCIInputClock == APCI1710_GATE_INPUT) {
+ /****************************/
+ /* Set the 40 MHz selection */
+ /****************************/
+
+ dw_Command
+ =
+ dw_Command
+ |
+ 0x20;
+ }
+
+ /*************************/
+ /* Write the new command */
+ /*************************/
+
+ outl(dw_Command, devpriv->s_BoardInfos.ui_Address + 4 + (16 * b_TorCounter) + (64 * b_ModulNbr));
+
+ /*******************/
+ /* Disable the tor */
+ /*******************/
+
+ outl(0, devpriv->s_BoardInfos.ui_Address + 8 + (16 * b_TorCounter) + (64 * b_ModulNbr));
+ /*************************/
+ /* Set the timer 1 value */
+ /*************************/
+
+ outl(ul_TimerValue, devpriv->s_BoardInfos.ui_Address + 0 + (16 * b_TorCounter) + (64 * b_ModulNbr));
+
+ /*********************/
+ /* Tor counter init. */
+ /*********************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TorCounterModuleInfo.
+ s_TorCounterInfo
+ [b_TorCounter].
+ b_TorCounterInit
+ =
+ 1;
+ } else {
+ /***********************************************/
+ /* TOR version error for 40MHz clock selection */
+ /***********************************************/
+
+ DPRINTK("TOR version error for 40MHz clock selection\n");
+ i_ReturnValue
+ =
+ -9;
+ }
+ } else {
+ /**************************************************************/
+ /* You can not used the 40MHz clock selection wich this board */
+ /**************************************************************/
+
+ DPRINTK("You can not used the 40MHz clock selection wich this board\n");
+ i_ReturnValue =
+ -8;
+ }
+ } else {
+ /**********************************/
+ /* Base timing selection is wrong */
+ /**********************************/
+
+ DPRINTK("Base timing selection is wrong\n");
+ i_ReturnValue = -7;
+ }
+ } // if ((b_TimingUnit >= 0) && (b_TimingUnit <= 4))
+ else {
+ /**********************************/
+ /* Timing unit selection is wrong */
+ /**********************************/
+
+ DPRINTK("Timing unit selection is wrong\n");
+ i_ReturnValue = -6;
+ } // if ((b_TimingUnit >= 0) && (b_TimingUnit <= 4))
+ } // if ((b_PCIInputClock == APCI1710_30MHZ) || (b_PCIInputClock == APCI1710_33MHZ))
+ else {
+ /*****************************************/
+ /* The selected PCI input clock is wrong */
+ /*****************************************/
+
+ DPRINTK("The selected PCI input clock is wrong\n");
+ i_ReturnValue = -5;
+ } // if ((b_PCIInputClock == APCI1710_30MHZ) || (b_PCIInputClock == APCI1710_33MHZ))
+ } // if (b_TorCounterMode >= 0 && b_TorCounterMode <= 7)
+ else {
+ /**********************************/
+ /* Tor Counter selection is wrong */
+ /**********************************/
+
+ DPRINTK("Tor Counter selection is wrong\n");
+ i_ReturnValue = -4;
+ } // if (b_TorCounterMode >= 0 && b_TorCounterMode <= 7)
+ } else {
+ /******************************************/
+ /* The module is not a tor counter module */
+ /******************************************/
+
+ DPRINTK("The module is not a tor counter module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+ data[0] = (UINT) ul_RealTimingInterval;
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_EnableTorCounter |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_TorCounter, |
+| BYTE_ b_InputMode, |
+| BYTE_ b_ExternGate, |
+| BYTE_ b_CycleMode, |
+| BYTE_ b_InterruptEnable) |
++----------------------------------------------------------------------------+
+| Task : Enable the tor counter (b_TorCounter) from selected |
+| module (b_ModulNbr). You must calling the |
+| "i_APCI1710_InitTorCounter" function be for you call |
+| this function. |
+| If you enable the tor counter interrupt, the |
+| tor counter generate a interrupt after the timing cycle|
+| See function "i_APCI1710_SetBoardIntRoutineX" and the |
+| Interrupt mask description chapter from this manual. |
+| The b_CycleMode parameter determine if you will |
+| measured a single or more cycle. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number (0 to 3) |
+| BYTE_ b_TorCounter : Tor counter selection (0 or 1). |
+| BYTE_ b_InputMode : Input signal level selection |
+| 0 : Tor count each low level |
+| 1 : Tor count each high level|
+| BYTE_ b_ExternGate : Extern gate action selection |
+| 0 : Extern gate signal not |
+| used |
+| 1 : Extern gate signal used. |
+| If you selected the |
+| single mode, each high |
+| level signal start the |
+| counter. |
+| If you selected the |
+| continuous mode, the |
+| first high level signal |
+| start the tor counter |
+| |
+| APCI1710_TOR_QUADRUPLE _MODE : |
+| In the quadruple mode, the edge|
+| analysis circuit generates a |
+| counting pulse from each edge |
+| of 2 signals which are phase |
+| shifted in relation to each |
+| other. |
+| The gate input is used for the |
+| signal B |
+| |
+| APCI1710_TOR_DOUBLE_MODE: |
+| Functions in the same way as |
+| the quadruple mode, except that|
+| only two of the four edges are |
+| analysed per period. |
+| The gate input is used for the |
+| signal B |
+| |
+| APCI1710_TOR_SIMPLE_MODE: |
+| Functions in the same way as |
+| the quadruple mode, except that|
+| only one of the four edges is |
+| analysed per period. |
+| The gate input is used for the |
+| signal B |
+| |
+| BYTE_ b_CycleMode : Selected the tor counter |
+| acquisition mode |
+| BYTE_ b_InterruptEnable : Enable or disable the |
+| tor counter interrupt. |
+| APCI1710_ENABLE: |
+| Enable the tor counter |
+| interrupt |
+| APCI1710_DISABLE: |
+| Disable the tor counter |
+| interrupt |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a tor counter module |
+| -4: Tor counter selection is wrong |
+| -5: Tor counter not initialised see function |
+| "i_APCI1710_InitTorCounter" |
+| -6: Tor input signal selection is wrong |
+| -7: Extern gate signal mode is wrong |
+| -8: Tor counter acquisition mode cycle is wrong |
+| -9: Interrupt parameter is wrong |
+| -10:Interrupt function not initialised. |
+| See function "i_APCI1710_SetBoardIntRoutineX" |
++----------------------------------------------------------------------------+
+*/
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_DisableTorCounter |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_TorCounter) |
++----------------------------------------------------------------------------+
+| Task : Disable the tor counter (b_TorCounter) from selected |
+| module (b_ModulNbr). If you disable the tor counter |
+| after a start cycle occur and you restart the tor |
+| counter witch the " i_APCI1710_EnableTorCounter" |
+| function, the status register is cleared |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number (0 to 3) |
+| BYTE_ b_TorCounter : Tor counter selection (0 or 1). |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a tor counter module |
+| -4: Tor counter selection is wrong |
+| -5: Tor counter not initialised see function |
+| "i_APCI1710_InitTorCounter" |
+| -6: Tor counter not enabled see function |
+| "i_APCI1710_EnableTorCounter" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnWriteEnableDisableTorCounter(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_Status;
+ DWORD dw_DummyRead;
+ DWORD dw_ConfigReg;
+ BYTE b_ModulNbr, b_Action;
+ BYTE b_TorCounter;
+ BYTE b_InputMode;
+ BYTE b_ExternGate;
+ BYTE b_CycleMode;
+ BYTE b_InterruptEnable;
+
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_Action = (BYTE) data[0]; // enable or disable
+ b_TorCounter = (BYTE) data[1];
+ b_InputMode = (BYTE) data[2];
+ b_ExternGate = (BYTE) data[3];
+ b_CycleMode = (BYTE) data[4];
+ b_InterruptEnable = (BYTE) data[5];
+ i_ReturnValue = insn->n;;
+ devpriv->tsk_Current = current; // Save the current process task structure
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***********************/
+ /* Test if tor counter */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_TOR_COUNTER) {
+ /**********************************/
+ /* Test the tor counter selection */
+ /**********************************/
+
+ if (b_TorCounter <= 1) {
+ switch (b_Action) // Enable or Disable
+ {
+ case APCI1710_ENABLE:
+ /***********************************/
+ /* Test if tor counter initialised */
+ /***********************************/
+
+ dw_Status =
+ inl(devpriv->s_BoardInfos.
+ ui_Address + 8 +
+ (16 * b_TorCounter) +
+ (64 * b_ModulNbr));
+
+ if (dw_Status & 0x10) {
+ /******************************/
+ /* Test the input signal mode */
+ /******************************/
+
+ if (b_InputMode == 0 ||
+ b_InputMode == 1 ||
+ b_InputMode ==
+ APCI1710_TOR_SIMPLE_MODE
+ || b_InputMode ==
+ APCI1710_TOR_DOUBLE_MODE
+ || b_InputMode ==
+ APCI1710_TOR_QUADRUPLE_MODE)
+ {
+ /************************************/
+ /* Test the extern gate signal mode */
+ /************************************/
+
+ if (b_ExternGate == 0
+ || b_ExternGate
+ == 1
+ || b_InputMode >
+ 1) {
+ /*********************************/
+ /* Test the cycle mode parameter */
+ /*********************************/
+
+ if ((b_CycleMode == APCI1710_SINGLE) || (b_CycleMode == APCI1710_CONTINUOUS)) {
+ /***************************/
+ /* Test the interrupt flag */
+ /***************************/
+
+ if ((b_InterruptEnable == APCI1710_ENABLE) || (b_InterruptEnable == APCI1710_DISABLE)) {
+
+ /***************************/
+ /* Save the interrupt mode */
+ /***************************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TorCounterModuleInfo.
+ s_TorCounterInfo
+ [b_TorCounter].
+ b_InterruptEnable
+ =
+ b_InterruptEnable;
+
+ /*******************/
+ /* Get the command */
+ /*******************/
+
+ dw_ConfigReg
+ =
+ inl
+ (devpriv->
+ s_BoardInfos.
+ ui_Address
+ +
+ 4
+ +
+ (16 * b_TorCounter) + (64 * b_ModulNbr));
+
+ dw_ConfigReg
+ =
+ (dw_ConfigReg
+ >>
+ 4)
+ &
+ 0x30;
+
+ /********************************/
+ /* Test if not direct mode used */
+ /********************************/
+
+ if (b_InputMode > 1) {
+ /*******************************/
+ /* Extern gate can not be used */
+ /*******************************/
+
+ b_ExternGate
+ =
+ 0;
+
+ /*******************************************/
+ /* Enable the extern gate for the Signal B */
+ /*******************************************/
+
+ dw_ConfigReg
+ =
+ dw_ConfigReg
+ |
+ 0x40;
+
+ /***********************/
+ /* Test if simple mode */
+ /***********************/
+
+ if (b_InputMode == APCI1710_TOR_SIMPLE_MODE) {
+ /**************************/
+ /* Enable the sinple mode */
+ /**************************/
+
+ dw_ConfigReg
+ =
+ dw_ConfigReg
+ |
+ 0x780;
+
+ } // if (b_InputMode == APCI1710_TOR_SIMPLE_MODE)
+
+ /***********************/
+ /* Test if double mode */
+ /***********************/
+
+ if (b_InputMode == APCI1710_TOR_DOUBLE_MODE) {
+ /**************************/
+ /* Enable the double mode */
+ /**************************/
+
+ dw_ConfigReg
+ =
+ dw_ConfigReg
+ |
+ 0x180;
+
+ } // if (b_InputMode == APCI1710_TOR_DOUBLE_MODE)
+
+ b_InputMode
+ =
+ 0;
+ } // if (b_InputMode > 1)
+
+ /*******************/
+ /* Set the command */
+ /*******************/
+
+ dw_ConfigReg
+ =
+ dw_ConfigReg
+ |
+ b_CycleMode
+ |
+ (b_InterruptEnable
+ *
+ 2)
+ |
+ (b_InputMode
+ *
+ 4)
+ |
+ (b_ExternGate
+ *
+ 8);
+
+ /*****************************/
+ /* Clear the status register */
+ /*****************************/
+
+ dw_DummyRead
+ =
+ inl
+ (devpriv->
+ s_BoardInfos.
+ ui_Address
+ +
+ 0
+ +
+ (16 * b_TorCounter) + (64 * b_ModulNbr));
+
+ /***************************************/
+ /* Clear the interrupt status register */
+ /***************************************/
+
+ dw_DummyRead
+ =
+ inl
+ (devpriv->
+ s_BoardInfos.
+ ui_Address
+ +
+ 12
+ +
+ (16 * b_TorCounter) + (64 * b_ModulNbr));
+
+ /********************/
+ /* Set the commando */
+ /********************/
+
+ outl(dw_ConfigReg, devpriv->s_BoardInfos.ui_Address + 4 + (16 * b_TorCounter) + (64 * b_ModulNbr));
+
+ /****************/
+ /* Set the gate */
+ /****************/
+
+ outl(1, devpriv->s_BoardInfos.ui_Address + 8 + (16 * b_TorCounter) + (64 * b_ModulNbr));
+
+ } // if ((b_InterruptEnable == APCI1710_ENABLE) || (b_InterruptEnable == APCI1710_DISABLE))
+ else {
+ /********************************/
+ /* Interrupt parameter is wrong */
+ /********************************/
+
+ DPRINTK("Interrupt parameter is wrong\n");
+ i_ReturnValue
+ =
+ -9;
+ } // if ((b_InterruptEnable == APCI1710_ENABLE) || (b_InterruptEnable == APCI1710_DISABLE))
+ } // if ((b_CycleMode == APCI1710_SINGLE) || (b_CycleMode == APCI1710_CONTINUOUS))
+ else {
+ /***********************************************/
+ /* Tor counter acquisition mode cycle is wrong */
+ /***********************************************/
+
+ DPRINTK("Tor counter acquisition mode cycle is wrong\n");
+ i_ReturnValue
+ =
+ -8;
+ } // if ((b_CycleMode == APCI1710_SINGLE) || (b_CycleMode == APCI1710_CONTINUOUS))
+ } // if (b_ExternGate >= 0 && b_ExternGate <= 1)
+ else {
+ /***********************************/
+ /* Extern gate input mode is wrong */
+ /***********************************/
+
+ DPRINTK("Extern gate input mode is wrong\n");
+ i_ReturnValue =
+ -7;
+ } // if (b_ExternGate >= 0 && b_ExternGate <= 1)
+ } // if (b_InputMode >= 0 && b_InputMode <= 1)
+ else {
+ /***************************************/
+ /* Tor input signal selection is wrong */
+ /***************************************/
+
+ DPRINTK("Tor input signal selection is wrong\n");
+ i_ReturnValue = -6;
+ }
+ } else {
+ /*******************************/
+ /* Tor counter not initialised */
+ /*******************************/
+
+ DPRINTK("Tor counter not initialised\n");
+ i_ReturnValue = -5;
+ }
+ break;
+
+ case APCI1710_DISABLE:
+ /***********************************/
+ /* Test if tor counter initialised */
+ /***********************************/
+
+ dw_Status = inl(devpriv->s_BoardInfos.
+ ui_Address + 8 +
+ (16 * b_TorCounter) +
+ (64 * b_ModulNbr));
+
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (dw_Status & 0x10) {
+ /***************************/
+ /* Test if counter enabled */
+ /***************************/
+
+ if (dw_Status & 0x1) {
+ /****************************/
+ /* Clear the interrupt mode */
+ /****************************/
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TorCounterModuleInfo.
+ s_TorCounterInfo
+ [b_TorCounter].
+ b_InterruptEnable
+ =
+ APCI1710_DISABLE;
+
+ /******************/
+ /* Clear the gate */
+ /******************/
+
+ outl(0, devpriv->
+ s_BoardInfos.
+ ui_Address + 8 +
+ (16 * b_TorCounter) + (64 * b_ModulNbr));
+ } // if (dw_Status & 0x1)
+ else {
+ /***************************/
+ /* Tor counter not enabled */
+ /***************************/
+
+ DPRINTK("Tor counter not enabled \n");
+ i_ReturnValue = -6;
+ } // if (dw_Status & 0x1)
+ } // if (dw_Status & 0x10)
+ else {
+ /*******************************/
+ /* Tor counter not initialised */
+ /*******************************/
+
+ DPRINTK("Tor counter not initialised\n");
+ i_ReturnValue = -5;
+ } // // if (dw_Status & 0x10)
+
+ } // switch
+ } // if (b_TorCounter <= 1)
+ else {
+ /**********************************/
+ /* Tor counter selection is wrong */
+ /**********************************/
+
+ DPRINTK("Tor counter selection is wrong\n");
+ i_ReturnValue = -4;
+ } // if (b_TorCounter <= 1)
+ } else {
+ /******************************************/
+ /* The module is not a tor counter module */
+ /******************************************/
+
+ DPRINTK("The module is not a tor counter module \n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ DPRINTK("Module number error \n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_GetTorCounterInitialisation |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_TorCounter, |
+| PBYTE_ pb_TimingUnit, |
+| PULONG_ pul_TimingInterval, |
+| PBYTE_ pb_InputMode, |
+| PBYTE_ pb_ExternGate, |
+| PBYTE_ pb_CycleMode, |
+| PBYTE_ pb_Enable, |
+| PBYTE_ pb_InterruptEnable)|
++----------------------------------------------------------------------------+
+| Task : Enable the tor counter (b_TorCounter) from selected |
+| module (b_ModulNbr). You must calling the |
+| "i_APCI1710_InitTorCounter" function be for you call |
+| this function. |
+| If you enable the tor counter interrupt, the |
+| tor counter generate a interrupt after the timing cycle|
+| See function "i_APCI1710_SetBoardIntRoutineX" and the |
+| Interrupt mask description chapter from this manual. |
+| The b_CycleMode parameter determine if you will |
+| measured a single or more cycle. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number (0 to 3) |
+| BYTE_ b_TorCounter : Tor counter selection (0 or 1)
+
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ b_TorCounter = CR_CHAN(insn->chanspec);
+. |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_TimingUnit : Base timing unit (0 to 4) |
+| 0 : ns |
+| 1 : µs |
+| 2 : ms |
+| 3 : s |
+| 4 : mn |
+| PULONG_ pul_TimingInterval : Base timing value. |
+| PBYTE_ pb_InputMode : Input signal level |
+| selection |
+| 0 : Tor count each low level |
+| 1 : Tor count each high level|
+| PBYTE_ pb_ExternGate : Extern gate action |
+| selection |
+| 0 : Extern gate signal not |
+| used |
+| 1 : Extern gate signal used|
+| PBYTE_ pb_CycleMode : Tor counter acquisition |
+| mode |
+| PBYTE_ pb_Enable : Indicate if the tor counter|
+| is enabled or no |
+| 0 : Tor counter disabled |
+| 1 : Tor counter enabled |
+| PBYTE_ pb_InterruptEnable : Enable or disable the |
+| tor counter interrupt. |
+| APCI1710_ENABLE: |
+| Enable the tor counter |
+| interrupt |
+| APCI1710_DISABLE: |
+| Disable the tor counter |
+| interrupt
+ pb_TimingUnit = (PBYTE) &data[0];
+ pul_TimingInterval = (PULONG) &data[1];
+ pb_InputMode = (PBYTE) &data[2];
+ pb_ExternGate = (PBYTE) &data[3];
+ pb_CycleMode = (PBYTE) &data[4];
+ pb_Enable = (PBYTE) &data[5];
+ pb_InterruptEnable = (PBYTE) &data[6];
+ |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a tor counter module |
+| -4: Tor counter selection is wrong |
+| -5: Tor counter not initialised see function |
+| "i_APCI1710_InitTorCounter" |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnReadGetTorCounterInitialisation(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_Status;
+ BYTE b_ModulNbr;
+ BYTE b_TorCounter;
+ PBYTE pb_TimingUnit;
+ PULONG pul_TimingInterval;
+ PBYTE pb_InputMode;
+ PBYTE pb_ExternGate;
+ PBYTE pb_CycleMode;
+ PBYTE pb_Enable;
+ PBYTE pb_InterruptEnable;
+
+ i_ReturnValue = insn->n;
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ b_TorCounter = CR_CHAN(insn->chanspec);
+
+ pb_TimingUnit = (PBYTE) & data[0];
+ pul_TimingInterval = (PULONG) & data[1];
+ pb_InputMode = (PBYTE) & data[2];
+ pb_ExternGate = (PBYTE) & data[3];
+ pb_CycleMode = (PBYTE) & data[4];
+ pb_Enable = (PBYTE) & data[5];
+ pb_InterruptEnable = (PBYTE) & data[6];
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /***********************/
+ /* Test if tor counter */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_TOR_COUNTER) {
+ /**********************************/
+ /* Test the tor counter selection */
+ /**********************************/
+
+ if (b_TorCounter <= 1) {
+
+ /***********************************/
+ /* Test if tor counter initialised */
+ /***********************************/
+
+ dw_Status = inl(devpriv->s_BoardInfos.
+ ui_Address + 8 + (16 * b_TorCounter) +
+ (64 * b_ModulNbr));
+
+ if (dw_Status & 0x10) {
+ *pb_Enable = dw_Status & 1;
+
+ /********************/
+ /* Get the commando */
+ /********************/
+
+ dw_Status = inl(devpriv->s_BoardInfos.
+ ui_Address + 4 +
+ (16 * b_TorCounter) +
+ (64 * b_ModulNbr));
+
+ *pb_CycleMode =
+ (BYTE) ((dw_Status >> 4) & 1);
+ *pb_InterruptEnable =
+ (BYTE) ((dw_Status >> 5) & 1);
+
+ /******************************************************/
+ /* Test if extern gate used for clock or for signal B */
+ /******************************************************/
+
+ if (dw_Status & 0x600) {
+ /*****************************************/
+ /* Test if extern gate used for signal B */
+ /*****************************************/
+
+ if (dw_Status & 0x400) {
+ /***********************/
+ /* Test if simple mode */
+ /***********************/
+
+ if ((dw_Status & 0x7800)
+ == 0x7800) {
+ *pb_InputMode =
+ APCI1710_TOR_SIMPLE_MODE;
+ }
+
+ /***********************/
+ /* Test if double mode */
+ /***********************/
+
+ if ((dw_Status & 0x7800)
+ == 0x1800) {
+ *pb_InputMode =
+ APCI1710_TOR_DOUBLE_MODE;
+ }
+
+ /**************************/
+ /* Test if quadruple mode */
+ /**************************/
+
+ if ((dw_Status & 0x7800)
+ == 0x0000) {
+ *pb_InputMode =
+ APCI1710_TOR_QUADRUPLE_MODE;
+ }
+ } // if (dw_Status & 0x400)
+ else {
+ *pb_InputMode = 1;
+ } // // if (dw_Status & 0x400)
+
+ /************************/
+ /* Extern gate not used */
+ /************************/
+
+ *pb_ExternGate = 0;
+ } // if (dw_Status & 0x600)
+ else {
+ *pb_InputMode =
+ (BYTE) ((dw_Status >> 6)
+ & 1);
+ *pb_ExternGate =
+ (BYTE) ((dw_Status >> 7)
+ & 1);
+ } // if (dw_Status & 0x600)
+
+ *pb_TimingUnit =
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_TorCounterModuleInfo.
+ s_TorCounterInfo[b_TorCounter].
+ b_TimingUnit;
+
+ *pul_TimingInterval =
+ devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_TorCounterModuleInfo.
+ s_TorCounterInfo[b_TorCounter].
+ ul_RealTimingInterval;
+ } else {
+ /*******************************/
+ /* Tor counter not initialised */
+ /*******************************/
+
+ DPRINTK("Tor counter not initialised\n");
+ i_ReturnValue = -5;
+ }
+
+ } // if (b_TorCounter <= 1)
+ else {
+ /**********************************/
+ /* Tor counter selection is wrong */
+ /**********************************/
+
+ DPRINTK("Tor counter selection is wrong \n");
+ i_ReturnValue = -4;
+ } // if (b_TorCounter <= 1)
+ } else {
+ /******************************************/
+ /* The module is not a tor counter module */
+ /******************************************/
+
+ DPRINTK("The module is not a tor counter module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_ReadTorCounterValue |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_TorCounter, |
+| UINT_ ui_TimeOut, |
+| PBYTE_ pb_TorCounterStatus, |
+| PULONG_ pul_TorCounterValue) |
++----------------------------------------------------------------------------+
+| Task case APCI1710_TOR_GETPROGRESSSTATUS: Return the tor counter
+(b_TorCounter) status (pb_TorCounterStatus) from selected tor counter |
+| module (b_ModulNbr).
+
+ case APCI1710_TOR_GETCOUNTERVALUE :
+ Return the tor counter (b_TorCounter) status |
+| (pb_TorCounterStatus) and the timing value |
+| (pul_TorCounterValue) after a conting cycle stop |
+| from selected tor counter module (b_ModulNbr). |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number (0 to 3) |
+| BYTE_ b_TorCounter : Tor counter selection (0 or 1).
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ b_ReadType = (BYTE) data[0];
+ b_TorCounter = (BYTE) data[1];
+ ui_TimeOut = (UINT) data[2]; |
++----------------------------------------------------------------------------+
+| Output Parameters : PBYTE_ pb_TorCounterStatus : Return the tor counter |
+| status. |
+| 0 : Conting cycle not started|
+| Software gate not set. |
+| 1 : Conting cycle started. |
+| Software gate set. |
+| 2 : Conting cycle stopped. |
+| The conting cycle is |
+| terminate. |
+| 3 : A overflow occur. You |
+| must change the base |
+| timing witch the |
+| function |
+| "i_APCI1710_InitTorCounter"|
+| 4 : Timeeout occur |
+| PULONG pul_TorCounterValue : Tor counter value.
+ pb_TorCounterStatus=(PBYTE) &data[0];
+ pul_TorCounterValue=(PULONG) &data[1]; |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: Module selection wrong |
+| -3: The module is not a tor counter module |
+| -4: Tor counter selection is wrong |
+| -5: Tor counter not initialised see function |
+| "i_APCI1710_InitTorCounter" |
+| -6: Tor counter not enabled see function |
+| "i_APCI1710_EnableTorCounter" |
+| -7: Timeout parameter is wrong (0 to 65535) |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnBitsGetTorCounterProgressStatusAndValue(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_Status;
+ DWORD dw_TimeOut = 0;
+
+ BYTE b_ModulNbr;
+ BYTE b_TorCounter;
+ BYTE b_ReadType;
+ UINT ui_TimeOut;
+ PBYTE pb_TorCounterStatus;
+ PULONG pul_TorCounterValue;
+
+ i_ReturnValue = insn->n;
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ b_ReadType = (BYTE) data[0];
+ b_TorCounter = (BYTE) data[1];
+ ui_TimeOut = (UINT) data[2];
+ pb_TorCounterStatus = (PBYTE) & data[0];
+ pul_TorCounterValue = (PULONG) & data[1];
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ReadType == APCI1710_TOR_READINTERRUPT) {
+
+ data[0] = devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.ui_Read].b_OldModuleMask;
+ data[1] = devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.ui_Read].ul_OldInterruptMask;
+ data[2] = devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.ui_Read].ul_OldCounterLatchValue;
+
+ /**************************/
+ /* Increment the read FIFO */
+ /***************************/
+
+ devpriv->
+ s_InterruptParameters.
+ ui_Read = (devpriv->
+ s_InterruptParameters.
+ ui_Read + 1) % APCI1710_SAVE_INTERRUPT;
+
+ return insn->n;
+ }
+
+ if (b_ModulNbr < 4) {
+ /***********************/
+ /* Test if tor counter */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_TOR_COUNTER) {
+ /**********************************/
+ /* Test the tor counter selection */
+ /**********************************/
+
+ if (b_TorCounter <= 1) {
+ /***********************************/
+ /* Test if tor counter initialised */
+ /***********************************/
+
+ dw_Status = inl(devpriv->s_BoardInfos.
+ ui_Address + 8 + (16 * b_TorCounter) +
+ (64 * b_ModulNbr));
+
+ /*******************************/
+ /* Test if counter initialised */
+ /*******************************/
+
+ if (dw_Status & 0x10) {
+ /***************************/
+ /* Test if counter enabled */
+ /***************************/
+
+ if (dw_Status & 0x1) {
+
+ switch (b_ReadType) {
+
+ case APCI1710_TOR_GETPROGRESSSTATUS:
+ /*******************/
+ /* Read the status */
+ /*******************/
+
+ dw_Status =
+ inl(devpriv->
+ s_BoardInfos.
+ ui_Address + 4 +
+ (16 * b_TorCounter) + (64 * b_ModulNbr));
+
+ dw_Status =
+ dw_Status & 0xF;
+
+ /*****************/
+ /* Test if start */
+ /*****************/
+
+ if (dw_Status & 1) {
+ if (dw_Status &
+ 2) {
+ if (dw_Status & 4) {
+ /************************/
+ /* Tor counter owerflow */
+ /************************/
+
+ *pb_TorCounterStatus
+ =
+ 3;
+ } else {
+ /***********************/
+ /* Tor counter started */
+ /***********************/
+
+ *pb_TorCounterStatus
+ =
+ 2;
+ }
+ } else {
+ /***********************/
+ /* Tor counter started */
+ /***********************/
+
+ *pb_TorCounterStatus
+ =
+ 1;
+ }
+ } else {
+ /***************************/
+ /* Tor counter not started */
+ /***************************/
+
+ *pb_TorCounterStatus
+ = 0;
+ }
+ break;
+
+ case APCI1710_TOR_GETCOUNTERVALUE:
+
+ /*****************************/
+ /* Test the timout parameter */
+ /*****************************/
+
+ if ((ui_TimeOut >= 0)
+ && (ui_TimeOut
+ <=
+ 65535UL))
+ {
+ for (;;) {
+ /*******************/
+ /* Read the status */
+ /*******************/
+
+ dw_Status
+ =
+ inl
+ (devpriv->
+ s_BoardInfos.
+ ui_Address
+ +
+ 4
+ +
+ (16 * b_TorCounter) + (64 * b_ModulNbr));
+ /********************/
+ /* Test if overflow */
+ /********************/
+
+ if ((dw_Status & 4) == 4) {
+ /******************/
+ /* Overflow occur */
+ /******************/
+
+ *pb_TorCounterStatus
+ =
+ 3;
+
+ /******************/
+ /* Read the value */
+ /******************/
+
+ *pul_TorCounterValue
+ =
+ inl
+ (devpriv->
+ s_BoardInfos.
+ ui_Address
+ +
+ 0
+ +
+ (16 * b_TorCounter) + (64 * b_ModulNbr));
+ break;
+ } // if ((dw_Status & 4) == 4)
+ else {
+ /*******************************/
+ /* Test if measurement stopped */
+ /*******************************/
+
+ if ((dw_Status & 2) == 2) {
+ /***********************/
+ /* A stop signal occur */
+ /***********************/
+
+ *pb_TorCounterStatus
+ =
+ 2;
+
+ /******************/
+ /* Read the value */
+ /******************/
+
+ *pul_TorCounterValue
+ =
+ inl
+ (devpriv->
+ s_BoardInfos.
+ ui_Address
+ +
+ 0
+ +
+ (16 * b_TorCounter) + (64 * b_ModulNbr));
+
+ break;
+ } // if ((dw_Status & 2) == 2)
+ else {
+ /*******************************/
+ /* Test if measurement started */
+ /*******************************/
+
+ if ((dw_Status & 1) == 1) {
+ /************************/
+ /* A start signal occur */
+ /************************/
+
+ *pb_TorCounterStatus
+ =
+ 1;
+ } // if ((dw_Status & 1) == 1)
+ else {
+ /***************************/
+ /* Measurement not started */
+ /***************************/
+
+ *pb_TorCounterStatus
+ =
+ 0;
+ } // if ((dw_Status & 1) == 1)
+ } // if ((dw_Status & 2) == 2)
+ } // if ((dw_Status & 8) == 8)
+
+ if (dw_TimeOut == ui_TimeOut) {
+ /*****************/
+ /* Timeout occur */
+ /*****************/
+
+ break;
+ } else {
+ /*************************/
+ /* Increment the timeout */
+ /*************************/
+
+ dw_TimeOut
+ =
+ dw_TimeOut
+ +
+ 1;
+
+ mdelay(1000);
+ }
+ } // for (;;)
+
+ /*************************/
+ /* Test if timeout occur */
+ /*************************/
+
+ if ((*pb_TorCounterStatus != 3) && (dw_TimeOut == ui_TimeOut) && (ui_TimeOut != 0)) {
+ /*****************/
+ /* Timeout occur */
+ /*****************/
+
+ *pb_TorCounterStatus
+ =
+ 4;
+ }
+ } else {
+ /******************************/
+ /* Timeout parameter is wrong */
+ /******************************/
+
+ DPRINTK("Timeout parameter is wrong\n");
+ i_ReturnValue =
+ -7;
+ }
+ break;
+
+ default:
+ printk("Inputs wrong\n");
+ } // switch end
+ } // if (dw_Status & 0x1)
+ else {
+ /***************************/
+ /* Tor counter not enabled */
+ /***************************/
+
+ DPRINTK("Tor counter not enabled\n");
+ i_ReturnValue = -6;
+ } // if (dw_Status & 0x1)
+ } else {
+ /*******************************/
+ /* Tor counter not initialised */
+ /*******************************/
+
+ DPRINTK("Tor counter not initialised\n");
+ i_ReturnValue = -5;
+ }
+ } // if (b_TorCounter <= 1)
+ else {
+ /**********************************/
+ /* Tor counter selection is wrong */
+ /**********************************/
+
+ DPRINTK("Tor counter selection is wrong\n");
+ i_ReturnValue = -4;
+ } // if (b_TorCounter <= 1)
+ } else {
+ /******************************************/
+ /* The module is not a tor counter module */
+ /******************************************/
+
+ DPRINTK("The module is not a tor counter module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_Tor.h b/drivers/staging/comedi/drivers/addi-data/APCI1710_Tor.h
new file mode 100644
index 000000000000..c245d16207b8
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_Tor.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+#define APCI1710_30MHZ 30
+#define APCI1710_33MHZ 33
+#define APCI1710_40MHZ 40
+
+#define APCI1710_GATE_INPUT 10
+
+#define APCI1710_TOR_SIMPLE_MODE 2
+#define APCI1710_TOR_DOUBLE_MODE 3
+#define APCI1710_TOR_QUADRUPLE_MODE 4
+
+#define APCI1710_SINGLE 0
+#define APCI1710_CONTINUOUS 1
+
+#define APCI1710_TOR_GETPROGRESSSTATUS 0
+#define APCI1710_TOR_GETCOUNTERVALUE 1
+#define APCI1710_TOR_READINTERRUPT 2
+
+/*
+ * TOR_COUNTER INISIALISATION FUNCTION
+ */
+INT i_APCI1710_InsnConfigInitTorCounter(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+INT i_APCI1710_InsnWriteEnableDisableTorCounter(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+
+INT i_APCI1710_InsnReadGetTorCounterInitialisation(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+/*
+ * TOR_COUNTER READ FUNCTION
+ */
+INT i_APCI1710_InsnBitsGetTorCounterProgressStatusAndValue(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_Ttl.c b/drivers/staging/comedi/drivers/addi-data/APCI1710_Ttl.c
new file mode 100644
index 000000000000..68b1d26bae13
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_Ttl.c
@@ -0,0 +1,1038 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-----------------------------------------------------------------------+
+ | Project : API APCI1710 | Compiler : gcc |
+ | Module name : TTL.C | Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-----------------------------------------------------------------------+
+ | Description : APCI-1710 TTL I/O module |
+ | |
+ | |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +-----------------------------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | 13/05/98 | S. Weber | TTL digital input / output implementation |
+ |----------|-----------|------------------------------------------------|
+ | 08/05/00 | Guinot C | - 0400/0228 All Function in RING 0 |
+ | | | available |
+ +-----------------------------------------------------------------------+
+ | | | |
+ | | | |
+ +-----------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+
+#include "APCI1710_Ttl.h"
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_InitTTLIODirection |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_PortAMode, |
+| BYTE_ b_PortBMode, |
+| BYTE_ b_PortCMode, |
+| BYTE_ b_PortDMode) |
++----------------------------------------------------------------------------+
+| Task APCI1710_TTL_INIT (using defaults) : Configure the TTL I/O operating mode from selected |
+| module (b_ModulNbr). You must calling this function be|
+| for you call any other function witch access of TTL. |
+ APCI1710_TTL_INITDIRECTION(user inputs for direction)
+
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710|
+| BYTE_ b_ModulNbr : Module number to |
+| configure (0 to 3)
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_InitType = (BYTE) data[0];
+ b_PortAMode = (BYTE) data[1];
+ b_PortBMode = (BYTE) data[2];
+ b_PortCMode = (BYTE) data[3];
+ b_PortDMode = (BYTE) data[4];|
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The module parameter is wrong |
+| -3: The module is not a TTL module |
+| -4: Function not available for this version |
+| -5: Port A mode selection is wrong |
+| -6: Port B mode selection is wrong |
+| -7: Port C mode selection is wrong |
+| -8: Port D mode selection is wrong |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnConfigInitTTLIO(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ BYTE b_ModulNbr;
+ BYTE b_InitType;
+ BYTE b_PortAMode;
+ BYTE b_PortBMode;
+ BYTE b_PortCMode;
+ BYTE b_PortDMode;
+
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ b_InitType = (BYTE) data[0];
+ i_ReturnValue = insn->n;
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /**************************/
+ /* Test if TTL I/O module */
+ /**************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_TTL_IO) {
+ switch (b_InitType) {
+ case APCI1710_TTL_INIT:
+
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_TTLIOInfo.b_TTLInit = 1;
+
+ /***************************/
+ /* Set TTL port A to input */
+ /***************************/
+
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_TTLIOInfo.b_PortConfiguration[0] = 0;
+
+ /***************************/
+ /* Set TTL port B to input */
+ /***************************/
+
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_TTLIOInfo.b_PortConfiguration[1] = 0;
+
+ /***************************/
+ /* Set TTL port C to input */
+ /***************************/
+
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_TTLIOInfo.b_PortConfiguration[2] = 0;
+
+ /****************************/
+ /* Set TTL port D to output */
+ /****************************/
+
+ devpriv->s_ModuleInfo[b_ModulNbr].
+ s_TTLIOInfo.b_PortConfiguration[3] = 1;
+
+ /*************************/
+ /* Set the configuration */
+ /*************************/
+
+ outl(0x8,
+ devpriv->s_BoardInfos.ui_Address + 20 +
+ (64 * b_ModulNbr));
+ break;
+
+ case APCI1710_TTL_INITDIRECTION:
+
+ b_PortAMode = (BYTE) data[1];
+ b_PortBMode = (BYTE) data[2];
+ b_PortCMode = (BYTE) data[3];
+ b_PortDMode = (BYTE) data[4];
+
+ /********************/
+ /* Test the version */
+ /********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration
+ [b_ModulNbr] & 0xFFFF) >=
+ 0x3230) {
+ /************************/
+ /* Test the port A mode */
+ /************************/
+
+ if ((b_PortAMode == 0)
+ || (b_PortAMode == 1)) {
+ /************************/
+ /* Test the port B mode */
+ /************************/
+
+ if ((b_PortBMode == 0)
+ || (b_PortBMode == 1)) {
+ /************************/
+ /* Test the port C mode */
+ /************************/
+
+ if ((b_PortCMode == 0)
+ || (b_PortCMode
+ == 1)) {
+ /************************/
+ /* Test the port D mode */
+ /************************/
+
+ if ((b_PortDMode == 0) || (b_PortDMode == 1)) {
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TTLIOInfo.
+ b_TTLInit
+ =
+ 1;
+
+ /***********************/
+ /* Set TTL port A mode */
+ /***********************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TTLIOInfo.
+ b_PortConfiguration
+ [0]
+ =
+ b_PortAMode;
+
+ /***********************/
+ /* Set TTL port B mode */
+ /***********************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TTLIOInfo.
+ b_PortConfiguration
+ [1]
+ =
+ b_PortBMode;
+
+ /***********************/
+ /* Set TTL port C mode */
+ /***********************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TTLIOInfo.
+ b_PortConfiguration
+ [2]
+ =
+ b_PortCMode;
+
+ /***********************/
+ /* Set TTL port D mode */
+ /***********************/
+
+ devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TTLIOInfo.
+ b_PortConfiguration
+ [3]
+ =
+ b_PortDMode;
+
+ /*************************/
+ /* Set the configuration */
+ /*************************/
+
+ outl((b_PortAMode << 0) | (b_PortBMode << 1) | (b_PortCMode << 2) | (b_PortDMode << 3), devpriv->s_BoardInfos.ui_Address + 20 + (64 * b_ModulNbr));
+ } else {
+ /**********************************/
+ /* Port D mode selection is wrong */
+ /**********************************/
+
+ DPRINTK("Port D mode selection is wrong\n");
+ i_ReturnValue
+ =
+ -8;
+ }
+ } else {
+ /**********************************/
+ /* Port C mode selection is wrong */
+ /**********************************/
+
+ DPRINTK("Port C mode selection is wrong\n");
+ i_ReturnValue =
+ -7;
+ }
+ } else {
+ /**********************************/
+ /* Port B mode selection is wrong */
+ /**********************************/
+
+ DPRINTK("Port B mode selection is wrong\n");
+ i_ReturnValue = -6;
+ }
+ } else {
+ /**********************************/
+ /* Port A mode selection is wrong */
+ /**********************************/
+
+ DPRINTK("Port A mode selection is wrong\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /*******************************************/
+ /* Function not available for this version */
+ /*******************************************/
+
+ DPRINTK("Function not available for this version\n");
+ i_ReturnValue = -4;
+ }
+ break;
+
+ DPRINTK("\n");
+ default:
+ printk("Bad Config Type\n");
+ } // switch end
+ } else {
+ /**********************************/
+ /* The module is not a TTL module */
+ /**********************************/
+
+ DPRINTK("The module is not a TTL module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| INPUT FUNCTIONS |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_ReadTTLIOChannelValue |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_SelectedPort, |
+| BYTE_ b_InputChannel, |
+| PBYTE_ pb_ChannelStatus) |
++----------------------------------------------------------------------------+
+| Task : Read the status from selected TTL digital input |
+| (b_InputChannel)
++----------------------------------------------------------------------------+
+| Task : Read the status from digital input port |
+| (b_SelectedPort) from selected TTL module (b_ModulNbr) |
++----------------------------------------------------------------------------+
+
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710|
+| BYTE_ b_ModulNbr : Module number to |
+| configure (0 to 7) |
+| BYTE_ b_SelectedPort, : Selection from TTL I/O |
+| port (0 to 2) |
+| 0 : Port A selection |
+| 1 : Port B selection |
+| 2 : Port C selection |
+| 3 : Port D selection |
+| BYTE_ b_InputChannel : Selection from digital |
+| input ( 0 to 2)
+APCI1710_TTL_READCHANNEL
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ b_SelectedPort= CR_RANGE(insn->chanspec);
+ b_InputChannel= CR_CHAN(insn->chanspec);
+ b_ReadType = (BYTE) data[0];
+
+ APCI1710_TTL_READPORT|
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ b_SelectedPort= CR_RANGE(insn->chanspec);
+ b_ReadType = (BYTE) data[0];
+
++----------------------------------------------------------------------------+
+| Output Parameters : data[0]
+
+ PBYTE_ pb_ChannelStatus : Digital input channel |
+| status |
+| 0 : Channle is not active|
+| 1 : Channle is active |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The module parameter is wrong |
+| -3: The module is not a TTL module |
+| -4: The selected TTL input port is wrong |
+| -5: The selected TTL digital input is wrong |
+| -6: TTL I/O not initialised |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnBitsReadTTLIO(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_StatusReg;
+ BYTE b_ModulNbr;
+ BYTE b_SelectedPort;
+ BYTE b_InputChannel;
+ BYTE b_ReadType;
+ PBYTE pb_ChannelStatus;
+ PBYTE pb_PortValue;
+
+ i_ReturnValue = insn->n;
+ b_ReadType = (BYTE) data[0];
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ b_SelectedPort = CR_RANGE(insn->chanspec);
+ b_InputChannel = CR_CHAN(insn->chanspec);
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /**************************/
+ /* Test if TTL I/O module */
+ /**************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_TTL_IO) {
+ switch (b_ReadType) {
+
+ case APCI1710_TTL_READCHANNEL:
+ pb_ChannelStatus = (PBYTE) & data[0];
+ /********************************/
+ /* Test the TTL I/O port number */
+ /********************************/
+
+ if (((b_SelectedPort <= 2)
+ && ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration
+ [b_ModulNbr] &
+ 0xFFFF) ==
+ 0x3130))
+ || ((b_SelectedPort <= 3)
+ && ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration
+ [b_ModulNbr] &
+ 0xFFFF) >=
+ 0x3230))) {
+ /******************************************/
+ /* Test the digital imnput channel number */
+ /******************************************/
+
+ if (((b_InputChannel <= 7)
+ && (b_SelectedPort < 3))
+ || ((b_InputChannel <= 1)
+ && (b_SelectedPort ==
+ 3))) {
+ /******************************************/
+ /* Test if the TTL I/O module initialised */
+ /******************************************/
+
+ if (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TTLIOInfo.b_TTLInit ==
+ 1) {
+ /***********************************/
+ /* Test if TTL port used for input */
+ /***********************************/
+
+ if (((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF) == 0x3130) || (((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF) >= 0x3230) && (devpriv->s_ModuleInfo[b_ModulNbr].s_TTLIOInfo.b_PortConfiguration[b_SelectedPort] == 0))) {
+ /**************************/
+ /* Read all digital input */
+ /**************************/
+
+ dw_StatusReg =
+ inl
+ (devpriv->
+ s_BoardInfos.
+ ui_Address
+ +
+ (64 * b_ModulNbr));
+
+ *pb_ChannelStatus
+ =
+ (BYTE) (
+ (dw_StatusReg
+ >>
+ (8 * b_SelectedPort)) >> b_InputChannel) & 1;
+ } else {
+ /*******************************/
+ /* Selected TTL I/O port error */
+ /*******************************/
+
+ DPRINTK("Selected TTL I/O port error\n");
+ i_ReturnValue =
+ -4;
+ }
+ } else {
+ /***************************/
+ /* TTL I/O not initialised */
+ /***************************/
+
+ DPRINTK("TTL I/O not initialised\n");
+ i_ReturnValue = -6;
+ }
+ } else {
+ /********************************/
+ /* Selected digital input error */
+ /********************************/
+
+ DPRINTK("Selected digital input error\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /*******************************/
+ /* Selected TTL I/O port error */
+ /*******************************/
+
+ DPRINTK("Selected TTL I/O port error\n");
+ i_ReturnValue = -4;
+ }
+ break;
+
+ case APCI1710_TTL_READPORT:
+ pb_PortValue = (PBYTE) & data[0];
+ /********************************/
+ /* Test the TTL I/O port number */
+ /********************************/
+
+ if (((b_SelectedPort <= 2)
+ && ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration
+ [b_ModulNbr] &
+ 0xFFFF) ==
+ 0x3130))
+ || ((b_SelectedPort <= 3)
+ && ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration
+ [b_ModulNbr] &
+ 0xFFFF) >=
+ 0x3230))) {
+ /******************************************/
+ /* Test if the TTL I/O module initialised */
+ /******************************************/
+
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_TTLIOInfo.b_TTLInit == 1) {
+ /***********************************/
+ /* Test if TTL port used for input */
+ /***********************************/
+
+ if (((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration
+ [b_ModulNbr]
+ &
+ 0xFFFF)
+ == 0x3130)
+ || (((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF) >= 0x3230) && (devpriv->s_ModuleInfo[b_ModulNbr].s_TTLIOInfo.b_PortConfiguration[b_SelectedPort] == 0))) {
+ /**************************/
+ /* Read all digital input */
+ /**************************/
+
+ dw_StatusReg =
+ inl(devpriv->
+ s_BoardInfos.
+ ui_Address +
+ (64 * b_ModulNbr));
+
+ *pb_PortValue =
+ (BYTE) (
+ (dw_StatusReg >>
+ (8 * b_SelectedPort)) & 0xFF);
+ } else {
+ /*******************************/
+ /* Selected TTL I/O port error */
+ /*******************************/
+
+ DPRINTK("Selected TTL I/O port error\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /***************************/
+ /* TTL I/O not initialised */
+ /***************************/
+
+ DPRINTK("TTL I/O not initialised\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /*******************************/
+ /* Selected TTL I/O port error */
+ /*******************************/
+
+ DPRINTK("Selected TTL I/O port error\n");
+ i_ReturnValue = -4;
+ }
+ break;
+
+ default:
+ printk("Bad ReadType\n");
+
+ } //End Switch
+ } else {
+ /**********************************/
+ /* The module is not a TTL module */
+ /**********************************/
+
+ DPRINTK("The module is not a TTL module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI1710_InsnReadTTLIOAllPortValue(comedi_device
+*dev,struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read the status from all digital input ports |
+| (port A, port B and port C) from selected TTL |
+| module (b_ModulNbr) |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710|
+| BYTE_ b_ModulNbr : Module number to |
+| configure (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : PULONG_ pul_PortValue : Digital TTL inputs port |
+| status |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The module parameter is wrong |
+| -3: The module is not a TTL module |
+| -4: TTL I/O not initialised |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnReadTTLIOAllPortValue(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_StatusReg;
+ BYTE b_ModulNbr;
+ PULONG pul_PortValue;
+
+ b_ModulNbr = (BYTE) CR_AREF(insn->chanspec);
+ i_ReturnValue = insn->n;
+ pul_PortValue = (PULONG) & data[0];
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /**************************/
+ /* Test if TTL I/O module */
+ /**************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_TTL_IO) {
+ /******************************************/
+ /* Test if the TTL I/O module initialised */
+ /******************************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_TTLIOInfo.b_TTLInit == 1) {
+ /**************************/
+ /* Read all digital input */
+ /**************************/
+
+ dw_StatusReg = inl(devpriv->s_BoardInfos.
+ ui_Address + (64 * b_ModulNbr));
+
+ /**********************/
+ /* Test if TTL Rev1.0 */
+ /**********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration
+ [b_ModulNbr] & 0xFFFF) ==
+ 0x3130) {
+ *pul_PortValue =
+ dw_StatusReg & 0xFFFFFFUL;
+ } else {
+ /**************************************/
+ /* Test if port A not used for output */
+ /**************************************/
+
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_TTLIOInfo.
+ b_PortConfiguration[0] == 1) {
+ *pul_PortValue =
+ dw_StatusReg &
+ 0x3FFFF00UL;
+ }
+
+ /**************************************/
+ /* Test if port B not used for output */
+ /**************************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_TTLIOInfo.
+ b_PortConfiguration[1] == 1) {
+ *pul_PortValue =
+ dw_StatusReg &
+ 0x3FF00FFUL;
+ }
+
+ /**************************************/
+ /* Test if port C not used for output */
+ /**************************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_TTLIOInfo.
+ b_PortConfiguration[2] == 1) {
+ *pul_PortValue =
+ dw_StatusReg &
+ 0x300FFFFUL;
+ }
+
+ /**************************************/
+ /* Test if port D not used for output */
+ /**************************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModulNbr].
+ s_TTLIOInfo.
+ b_PortConfiguration[3] == 1) {
+ *pul_PortValue =
+ dw_StatusReg &
+ 0xFFFFFFUL;
+ }
+ }
+ } else {
+ /***************************/
+ /* TTL I/O not initialised */
+ /***************************/
+ DPRINTK("TTL I/O not initialised\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /**********************************/
+ /* The module is not a TTL module */
+ /**********************************/
+ DPRINTK("The module is not a TTL module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| OUTPUT FUNCTIONS |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : _INT_ i_APCI1710_SetTTLIOChlOn |
+| (BYTE_ b_BoardHandle, |
+| BYTE_ b_ModulNbr, |
+| BYTE_ b_OutputChannel)
+INT i_APCI1710_InsnWriteSetTTLIOChlOnOff(struct comedi_device *dev,struct comedi_subdevice *s,
+ struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Sets or resets the output witch has been passed with the |
+| parameter b_Channel. Setting an output means setting |
+| an ouput high. |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE_ b_BoardHandle : Handle of board APCI-1710 |
+| BYTE_ b_ModulNbr : Selected module number (0 to 3)|
+| BYTE_ b_OutputChannel : Selection from digital output |
+| channel (0 or 1) |
+| 0 : PD0 |
+| 1 : PD1 |
+| 2 to 9 : PA |
+| 10 to 17: PB |
+| 18 to 25: PC |
+
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ b_OutputChannel= CR_CHAN(insn->chanspec);
+ ui_State = data[0]; // ON or OFF
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -1: The handle parameter of the board is wrong |
+| -2: The module parameter is wrong |
+| -3: The module is not a TTL I/O module |
+| -4: The selected digital output is wrong |
+| -5: TTL I/O not initialised see function |
+| " i_APCI1710_InitTTLIO"
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1710_InsnWriteSetTTLIOChlOnOff(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = 0;
+ DWORD dw_StatusReg = 0;
+ BYTE b_ModulNbr;
+ BYTE b_OutputChannel;
+ UINT ui_State;
+
+ i_ReturnValue = insn->n;
+ b_ModulNbr = CR_AREF(insn->chanspec);
+ b_OutputChannel = CR_CHAN(insn->chanspec);
+ ui_State = data[0]; // ON or OFF
+
+ /**************************/
+ /* Test the module number */
+ /**************************/
+
+ if (b_ModulNbr < 4) {
+ /**************************/
+ /* Test if TTL I/O module */
+ /**************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModulNbr] &
+ 0xFFFF0000UL) == APCI1710_TTL_IO) {
+ /******************************************/
+ /* Test if the TTL I/O module initialised */
+ /******************************************/
+
+ if (devpriv->s_ModuleInfo[b_ModulNbr].
+ s_TTLIOInfo.b_TTLInit == 1) {
+ /***********************************/
+ /* Test the TTL I/O channel number */
+ /***********************************/
+
+ if (((b_OutputChannel <= 1)
+ && ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration
+ [b_ModulNbr] &
+ 0xFFFF) ==
+ 0x3130))
+ || ((b_OutputChannel <= 25)
+ && ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration
+ [b_ModulNbr] &
+ 0xFFFF) >=
+ 0x3230))) {
+ /****************************************************/
+ /* Test if the selected channel is a output channel */
+ /****************************************************/
+
+ if (((b_OutputChannel <= 1)
+ && (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TTLIOInfo.
+ b_PortConfiguration
+ [3] == 1))
+ || ((b_OutputChannel >= 2)
+ && (b_OutputChannel <=
+ 9)
+ && (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TTLIOInfo.
+ b_PortConfiguration
+ [0] == 1))
+ || ((b_OutputChannel >= 10)
+ && (b_OutputChannel <=
+ 17)
+ && (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TTLIOInfo.
+ b_PortConfiguration
+ [1] == 1))
+ || ((b_OutputChannel >= 18)
+ && (b_OutputChannel <=
+ 25)
+ && (devpriv->
+ s_ModuleInfo
+ [b_ModulNbr].
+ s_TTLIOInfo.
+ b_PortConfiguration
+ [2] == 1))) {
+ /************************/
+ /* Test if PD0 selected */
+ /************************/
+
+ if (b_OutputChannel == 0) {
+
+ outl(ui_State,
+ devpriv->
+ s_BoardInfos.
+ ui_Address +
+ (64 * b_ModulNbr));
+ } else {
+ /************************/
+ /* Test if PD1 selected */
+ /************************/
+
+ if (b_OutputChannel ==
+ 1) {
+
+ outl(ui_State,
+ devpriv->
+ s_BoardInfos.
+ ui_Address
+ + 4 +
+ (64 * b_ModulNbr));
+ } else {
+ b_OutputChannel
+ =
+ b_OutputChannel
+ - 2;
+
+ /********************/
+ /* Read all channel */
+ /********************/
+
+ dw_StatusReg =
+ inl
+ (devpriv->
+ s_BoardInfos.
+ ui_Address
+ +
+ (64 * b_ModulNbr));
+ if (ui_State) // ON
+ {
+ dw_StatusReg
+ =
+ (dw_StatusReg
+ >>
+ ((b_OutputChannel / 8) * 8)) & 0xFF;
+ dw_StatusReg
+ =
+ dw_StatusReg
+ |
+ (1
+ <<
+ (b_OutputChannel
+ %
+ 8));
+ } else // Off
+ {
+ dw_StatusReg
+ =
+ (dw_StatusReg
+ >>
+ ((b_OutputChannel / 8) * 8)) & 0xFF;
+ dw_StatusReg
+ =
+ dw_StatusReg
+ &
+ (0xFF
+ -
+ (1 << (b_OutputChannel % 8)));
+
+ }
+
+ /****************************/
+ /* Set the new output value */
+ /****************************/
+
+ outl(dw_StatusReg, devpriv->s_BoardInfos.ui_Address + 8 + ((b_OutputChannel / 8) * 4) + (64 * b_ModulNbr));
+ }
+ }
+ } else {
+ /************************************/
+ /* The selected TTL output is wrong */
+ /************************************/
+
+ DPRINTK(" The selected TTL output is wrong\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /************************************/
+ /* The selected TTL output is wrong */
+ /************************************/
+
+ DPRINTK("The selected TTL output is wrong\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /***************************/
+ /* TTL I/O not initialised */
+ /***************************/
+
+ DPRINTK("TTL I/O not initialised\n");
+ i_ReturnValue = -5;
+ }
+ } else {
+ /**************************************/
+ /* The module is not a TTL I/O module */
+ /**************************************/
+
+ DPRINTK("The module is not a TTL I/O module\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /***********************/
+ /* Module number error */
+ /***********************/
+
+ DPRINTK("Module number error\n");
+ i_ReturnValue = -2;
+ }
+
+ return (i_ReturnValue);
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/APCI1710_Ttl.h b/drivers/staging/comedi/drivers/addi-data/APCI1710_Ttl.h
new file mode 100644
index 000000000000..00915ddf9218
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/APCI1710_Ttl.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+#define APCI1710_TTL_INIT 0
+#define APCI1710_TTL_INITDIRECTION 1
+
+#define APCI1710_TTL_READCHANNEL 0
+#define APCI1710_TTL_READPORT 1
+
+/*
+ * TTL INISIALISATION FUNCTION
+ */
+INT i_APCI1710_InsnConfigInitTTLIO(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+/*
+ * TTL INPUT FUNCTION
+ */
+INT i_APCI1710_InsnBitsReadTTLIO(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI1710_InsnReadTTLIOAllPortValue(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+/*
+ * TTL OUTPUT FUNCTIONS
+ */
+INT i_APCI1710_InsnWriteSetTTLIOChlOnOff(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
diff --git a/drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.c b/drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.c
new file mode 100644
index 000000000000..b0907ec14667
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.c
@@ -0,0 +1,203 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-------------------------------+---------------------------------------+
+ | Project : ADDI HEADER READ WRITER | Compiler : Visual C++ |
+ | Module name : S5920.cpp | Version : 6.0 |
+ +-------------------------------+---------------------------------------+
+ | Author : E. LIBS Date : 02/05/2002 |
+ +-----------------------------------------------------------------------+
+ | Description : DLL with the S5920 PCI Controller functions |
+ +-----------------------------------------------------------------------+
+ | UPDATE'S |
+ +-----------------------------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | 28/08/02 | LIBS Eric | Add return codes each time a function of the |
+ | | | Addi Library is called |
+ +-----------------------------------------------------------------------+
+ | 31/07/03 | KRAUTH J. | Changes for the MSX-Box |
+ +-----------------------------------------------------------------------+
+*/
+
+#include "addi_amcc_S5920.h"
+
+/*+----------------------------------------------------------------------------+*/
+/*| Function Name : INT i_AddiHeaderRW_ReadEeprom |*/
+/*| (INT i_NbOfWordsToRead, |*/
+/*| DWORD dw_PCIBoardEepromAddress, |*/
+/*| WORD w_EepromStartAddress, |*/
+/*| PWORD pw_DataRead) |*/
+/*+----------------------------------------------------------------------------+*/
+/*| Task : Read word from the 5920 eeprom. |*/
+/*+----------------------------------------------------------------------------+*/
+/*| Input Parameters : INT i_NbOfWordsToRead : Nbr. of word to read |*/
+/*| DWORD dw_PCIBoardEepromAddress : Address of the eeprom |*/
+/*| WORD w_EepromStartAddress : Eeprom strat address |*/
+/*+----------------------------------------------------------------------------+*/
+/*| Output Parameters : PWORD pw_DataRead : Read data |*/
+/*+----------------------------------------------------------------------------+*/
+/*| Return Value : - |*/
+/*+----------------------------------------------------------------------------+*/
+
+INT i_AddiHeaderRW_ReadEeprom(INT i_NbOfWordsToRead,
+ DWORD dw_PCIBoardEepromAddress,
+ WORD w_EepromStartAddress, PWORD pw_DataRead)
+{
+ DWORD dw_eeprom_busy = 0;
+ INT i_Counter = 0;
+ INT i_WordCounter;
+ INT i;
+ BYTE pb_ReadByte[1];
+ BYTE b_ReadLowByte = 0;
+ BYTE b_ReadHighByte = 0;
+ BYTE b_SelectedAddressLow = 0;
+ BYTE b_SelectedAddressHigh = 0;
+ WORD w_ReadWord = 0;
+
+ for (i_WordCounter = 0; i_WordCounter < i_NbOfWordsToRead;
+ i_WordCounter++) {
+ do {
+ dw_eeprom_busy =
+ inl(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR);
+ dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
+ }
+ while (dw_eeprom_busy == EEPROM_BUSY);
+
+ for (i_Counter = 0; i_Counter < 2; i_Counter++) {
+ b_SelectedAddressLow = (w_EepromStartAddress + i_Counter) % 256; //Read the low 8 bit part
+ b_SelectedAddressHigh = (w_EepromStartAddress + i_Counter) / 256; //Read the high 8 bit part
+
+ //Select the load low address mode
+ outb(NVCMD_LOAD_LOW,
+ dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
+ 3);
+
+ //Wait on busy
+ do {
+ dw_eeprom_busy =
+ inl(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR);
+ dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
+ }
+ while (dw_eeprom_busy == EEPROM_BUSY);
+
+ //Load the low address
+ outb(b_SelectedAddressLow,
+ dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
+ 2);
+
+ //Wait on busy
+ do {
+ dw_eeprom_busy =
+ inl(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR);
+ dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
+ }
+ while (dw_eeprom_busy == EEPROM_BUSY);
+
+ //Select the load high address mode
+ outb(NVCMD_LOAD_HIGH,
+ dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
+ 3);
+
+ //Wait on busy
+ do {
+ dw_eeprom_busy =
+ inl(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR);
+ dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
+ }
+ while (dw_eeprom_busy == EEPROM_BUSY);
+
+ //Load the high address
+ outb(b_SelectedAddressHigh,
+ dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
+ 2);
+
+ //Wait on busy
+ do {
+ dw_eeprom_busy =
+ inl(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR);
+ dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
+ }
+ while (dw_eeprom_busy == EEPROM_BUSY);
+
+ //Select the READ mode
+ outb(NVCMD_BEGIN_READ,
+ dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
+ 3);
+
+ //Wait on busy
+ do {
+ dw_eeprom_busy =
+ inl(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR);
+ dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
+ }
+ while (dw_eeprom_busy == EEPROM_BUSY);
+
+ //Read data into the EEPROM
+ *pb_ReadByte =
+ inb(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR + 2);
+
+ //Wait on busy
+ do {
+ dw_eeprom_busy =
+ inl(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR);
+ dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
+ }
+ while (dw_eeprom_busy == EEPROM_BUSY);
+
+ //Select the upper address part
+ if (i_Counter == 0) {
+ b_ReadLowByte = pb_ReadByte[0];
+ } else {
+ b_ReadHighByte = pb_ReadByte[0];
+ }
+
+ //Sleep
+ for (i = 0; i < 10000; i++) ;
+
+ }
+ w_ReadWord =
+ (b_ReadLowByte | (((unsigned short)b_ReadHighByte) *
+ 256));
+
+ pw_DataRead[i_WordCounter] = w_ReadWord;
+
+ w_EepromStartAddress += 2; // to read the next word
+
+ } // for (...) i_NbOfWordsToRead
+ return (0);
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.h b/drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.h
new file mode 100644
index 000000000000..9ae56bc3adad
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+#define AMCC_OP_REG_MCSR 0x3c
+#define EEPROM_BUSY 0x80000000
+#define NVCMD_LOAD_LOW (0x4 << 5) /* nvRam load low command */
+#define NVCMD_LOAD_HIGH (0x5 << 5) /* nvRam load high command */
+#define NVCMD_BEGIN_READ (0x7 << 5) /* nvRam begin read command */
+#define NVCMD_BEGIN_WRITE (0x6 << 5) /* EEPROM begin write command */
+
+INT i_AddiHeaderRW_ReadEeprom(INT i_NbOfWordsToRead,
+ DWORD dw_PCIBoardEepromAddress,
+ WORD w_EepromStartAddress, PWORD pw_DataRead);
diff --git a/drivers/staging/comedi/drivers/addi-data/addi_amcc_s5933.h b/drivers/staging/comedi/drivers/addi-data/addi_amcc_s5933.h
new file mode 100644
index 000000000000..b50774cdcf77
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/addi_amcc_s5933.h
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+/* Header file for AMCC s 5933 */
+
+#ifndef _AMCC_S5933_H_
+#define _AMCC_S5933_H_
+
+#include "../../comedidev.h"
+
+#include "../comedi_pci.h"
+
+#ifdef PCI_SUPPORT_VER1
+#error No support for 2.1.55 and older
+#endif
+
+/* written on base0 */
+#define FIFO_ADVANCE_ON_BYTE_2 0x20000000
+
+/* added for step 6 dma written on base2 */
+#define AMWEN_ENABLE 0x02
+
+#define A2P_FIFO_WRITE_ENABLE 0x01
+
+/* for transfer count enable bit */
+#define AGCSTS_TC_ENABLE 0x10000000
+
+/*
+ * ADDON RELATED ADDITIONS
+ */
+/* Constant */
+#define APCI3120_ENABLE_TRANSFER_ADD_ON_LOW 0x00
+#define APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH 0x1200
+#define APCI3120_A2P_FIFO_MANAGEMENT 0x04000400L
+#define APCI3120_AMWEN_ENABLE 0x02
+#define APCI3120_A2P_FIFO_WRITE_ENABLE 0x01
+#define APCI3120_FIFO_ADVANCE_ON_BYTE_2 0x20000000L
+#define APCI3120_ENABLE_WRITE_TC_INT 0x00004000L
+#define APCI3120_CLEAR_WRITE_TC_INT 0x00040000L
+#define APCI3120_DISABLE_AMWEN_AND_A2P_FIFO_WRITE 0x0
+#define APCI3120_DISABLE_BUS_MASTER_ADD_ON 0x0
+#define APCI3120_DISABLE_BUS_MASTER_PCI 0x0
+
+/* ADD_ON ::: this needed since apci supports 16 bit interface to add on */
+#define APCI3120_ADD_ON_AGCSTS_LOW 0x3C
+#define APCI3120_ADD_ON_AGCSTS_HIGH (APCI3120_ADD_ON_AGCSTS_LOW + 2)
+#define APCI3120_ADD_ON_MWAR_LOW 0x24
+#define APCI3120_ADD_ON_MWAR_HIGH (APCI3120_ADD_ON_MWAR_LOW + 2)
+#define APCI3120_ADD_ON_MWTC_LOW 0x058
+#define APCI3120_ADD_ON_MWTC_HIGH (APCI3120_ADD_ON_MWTC_LOW + 2)
+
+/* AMCC */
+#define APCI3120_AMCC_OP_MCSR 0x3C
+#define APCI3120_AMCC_OP_REG_INTCSR 0x38
+
+/*
+ * AMCC Operation Register Offsets - PCI
+ */
+#define AMCC_OP_REG_OMB1 0x00
+#define AMCC_OP_REG_OMB2 0x04
+#define AMCC_OP_REG_OMB3 0x08
+#define AMCC_OP_REG_OMB4 0x0c
+#define AMCC_OP_REG_IMB1 0x10
+#define AMCC_OP_REG_IMB2 0x14
+#define AMCC_OP_REG_IMB3 0x18
+#define AMCC_OP_REG_IMB4 0x1c
+#define AMCC_OP_REG_FIFO 0x20
+#define AMCC_OP_REG_MWAR 0x24
+#define AMCC_OP_REG_MWTC 0x28
+#define AMCC_OP_REG_MRAR 0x2c
+#define AMCC_OP_REG_MRTC 0x30
+#define AMCC_OP_REG_MBEF 0x34
+#define AMCC_OP_REG_INTCSR 0x38
+/* INT source */
+#define AMCC_OP_REG_INTCSR_SRC (AMCC_OP_REG_INTCSR + 2)
+/* FIFO ctrl */
+#define AMCC_OP_REG_INTCSR_FEC (AMCC_OP_REG_INTCSR + 3)
+#define AMCC_OP_REG_MCSR 0x3c
+/* Data in byte 2 */
+#define AMCC_OP_REG_MCSR_NVDATA (AMCC_OP_REG_MCSR + 2)
+/* Command in byte 3 */
+#define AMCC_OP_REG_MCSR_NVCMD (AMCC_OP_REG_MCSR + 3)
+
+#define AMCC_FIFO_DEPTH_DWORD 8
+#define AMCC_FIFO_DEPTH_BYTES (8 * sizeof(u32))
+
+/*
+ * AMCC Operation Registers Size - PCI
+ */
+#define AMCC_OP_REG_SIZE 64 /* in bytes */
+
+/*
+ * AMCC Operation Register Offsets - Add-on
+ */
+#define AMCC_OP_REG_AIMB1 0x00
+#define AMCC_OP_REG_AIMB2 0x04
+#define AMCC_OP_REG_AIMB3 0x08
+#define AMCC_OP_REG_AIMB4 0x0c
+#define AMCC_OP_REG_AOMB1 0x10
+#define AMCC_OP_REG_AOMB2 0x14
+#define AMCC_OP_REG_AOMB3 0x18
+#define AMCC_OP_REG_AOMB4 0x1c
+#define AMCC_OP_REG_AFIFO 0x20
+#define AMCC_OP_REG_AMWAR 0x24
+#define AMCC_OP_REG_APTA 0x28
+#define AMCC_OP_REG_APTD 0x2c
+#define AMCC_OP_REG_AMRAR 0x30
+#define AMCC_OP_REG_AMBEF 0x34
+#define AMCC_OP_REG_AINT 0x38
+#define AMCC_OP_REG_AGCSTS 0x3c
+#define AMCC_OP_REG_AMWTC 0x58
+#define AMCC_OP_REG_AMRTC 0x5c
+
+/*
+ * AMCC - Add-on General Control/Status Register
+ */
+#define AGCSTS_CONTROL_MASK 0xfffff000
+#define AGCSTS_NV_ACC_MASK 0xe0000000
+#define AGCSTS_RESET_MASK 0x0e000000
+#define AGCSTS_NV_DA_MASK 0x00ff0000
+#define AGCSTS_BIST_MASK 0x0000f000
+#define AGCSTS_STATUS_MASK 0x000000ff
+#define AGCSTS_TCZERO_MASK 0x000000c0
+#define AGCSTS_FIFO_ST_MASK 0x0000003f
+
+#define AGCSTS_RESET_MBFLAGS 0x08000000
+#define AGCSTS_RESET_P2A_FIFO 0x04000000
+#define AGCSTS_RESET_A2P_FIFO 0x02000000
+#define AGCSTS_RESET_FIFOS (AGCSTS_RESET_A2P_FIFO | AGCSTS_RESET_P2A_FIFO)
+
+#define AGCSTS_A2P_TCOUNT 0x00000080
+#define AGCSTS_P2A_TCOUNT 0x00000040
+
+#define AGCSTS_FS_P2A_EMPTY 0x00000020
+#define AGCSTS_FS_P2A_HALF 0x00000010
+#define AGCSTS_FS_P2A_FULL 0x00000008
+
+#define AGCSTS_FS_A2P_EMPTY 0x00000004
+#define AGCSTS_FS_A2P_HALF 0x00000002
+#define AGCSTS_FS_A2P_FULL 0x00000001
+
+/*
+ * AMCC - Add-on Interrupt Control/Status Register
+ */
+#define AINT_INT_MASK 0x00ff0000
+#define AINT_SEL_MASK 0x0000ffff
+#define AINT_IS_ENSEL_MASK 0x00001f1f
+
+#define AINT_INT_ASSERTED 0x00800000
+#define AINT_BM_ERROR 0x00200000
+#define AINT_BIST_INT 0x00100000
+
+#define AINT_RT_COMPLETE 0x00080000
+#define AINT_WT_COMPLETE 0x00040000
+
+#define AINT_OUT_MB_INT 0x00020000
+#define AINT_IN_MB_INT 0x00010000
+
+#define AINT_READ_COMPL 0x00008000
+#define AINT_WRITE_COMPL 0x00004000
+
+#define AINT_OMB_ENABLE 0x00001000
+#define AINT_OMB_SELECT 0x00000c00
+#define AINT_OMB_BYTE 0x00000300
+
+#define AINT_IMB_ENABLE 0x00000010
+#define AINT_IMB_SELECT 0x0000000c
+#define AINT_IMB_BYTE 0x00000003
+
+/* Enable Bus Mastering */
+#define EN_A2P_TRANSFERS 0x00000400
+/* FIFO Flag Reset */
+#define RESET_A2P_FLAGS 0x04000000L
+/* FIFO Relative Priority */
+#define A2P_HI_PRIORITY 0x00000100L
+/* Identify Interrupt Sources */
+#define ANY_S593X_INT 0x00800000L
+#define READ_TC_INT 0x00080000L
+#define WRITE_TC_INT 0x00040000L
+#define IN_MB_INT 0x00020000L
+#define MASTER_ABORT_INT 0x00100000L
+#define TARGET_ABORT_INT 0x00200000L
+#define BUS_MASTER_INT 0x00200000L
+
+/****************************************************************************/
+
+struct pcilst_struct {
+ struct pcilst_struct *next;
+ int used;
+ struct pci_dev *pcidev;
+ unsigned short vendor;
+ unsigned short device;
+ unsigned char pci_bus;
+ unsigned char pci_slot;
+ unsigned char pci_func;
+ resource_size_t io_addr[5];
+ unsigned int irq;
+};
+
+/* ptr to root list of all amcc devices */
+struct pcilst_struct *amcc_devices;
+
+static const int i_ADDIDATADeviceID[] = { 0x15B8, 0x10E8 };
+
+/****************************************************************************/
+
+void v_pci_card_list_init(unsigned short pci_vendor, char display);
+void v_pci_card_list_cleanup(unsigned short pci_vendor);
+struct pcilst_struct *ptr_find_free_pci_card_by_device(unsigned short vendor_id,
+ unsigned short
+ device_id);
+int i_find_free_pci_card_by_position(unsigned short vendor_id,
+ unsigned short device_id,
+ unsigned short pci_bus,
+ unsigned short pci_slot,
+ struct pcilst_struct **card);
+struct pcilst_struct *ptr_select_and_alloc_pci_card(unsigned short vendor_id,
+ unsigned short device_id,
+ unsigned short pci_bus,
+ unsigned short pci_slot,
+ int i_Master);
+
+int pci_card_alloc(struct pcilst_struct *amcc, int master);
+int i_pci_card_free(struct pcilst_struct *amcc);
+void v_pci_card_list_display(void);
+int i_pci_card_data(struct pcilst_struct *amcc,
+ unsigned char *pci_bus, unsigned char *pci_slot,
+ unsigned char *pci_func, resource_size_t * io_addr,
+ unsigned int *irq);
+
+/****************************************************************************/
+
+/* build list of amcc cards in this system */
+void v_pci_card_list_init(unsigned short pci_vendor, char display)
+{
+ struct pci_dev *pcidev;
+ struct pcilst_struct *amcc, *last;
+ int i;
+ int i_Count = 0;
+ amcc_devices = NULL;
+ last = NULL;
+
+ for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pcidev != NULL;
+ pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
+ for (i_Count = 0; i_Count < 2; i_Count++) {
+ pci_vendor = i_ADDIDATADeviceID[i_Count];
+ if (pcidev->vendor == pci_vendor) {
+ amcc = kmalloc(sizeof(*amcc), GFP_KERNEL);
+ memset(amcc, 0, sizeof(*amcc));
+
+ amcc->pcidev = pcidev;
+ if (last)
+ last->next = amcc;
+ else
+ amcc_devices = amcc;
+ last = amcc;
+
+ amcc->vendor = pcidev->vendor;
+ amcc->device = pcidev->device;
+ amcc->pci_bus = pcidev->bus->number;
+ amcc->pci_slot = PCI_SLOT(pcidev->devfn);
+ amcc->pci_func = PCI_FUNC(pcidev->devfn);
+ /* Note: resources may be invalid if PCI device
+ * not enabled, but they are corrected in
+ * pci_card_alloc. */
+ for (i = 0; i < 5; i++)
+ amcc->io_addr[i] =
+ pci_resource_start(pcidev, i);
+ amcc->irq = pcidev->irq;
+
+ }
+ }
+ }
+
+ if (display)
+ v_pci_card_list_display();
+}
+
+/****************************************************************************/
+/* free up list of amcc cards in this system */
+void v_pci_card_list_cleanup(unsigned short pci_vendor)
+{
+ struct pcilst_struct *amcc, *next;
+
+ for (amcc = amcc_devices; amcc; amcc = next) {
+ next = amcc->next;
+ kfree(amcc);
+ }
+
+ amcc_devices = NULL;
+}
+
+/****************************************************************************/
+/* find first unused card with this device_id */
+struct pcilst_struct *ptr_find_free_pci_card_by_device(unsigned short vendor_id,
+ unsigned short device_id)
+{
+ struct pcilst_struct *amcc, *next;
+
+ for (amcc = amcc_devices; amcc; amcc = next) {
+ next = amcc->next;
+ if ((!amcc->used) && (amcc->device == device_id)
+ && (amcc->vendor == vendor_id))
+ return amcc;
+
+ }
+
+ return NULL;
+}
+
+/****************************************************************************/
+/* find card on requested position */
+int i_find_free_pci_card_by_position(unsigned short vendor_id,
+ unsigned short device_id,
+ unsigned short pci_bus,
+ unsigned short pci_slot,
+ struct pcilst_struct **card)
+{
+ struct pcilst_struct *amcc, *next;
+
+ *card = NULL;
+ for (amcc = amcc_devices; amcc; amcc = next) {
+ next = amcc->next;
+ if ((amcc->vendor == vendor_id) && (amcc->device == device_id)
+ && (amcc->pci_bus == pci_bus)
+ && (amcc->pci_slot == pci_slot)) {
+ if (!(amcc->used)) {
+ *card = amcc;
+ return 0; /* ok, card is found */
+ } else {
+ rt_printk(" - \nCard on requested position is used b:s %d:%d!\n",
+ pci_bus, pci_slot);
+ return 2; /* card exist but is used */
+ }
+ }
+ }
+
+ /* no card found */
+ return 1;
+}
+
+/****************************************************************************/
+/* mark card as used */
+int pci_card_alloc(struct pcilst_struct *amcc, int master)
+{
+ int i;
+
+ if (!amcc)
+ return -1;
+
+ if (amcc->used)
+ return 1;
+ if (comedi_pci_enable(amcc->pcidev, "addi_amcc_s5933"))
+ return -1;
+ /* Resources will be accurate now. */
+ for (i = 0; i < 5; i++)
+ amcc->io_addr[i] = pci_resource_start(amcc->pcidev, i);
+ if (master)
+ pci_set_master(amcc->pcidev);
+ amcc->used = 1;
+
+ return 0;
+}
+
+/****************************************************************************/
+/* mark card as free */
+int i_pci_card_free(struct pcilst_struct *amcc)
+{
+ if (!amcc)
+ return -1;
+
+ if (!amcc->used)
+ return 1;
+ amcc->used = 0;
+ comedi_pci_disable(amcc->pcidev);
+ return 0;
+}
+
+/****************************************************************************/
+/* display list of found cards */
+void v_pci_card_list_display(void)
+{
+ struct pcilst_struct *amcc, *next;
+
+ printk(KERN_DEBUG "List of pci cards\n");
+ printk(KERN_DEBUG "bus:slot:func vendor device io_amcc io_daq irq used\n");
+
+ for (amcc = amcc_devices; amcc; amcc = next) {
+ next = amcc->next;
+ printk
+ ("%2d %2d %2d 0x%4x 0x%4x 0x%8llx 0x%8llx %2u %2d\n",
+ amcc->pci_bus, amcc->pci_slot, amcc->pci_func,
+ amcc->vendor, amcc->device,
+ (unsigned long long)amcc->io_addr[0],
+ (unsigned long long)amcc->io_addr[2], amcc->irq,
+ amcc->used);
+
+ }
+}
+
+/****************************************************************************/
+/* return all card information for driver */
+int i_pci_card_data(struct pcilst_struct *amcc,
+ unsigned char *pci_bus, unsigned char *pci_slot,
+ unsigned char *pci_func, resource_size_t * io_addr,
+ unsigned int *irq)
+{
+ int i;
+
+ if (!amcc)
+ return -1;
+ *pci_bus = amcc->pci_bus;
+ *pci_slot = amcc->pci_slot;
+ *pci_func = amcc->pci_func;
+ for (i = 0; i < 5; i++)
+ io_addr[i] = amcc->io_addr[i];
+ *irq = amcc->irq;
+ return 0;
+}
+
+/****************************************************************************/
+/* select and alloc card */
+struct pcilst_struct *ptr_select_and_alloc_pci_card(unsigned short vendor_id,
+ unsigned short device_id,
+ unsigned short pci_bus,
+ unsigned short pci_slot,
+ int i_Master)
+{
+ struct pcilst_struct *card;
+
+ if ((pci_bus < 1) & (pci_slot < 1)) {
+ /* use autodetection */
+ card = ptr_find_free_pci_card_by_device(vendor_id, device_id);
+ if (card == NULL) {
+ rt_printk(" - Unused card not found in system!\n");
+ return NULL;
+ }
+ } else {
+ switch (i_find_free_pci_card_by_position(vendor_id, device_id,
+ pci_bus, pci_slot,
+ &card)) {
+ case 1:
+ rt_printk(" - Card not found on requested position b:s %d:%d!\n",
+ pci_bus, pci_slot);
+ return NULL;
+ case 2:
+ rt_printk(" - Card on requested position is used b:s %d:%d!\n",
+ pci_bus, pci_slot);
+ return NULL;
+ }
+ }
+
+ if (pci_card_alloc(card, i_Master) != 0) {
+ rt_printk(" - Can't allocate card!\n");
+ return NULL;
+
+ }
+
+ return card;
+}
+#endif
diff --git a/drivers/staging/comedi/drivers/addi-data/addi_common.c b/drivers/staging/comedi/drivers/addi-data/addi_common.c
new file mode 100644
index 000000000000..618c69b6838d
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/addi_common.c
@@ -0,0 +1,3062 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstrasse 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-----------------------------------------------------------------------+
+ | Project : ADDI DATA | Compiler : GCC |
+ | Modulname : addi_common.c | Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Author : | Date : |
+ +-----------------------------------------------------------------------+
+ | Description : ADDI COMMON Main Module |
+ +-----------------------------------------------------------------------+
+ | CONFIG OPTIONS |
+ | option[0] - PCI bus number - if bus number and slot number are 0, |
+ | then driver search for first unused card |
+ | option[1] - PCI slot number |
+ | |
+ | option[2] = 0 - DMA ENABLE |
+ | = 1 - DMA DISABLE |
+ +----------+-----------+------------------------------------------------+
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include "../../comedidev.h"
+#include <asm/io.h>
+#if defined(CONFIG_APCI_1710) || defined(CONFIG_APCI_3200) || defined(CONFIG_APCI_3300)
+#include <asm/i387.h>
+#endif
+#include "../comedi_fc.h"
+
+#include "addi_common.h"
+#include "addi_amcc_s5933.h"
+
+//Update-0.7.57->0.7.68MODULE_AUTHOR("ADDI-DATA GmbH <info@addi-data.com>");
+//Update-0.7.57->0.7.68MODULE_DESCRIPTION("Comedi ADDI-DATA module");
+//Update-0.7.57->0.7.68MODULE_LICENSE("GPL");
+
+#define devpriv ((addi_private *)dev->private)
+#define this_board ((boardtype *)dev->board_ptr)
+
+#if defined(CONFIG_APCI_1710) || defined(CONFIG_APCI_3200) || defined(CONFIG_APCI_3300)
+//BYTE b_SaveFPUReg [94];
+
+void fpu_begin(void)
+{
+ //asm ("fstenv b_SaveFPUReg");
+ kernel_fpu_begin();
+}
+
+void fpu_end(void)
+{
+ // asm ("frstor b_SaveFPUReg");
+ kernel_fpu_end();
+}
+#endif
+
+#include "addi_eeprom.c"
+#if (defined (CONFIG_APCI_3120) || defined (CONFIG_APCI_3001))
+#include "hwdrv_apci3120.c"
+#endif
+#ifdef CONFIG_APCI_1032
+#include "hwdrv_apci1032.c"
+#endif
+#ifdef CONFIG_APCI_1516
+#include "hwdrv_apci1516.c"
+#endif
+#ifdef CONFIG_APCI_2016
+#include "hwdrv_apci2016.c"
+#endif
+#ifdef CONFIG_APCI_2032
+#include "hwdrv_apci2032.c"
+#endif
+#ifdef CONFIG_APCI_2200
+#include "hwdrv_apci2200.c"
+#endif
+#ifdef CONFIG_APCI_1564
+#include "hwdrv_apci1564.c"
+#endif
+#ifdef CONFIG_APCI_1500
+#include "hwdrv_apci1500.c"
+#endif
+#ifdef CONFIG_APCI_3501
+#include "hwdrv_apci3501.c"
+#endif
+#ifdef CONFIG_APCI_035
+#include "hwdrv_apci035.c"
+#endif
+#if (defined (CONFIG_APCI_3200) || defined (CONFIG_APCI_3300))
+#include "hwdrv_apci3200.c"
+#endif
+#ifdef CONFIG_APCI_1710
+#include "hwdrv_APCI1710.c"
+#endif
+#ifdef CONFIG_APCI_16XX
+#include "hwdrv_apci16xx.c"
+#endif
+#ifdef CONFIG_APCI_3XXX
+#include "hwdrv_apci3xxx.c"
+#endif
+
+#ifndef COMEDI_SUBD_TTLIO
+#define COMEDI_SUBD_TTLIO 11 /* Digital Input Output But TTL */
+#endif
+
+static DEFINE_PCI_DEVICE_TABLE(addi_apci_tbl) = {
+#ifdef CONFIG_APCI_3120
+ {APCI3120_BOARD_VENDOR_ID, 0x818D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+#ifdef CONFIG_APCI_1032
+ {APCI1032_BOARD_VENDOR_ID, 0x1003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+#ifdef CONFIG_APCI_1516
+ {APCI1516_BOARD_VENDOR_ID, 0x1001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+#ifdef CONFIG_APCI_2016
+ {APCI2016_BOARD_VENDOR_ID, 0x1002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+#ifdef CONFIG_APCI_2032
+ {APCI2032_BOARD_VENDOR_ID, 0x1004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+#ifdef CONFIG_APCI_2200
+ {APCI2200_BOARD_VENDOR_ID, 0x1005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+#ifdef CONFIG_APCI_1564
+ {APCI1564_BOARD_VENDOR_ID, 0x1006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+#ifdef CONFIG_APCI_1500
+ {APCI1500_BOARD_VENDOR_ID, 0x80fc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+#ifdef CONFIG_APCI_3001
+ {APCI3120_BOARD_VENDOR_ID, 0x828D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+#ifdef CONFIG_APCI_3501
+ {APCI3501_BOARD_VENDOR_ID, 0x3001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+#ifdef CONFIG_APCI_035
+ {APCI035_BOARD_VENDOR_ID, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+#ifdef CONFIG_APCI_3200
+ {APCI3200_BOARD_VENDOR_ID, 0x3000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+#ifdef CONFIG_APCI_3300
+ {APCI3200_BOARD_VENDOR_ID, 0x3007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+#ifdef CONFIG_APCI_1710
+ {APCI1710_BOARD_VENDOR_ID, APCI1710_BOARD_DEVICE_ID,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+#ifdef CONFIG_APCI_16XX
+ {0x15B8, 0x1009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x100A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+#ifdef CONFIG_APCI_3XXX
+ {0x15B8, 0x3010, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x300F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x300E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x3013, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x3014, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x3015, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x3016, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x3017, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x3018, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x3019, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x301A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x301B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x301C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x301D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x301E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x301F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x3020, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x3021, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x3022, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x3023, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x300B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x3002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x3003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x3004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0x15B8, 0x3024, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+#endif
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, addi_apci_tbl);
+
+static const boardtype boardtypes[] = {
+#ifdef CONFIG_APCI_3120
+ {"apci3120",
+ APCI3120_BOARD_VENDOR_ID,
+ 0x818D,
+ AMCC_OP_REG_SIZE,
+ APCI3120_ADDRESS_RANGE,
+ 8,
+ 0,
+ ADDIDATA_NO_EEPROM,
+ NULL,
+ 16,
+ 8,
+ 16,
+ 8,
+ 0xffff,
+ 0x3fff,
+ &range_apci3120_ai,
+ &range_apci3120_ao,
+ 4,
+ 4,
+ 0x0f,
+ 0,
+ NULL,
+ 1,
+ 1,
+ 1,
+ 10000,
+ 100000,
+ v_APCI3120_Interrupt,
+ i_APCI3120_Reset,
+ i_APCI3120_InsnConfigAnalogInput,
+ i_APCI3120_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ i_APCI3120_CommandTestAnalogInput,
+ i_APCI3120_CommandAnalogInput,
+ i_APCI3120_StopCyclicAcquisition,
+ NULL,
+ i_APCI3120_InsnWriteAnalogOutput,
+ NULL,
+ NULL,
+ i_APCI3120_InsnReadDigitalInput,
+ NULL,
+ i_APCI3120_InsnBitsDigitalInput,
+ i_APCI3120_InsnConfigDigitalOutput,
+ i_APCI3120_InsnWriteDigitalOutput,
+ i_APCI3120_InsnBitsDigitalOutput,
+ NULL,
+ i_APCI3120_InsnConfigTimer,
+ i_APCI3120_InsnWriteTimer,
+ i_APCI3120_InsnReadTimer,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+#endif
+#ifdef CONFIG_APCI_1032
+ {"apci1032",
+ APCI1032_BOARD_VENDOR_ID,
+ 0x1003,
+ 4,
+ APCI1032_ADDRESS_RANGE,
+ 0,
+ 0,
+ ADDIDATA_EEPROM,
+ ADDIDATA_93C76,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 32,
+ 0,
+ 0,
+ 0,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ v_APCI1032_Interrupt,
+ i_APCI1032_Reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI1032_ConfigDigitalInput,
+ i_APCI1032_Read1DigitalInput,
+ NULL,
+ i_APCI1032_ReadMoreDigitalInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+#endif
+#ifdef CONFIG_APCI_1516
+ {"apci1516",
+ APCI1516_BOARD_VENDOR_ID,
+ 0x1001,
+ 128,
+ APCI1516_ADDRESS_RANGE,
+ 32,
+ 0,
+ ADDIDATA_EEPROM,
+ ADDIDATA_S5920,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ 8,
+ 8,
+ 0,
+ 0,
+ NULL,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ NULL,
+ i_APCI1516_Reset,
+ NULL, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI1516_Read1DigitalInput,
+ NULL,
+ i_APCI1516_ReadMoreDigitalInput,
+ i_APCI1516_ConfigDigitalOutput,
+ i_APCI1516_WriteDigitalOutput,
+ i_APCI1516_ReadDigitalOutput,
+ NULL,
+ i_APCI1516_ConfigWatchdog,
+ i_APCI1516_StartStopWriteWatchdog,
+ i_APCI1516_ReadWatchdog,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+#endif
+#ifdef CONFIG_APCI_2016
+ {"apci2016",
+ APCI2016_BOARD_VENDOR_ID,
+ 0x1002,
+ 128,
+ APCI2016_ADDRESS_RANGE,
+ 32,
+ 0,
+ ADDIDATA_EEPROM,
+ ADDIDATA_S5920,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ 16,
+ 0,
+ 0,
+ NULL,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ NULL,
+ i_APCI2016_Reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI2016_ConfigDigitalOutput,
+ i_APCI2016_WriteDigitalOutput,
+ i_APCI2016_BitsDigitalOutput,
+ NULL,
+ i_APCI2016_ConfigWatchdog,
+ i_APCI2016_StartStopWriteWatchdog,
+ i_APCI2016_ReadWatchdog,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+#endif
+#ifdef CONFIG_APCI_2032
+ {"apci2032",
+ APCI2032_BOARD_VENDOR_ID,
+ 0x1004,
+ 4,
+ APCI2032_ADDRESS_RANGE,
+ 0,
+ 0,
+ ADDIDATA_EEPROM,
+ ADDIDATA_93C76,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ 32,
+ 0xffffffff,
+ 0,
+ NULL,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ v_APCI2032_Interrupt,
+ i_APCI2032_Reset,
+ NULL, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI2032_ConfigDigitalOutput,
+ i_APCI2032_WriteDigitalOutput,
+ i_APCI2032_ReadDigitalOutput,
+ i_APCI2032_ReadInterruptStatus,
+ i_APCI2032_ConfigWatchdog,
+ i_APCI2032_StartStopWriteWatchdog,
+ i_APCI2032_ReadWatchdog,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+#endif
+#ifdef CONFIG_APCI_2200
+ {"apci2200",
+ APCI2200_BOARD_VENDOR_ID,
+ 0x1005,
+ 4,
+ APCI2200_ADDRESS_RANGE,
+ 0,
+ 0,
+ ADDIDATA_EEPROM,
+ ADDIDATA_93C76,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ 8,
+ 16,
+ 0,
+ 0,
+ NULL,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ NULL,
+ i_APCI2200_Reset,
+ NULL, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI2200_Read1DigitalInput,
+ NULL,
+ i_APCI2200_ReadMoreDigitalInput,
+ i_APCI2200_ConfigDigitalOutput,
+ i_APCI2200_WriteDigitalOutput,
+ i_APCI2200_ReadDigitalOutput,
+ NULL,
+ i_APCI2200_ConfigWatchdog,
+ i_APCI2200_StartStopWriteWatchdog,
+ i_APCI2200_ReadWatchdog,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+#endif
+#ifdef CONFIG_APCI_1564
+ {"apci1564",
+ APCI1564_BOARD_VENDOR_ID,
+ 0x1006,
+ 128,
+ APCI1564_ADDRESS_RANGE,
+ 0,
+ 0,
+ ADDIDATA_EEPROM,
+ ADDIDATA_93C76,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ 32,
+ 32,
+ 0xffffffff,
+ 0,
+ NULL,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ v_APCI1564_Interrupt,
+ i_APCI1564_Reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI1564_ConfigDigitalInput,
+ i_APCI1564_Read1DigitalInput,
+ NULL,
+ i_APCI1564_ReadMoreDigitalInput,
+ i_APCI1564_ConfigDigitalOutput,
+ i_APCI1564_WriteDigitalOutput,
+ i_APCI1564_ReadDigitalOutput,
+ i_APCI1564_ReadInterruptStatus,
+ i_APCI1564_ConfigTimerCounterWatchdog,
+ i_APCI1564_StartStopWriteTimerCounterWatchdog,
+ i_APCI1564_ReadTimerCounterWatchdog,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+#endif
+#ifdef CONFIG_APCI_1500
+ {"apci1500",
+ APCI1500_BOARD_VENDOR_ID,
+ 0x80fc,
+ 128,
+ APCI1500_ADDRESS_RANGE,
+ 4,
+ 0,
+ ADDIDATA_NO_EEPROM,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ 16,
+ 16,
+ 0xffff,
+ 0,
+ NULL,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ v_APCI1500_Interrupt,
+ i_APCI1500_Reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI1500_ConfigDigitalInputEvent,
+ i_APCI1500_Initialisation,
+ i_APCI1500_StartStopInputEvent,
+ i_APCI1500_ReadMoreDigitalInput,
+ i_APCI1500_ConfigDigitalOutputErrorInterrupt,
+ i_APCI1500_WriteDigitalOutput,
+ i_APCI1500_ConfigureInterrupt,
+ NULL,
+ i_APCI1500_ConfigCounterTimerWatchdog,
+ i_APCI1500_StartStopTriggerTimerCounterWatchdog,
+ i_APCI1500_ReadInterruptMask,
+ i_APCI1500_ReadCounterTimerWatchdog,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+#endif
+#ifdef CONFIG_APCI_3001
+ {"apci3001",
+ APCI3120_BOARD_VENDOR_ID,
+ 0x828D,
+ AMCC_OP_REG_SIZE,
+ APCI3120_ADDRESS_RANGE,
+ 8,
+ 0,
+ ADDIDATA_NO_EEPROM,
+ NULL,
+ 16,
+ 8,
+ 16,
+ 0,
+ 0xfff,
+ 0,
+ &range_apci3120_ai,
+ NULL,
+ 4,
+ 4,
+ 0x0f,
+ 0,
+ NULL,
+ 1,
+ 1,
+ 1,
+ 10000,
+ 100000,
+ v_APCI3120_Interrupt,
+ i_APCI3120_Reset,
+ i_APCI3120_InsnConfigAnalogInput,
+ i_APCI3120_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ i_APCI3120_CommandTestAnalogInput,
+ i_APCI3120_CommandAnalogInput,
+ i_APCI3120_StopCyclicAcquisition,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3120_InsnReadDigitalInput,
+ NULL,
+ i_APCI3120_InsnBitsDigitalInput,
+ i_APCI3120_InsnConfigDigitalOutput,
+ i_APCI3120_InsnWriteDigitalOutput,
+ i_APCI3120_InsnBitsDigitalOutput,
+ NULL,
+ i_APCI3120_InsnConfigTimer,
+ i_APCI3120_InsnWriteTimer,
+ i_APCI3120_InsnReadTimer,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+#endif
+#ifdef CONFIG_APCI_3501
+ {"apci3501",
+ APCI3501_BOARD_VENDOR_ID,
+ 0x3001,
+ 64,
+ APCI3501_ADDRESS_RANGE,
+ 0,
+ 0,
+ ADDIDATA_EEPROM,
+ ADDIDATA_S5933,
+ 0,
+ 0,
+ 0,
+ 8,
+ 0,
+ 16383,
+ NULL,
+ &range_apci3501_ao,
+ 2,
+ 2,
+ 0x3,
+ 0,
+ NULL,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ v_APCI3501_Interrupt,
+ i_APCI3501_Reset,
+ NULL, NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3501_ConfigAnalogOutput,
+ i_APCI3501_WriteAnalogOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3501_ReadDigitalInput,
+ i_APCI3501_ConfigDigitalOutput,
+ i_APCI3501_WriteDigitalOutput,
+ i_APCI3501_ReadDigitalOutput,
+ NULL,
+ i_APCI3501_ConfigTimerCounterWatchdog,
+ i_APCI3501_StartStopWriteTimerCounterWatchdog,
+ i_APCI3501_ReadTimerCounterWatchdog,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+#endif
+#ifdef CONFIG_APCI_035
+ {"apci035",
+ APCI035_BOARD_VENDOR_ID,
+ 0x0300,
+ 127,
+ APCI035_ADDRESS_RANGE,
+ 0,
+ 0,
+ 1,
+ ADDIDATA_S5920,
+ 16,
+ 8,
+ 16,
+ 0,
+ 0xff,
+ 0,
+ &range_apci035_ai,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL,
+ 0,
+ 1,
+ 0,
+ 10000,
+ 100000,
+ v_APCI035_Interrupt,
+ i_APCI035_Reset,
+ i_APCI035_ConfigAnalogInput,
+ i_APCI035_ReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI035_ConfigTimerWatchdog,
+ i_APCI035_StartStopWriteTimerWatchdog,
+ i_APCI035_ReadTimerWatchdog,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+#endif
+#ifdef CONFIG_APCI_3200
+ {"apci3200",
+ APCI3200_BOARD_VENDOR_ID,
+ 0x3000,
+ 128,
+ 256,
+ 4,
+ 4,
+ ADDIDATA_EEPROM,
+ ADDIDATA_S5920,
+ 16,
+ 8,
+ 16,
+ 0,
+ 0x3ffff,
+ 0,
+ &range_apci3200_ai,
+ NULL,
+ 4,
+ 4,
+ 0,
+ 0,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 10000,
+ 100000,
+ v_APCI3200_Interrupt,
+ i_APCI3200_Reset,
+ i_APCI3200_ConfigAnalogInput,
+ i_APCI3200_ReadAnalogInput,
+ i_APCI3200_InsnWriteReleaseAnalogInput,
+ i_APCI3200_InsnBits_AnalogInput_Test,
+ i_APCI3200_CommandTestAnalogInput,
+ i_APCI3200_CommandAnalogInput,
+ i_APCI3200_StopCyclicAcquisition,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3200_ReadDigitalInput,
+ i_APCI3200_ConfigDigitalOutput,
+ i_APCI3200_WriteDigitalOutput,
+ i_APCI3200_ReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+#endif
+#ifdef CONFIG_APCI_3300
+ //Begin JK 20.10.2004: APCI-3300 integration
+ {"apci3300",
+ APCI3200_BOARD_VENDOR_ID,
+ 0x3007,
+ 128,
+ 256,
+ 4,
+ 4,
+ ADDIDATA_EEPROM,
+ ADDIDATA_S5920,
+ 0,
+ 8,
+ 8,
+ 0,
+ 0x3ffff,
+ 0,
+ &range_apci3300_ai,
+ NULL,
+ 4,
+ 4,
+ 0,
+ 0,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 10000,
+ 100000,
+ v_APCI3200_Interrupt,
+ i_APCI3200_Reset,
+ i_APCI3200_ConfigAnalogInput,
+ i_APCI3200_ReadAnalogInput,
+ i_APCI3200_InsnWriteReleaseAnalogInput,
+ i_APCI3200_InsnBits_AnalogInput_Test,
+ i_APCI3200_CommandTestAnalogInput,
+ i_APCI3200_CommandAnalogInput,
+ i_APCI3200_StopCyclicAcquisition,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3200_ReadDigitalInput,
+ i_APCI3200_ConfigDigitalOutput,
+ i_APCI3200_WriteDigitalOutput,
+ i_APCI3200_ReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+#endif
+#ifdef CONFIG_APCI_1710
+ {"apci1710", APCI1710_BOARD_VENDOR_ID, APCI1710_BOARD_DEVICE_ID,
+ 128,
+ 8,
+ 256,
+ 0,
+ ADDIDATA_NO_EEPROM,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ v_APCI1710_Interrupt,
+ i_APCI1710_Reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+#endif
+#ifdef CONFIG_APCI_16XX
+ {"apci1648",
+ 0x15B8,
+ 0x1009,
+ 128,
+ 0,
+ 0,
+ 0,
+ ADDIDATA_NO_EEPROM,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 48,
+ &range_apci16xx_ttl,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL,
+ i_APCI16XX_Reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI16XX_InsnConfigInitTTLIO,
+ i_APCI16XX_InsnBitsReadTTLIO,
+ i_APCI16XX_InsnReadTTLIOAllPortValue,
+ i_APCI16XX_InsnBitsWriteTTLIO},
+
+ {"apci1696",
+ 0x15B8,
+ 0x100A,
+ 128,
+ 0,
+ 0,
+ 0,
+ ADDIDATA_NO_EEPROM,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 96,
+ &range_apci16xx_ttl,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL,
+ i_APCI16XX_Reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI16XX_InsnConfigInitTTLIO,
+ i_APCI16XX_InsnBitsReadTTLIO,
+ i_APCI16XX_InsnReadTTLIOAllPortValue,
+ i_APCI16XX_InsnBitsWriteTTLIO},
+#endif
+#ifdef CONFIG_APCI_3XXX
+ {"apci3000-16",
+ 0x15B8,
+ 0x3010,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 16,
+ 8,
+ 16,
+ 0,
+ 4095,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 10000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3000-8",
+ 0x15B8,
+ 0x300F,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 8,
+ 4,
+ 8,
+ 0,
+ 4095,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 10000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3000-4",
+ 0x15B8,
+ 0x300E,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 4,
+ 2,
+ 4,
+ 0,
+ 4095,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 10000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3006-16",
+ 0x15B8,
+ 0x3013,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 16,
+ 8,
+ 16,
+ 0,
+ 65535,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 10000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3006-8",
+ 0x15B8,
+ 0x3014,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 8,
+ 4,
+ 8,
+ 0,
+ 65535,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 10000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3006-4",
+ 0x15B8,
+ 0x3015,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 4,
+ 2,
+ 4,
+ 0,
+ 65535,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 10000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3010-16",
+ 0x15B8,
+ 0x3016,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 16,
+ 8,
+ 16,
+ 0,
+ 4095,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 4,
+ 4,
+ 1,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 5000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnReadDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnBitsDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnWriteDigitalOutput,
+ i_APCI3XXX_InsnBitsDigitalOutput,
+ i_APCI3XXX_InsnReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3010-8",
+ 0x15B8,
+ 0x3017,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 8,
+ 4,
+ 8,
+ 0,
+ 4095,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 4,
+ 4,
+ 1,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 5000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnReadDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnBitsDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnWriteDigitalOutput,
+ i_APCI3XXX_InsnBitsDigitalOutput,
+ i_APCI3XXX_InsnReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3010-4",
+ 0x15B8,
+ 0x3018,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 4,
+ 2,
+ 4,
+ 0,
+ 4095,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 4,
+ 4,
+ 1,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 5000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnReadDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnBitsDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnWriteDigitalOutput,
+ i_APCI3XXX_InsnBitsDigitalOutput,
+ i_APCI3XXX_InsnReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3016-16",
+ 0x15B8,
+ 0x3019,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 16,
+ 8,
+ 16,
+ 0,
+ 65535,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 4,
+ 4,
+ 1,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 5000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnReadDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnBitsDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnWriteDigitalOutput,
+ i_APCI3XXX_InsnBitsDigitalOutput,
+ i_APCI3XXX_InsnReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3016-8",
+ 0x15B8,
+ 0x301A,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 8,
+ 4,
+ 8,
+ 0,
+ 65535,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 4,
+ 4,
+ 1,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 5000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnReadDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnBitsDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnWriteDigitalOutput,
+ i_APCI3XXX_InsnBitsDigitalOutput,
+ i_APCI3XXX_InsnReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3016-4",
+ 0x15B8,
+ 0x301B,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 4,
+ 2,
+ 4,
+ 0,
+ 65535,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 4,
+ 4,
+ 1,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 5000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnReadDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnBitsDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnWriteDigitalOutput,
+ i_APCI3XXX_InsnBitsDigitalOutput,
+ i_APCI3XXX_InsnReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3100-16-4",
+ 0x15B8,
+ 0x301C,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 16,
+ 8,
+ 16,
+ 4,
+ 4095,
+ 4095,
+ &range_apci3XXX_ai,
+ &range_apci3XXX_ao,
+ 0,
+ 0,
+ 0,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 10000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnWriteAnalogOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3100-8-4",
+ 0x15B8,
+ 0x301D,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 8,
+ 4,
+ 8,
+ 4,
+ 4095,
+ 4095,
+ &range_apci3XXX_ai,
+ &range_apci3XXX_ao,
+ 0,
+ 0,
+ 0,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 10000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnWriteAnalogOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3106-16-4",
+ 0x15B8,
+ 0x301E,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 16,
+ 8,
+ 16,
+ 4,
+ 65535,
+ 4095,
+ &range_apci3XXX_ai,
+ &range_apci3XXX_ao,
+ 0,
+ 0,
+ 0,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 10000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnWriteAnalogOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3106-8-4",
+ 0x15B8,
+ 0x301F,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 8,
+ 4,
+ 8,
+ 4,
+ 65535,
+ 4095,
+ &range_apci3XXX_ai,
+ &range_apci3XXX_ao,
+ 0,
+ 0,
+ 0,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 10000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnWriteAnalogOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3110-16-4",
+ 0x15B8,
+ 0x3020,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 16,
+ 8,
+ 16,
+ 4,
+ 4095,
+ 4095,
+ &range_apci3XXX_ai,
+ &range_apci3XXX_ao,
+ 4,
+ 4,
+ 1,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 5000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnWriteAnalogOutput,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnReadDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnBitsDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnWriteDigitalOutput,
+ i_APCI3XXX_InsnBitsDigitalOutput,
+ i_APCI3XXX_InsnReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3110-8-4",
+ 0x15B8,
+ 0x3021,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 8,
+ 4,
+ 8,
+ 4,
+ 4095,
+ 4095,
+ &range_apci3XXX_ai,
+ &range_apci3XXX_ao,
+ 4,
+ 4,
+ 1,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 5000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnWriteAnalogOutput,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnReadDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnBitsDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnWriteDigitalOutput,
+ i_APCI3XXX_InsnBitsDigitalOutput,
+ i_APCI3XXX_InsnReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3116-16-4",
+ 0x15B8,
+ 0x3022,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 16,
+ 8,
+ 16,
+ 4,
+ 65535,
+ 4095,
+ &range_apci3XXX_ai,
+ &range_apci3XXX_ao,
+ 4,
+ 4,
+ 1,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 5000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnWriteAnalogOutput,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnReadDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnBitsDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnWriteDigitalOutput,
+ i_APCI3XXX_InsnBitsDigitalOutput,
+ i_APCI3XXX_InsnReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3116-8-4",
+ 0x15B8,
+ 0x3023,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 8,
+ 4,
+ 8,
+ 4,
+ 65535,
+ 4095,
+ &range_apci3XXX_ai,
+ &range_apci3XXX_ao,
+ 4,
+ 4,
+ 1,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 6,
+ 5000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnWriteAnalogOutput,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnReadDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnBitsDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnWriteDigitalOutput,
+ i_APCI3XXX_InsnBitsDigitalOutput,
+ i_APCI3XXX_InsnReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+
+ {"apci3003",
+ 0x15B8,
+ 0x300B,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 0,
+ 4,
+ 4,
+ 0,
+ 65535,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 4,
+ 4,
+ 1,
+ 0,
+ NULL,
+ 0,
+ 0,
+ 7,
+ 2500,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnReadDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnBitsDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnWriteDigitalOutput,
+ i_APCI3XXX_InsnBitsDigitalOutput,
+ i_APCI3XXX_InsnReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+
+ {"apci3002-16",
+ 0x15B8,
+ 0x3002,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 0,
+ 16,
+ 16,
+ 0,
+ 65535,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 4,
+ 4,
+ 1,
+ 0,
+ NULL,
+ 0,
+ 0,
+ 6,
+ 5000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnReadDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnBitsDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnWriteDigitalOutput,
+ i_APCI3XXX_InsnBitsDigitalOutput,
+ i_APCI3XXX_InsnReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+
+ {"apci3002-8",
+ 0x15B8,
+ 0x3003,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 0,
+ 8,
+ 8,
+ 0,
+ 65535,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 4,
+ 4,
+ 1,
+ 0,
+ NULL,
+ 0,
+ 0,
+ 6,
+ 5000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnReadDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnBitsDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnWriteDigitalOutput,
+ i_APCI3XXX_InsnBitsDigitalOutput,
+ i_APCI3XXX_InsnReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+
+ {"apci3002-4",
+ 0x15B8,
+ 0x3004,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 0,
+ 4,
+ 4,
+ 0,
+ 65535,
+ 0,
+ &range_apci3XXX_ai,
+ NULL,
+ 4,
+ 4,
+ 1,
+ 0,
+ NULL,
+ 0,
+ 0,
+ 6,
+ 5000,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ i_APCI3XXX_InsnConfigAnalogInput,
+ i_APCI3XXX_InsnReadAnalogInput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnReadDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnBitsDigitalInput,
+ NULL,
+ i_APCI3XXX_InsnWriteDigitalOutput,
+ i_APCI3XXX_InsnBitsDigitalOutput,
+ i_APCI3XXX_InsnReadDigitalOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL},
+
+ {"apci3500",
+ 0x15B8,
+ 0x3024,
+ 256,
+ 256,
+ 256,
+ 256,
+ ADDIDATA_NO_EEPROM,
+ ADDIDATA_9054,
+ 0,
+ 0,
+ 0,
+ 4,
+ 0,
+ 4095,
+ NULL,
+ &range_apci3XXX_ao,
+ 0,
+ 0,
+ 0,
+ 24,
+ &range_apci3XXX_ttl,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ v_APCI3XXX_Interrupt,
+ i_APCI3XXX_Reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnWriteAnalogOutput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ i_APCI3XXX_InsnConfigInitTTLIO,
+ i_APCI3XXX_InsnBitsTTLIO,
+ i_APCI3XXX_InsnReadTTLIO,
+ i_APCI3XXX_InsnWriteTTLIO},
+#endif
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
+
+struct comedi_driver driver_addi = {
+ driver_name:"addi_common",
+ module:THIS_MODULE,
+ attach:i_ADDI_Attach,
+ detach:i_ADDI_Detach,
+ num_names:n_boardtypes,
+ board_name:&boardtypes[0].pc_DriverName,
+ offset:sizeof(boardtype),
+};
+
+COMEDI_PCI_INITCLEANUP(driver_addi, addi_apci_tbl);
+
+/*
++----------------------------------------------------------------------------+
+| Function name :static int i_ADDI_Attach(struct comedi_device *dev, |
+| struct comedi_devconfig *it) |
+| |
++----------------------------------------------------------------------------+
+| Task :Detects the card. |
+| Configure the driver for a particular board. |
+| This function does all the initializations and memory |
+| allocation of data structures for the driver. |
++----------------------------------------------------------------------------+
+| Input Parameters :struct comedi_device *dev |
+| struct comedi_devconfig *it |
+| |
++----------------------------------------------------------------------------+
+| Return Value : 0 |
+| |
++----------------------------------------------------------------------------+
+*/
+
+static int i_ADDI_Attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int ret, pages, i, n_subdevices;
+ DWORD dw_Dummy;
+ resource_size_t io_addr[5];
+ unsigned int irq;
+ resource_size_t iobase_a, iobase_main, iobase_addon, iobase_reserved;
+ struct pcilst_struct *card = NULL;
+ unsigned char pci_bus, pci_slot, pci_func;
+ int i_Dma = 0;
+ static char c_Identifier[150];
+
+ sprintf(c_Identifier, "Addi-Data GmbH Comedi %s",
+ this_board->pc_DriverName);
+
+ if ((ret = alloc_private(dev, sizeof(addi_private))) < 0) {
+ return -ENOMEM;
+ }
+
+ if (!pci_list_builded) {
+ v_pci_card_list_init(this_board->i_VendorId, 1); //1 for displaying the list..
+ pci_list_builded = 1;
+ }
+ //rt_printk("comedi%d: addi_common: board=%s",dev->minor,this_board->pc_DriverName);
+
+ if ((this_board->i_Dma) && (it->options[2] == 0)) {
+ i_Dma = 1;
+ }
+
+ if ((card = ptr_select_and_alloc_pci_card(this_board->i_VendorId,
+ this_board->i_DeviceId,
+ it->options[0],
+ it->options[1], i_Dma)) == NULL) {
+ return -EIO;
+ }
+ devpriv->allocated = 1;
+
+ if ((i_pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
+ &irq)) < 0) {
+ i_pci_card_free(card);
+ printk(" - Can't get AMCC data!\n");
+ return -EIO;
+ }
+
+ iobase_a = io_addr[0];
+ iobase_main = io_addr[1];
+ iobase_addon = io_addr[2];
+ iobase_reserved = io_addr[3];
+ printk("\nBus %d: Slot %d: Funct%d\nBase0: 0x%8llx\nBase1: 0x%8llx\nBase2: 0x%8llx\nBase3: 0x%8llx\n", pci_bus, pci_slot, pci_func, (unsigned long long)io_addr[0], (unsigned long long)io_addr[1], (unsigned long long)io_addr[2], (unsigned long long)io_addr[3]);
+
+ if ((this_board->pc_EepromChip == NULL)
+ || (strcmp(this_board->pc_EepromChip, ADDIDATA_9054) != 0)) {
+ /************************************/
+ /* Test if more that 1 address used */
+ /************************************/
+
+ if (this_board->i_IorangeBase1 != 0) {
+ dev->iobase = (unsigned long)iobase_main; // DAQ base address...
+ } else {
+ dev->iobase = (unsigned long)iobase_a; // DAQ base address...
+ }
+
+ dev->board_name = this_board->pc_DriverName;
+ devpriv->amcc = card;
+ devpriv->iobase = (INT) dev->iobase;
+ devpriv->i_IobaseAmcc = (INT) iobase_a; //AMCC base address...
+ devpriv->i_IobaseAddon = (INT) iobase_addon; //ADD ON base address....
+ devpriv->i_IobaseReserved = (INT) iobase_reserved;
+ devpriv->ps_BoardInfo = this_board;
+ } else {
+ dev->board_name = this_board->pc_DriverName;
+ dev->iobase = (unsigned long)io_addr[2];
+ devpriv->amcc = card;
+ devpriv->iobase = (INT) io_addr[2];
+ devpriv->ps_BoardInfo = this_board;
+ devpriv->i_IobaseReserved = (INT) io_addr[3];
+ printk("\nioremap begin");
+ devpriv->dw_AiBase =
+ (ULONG_PTR) ioremap(io_addr[3],
+ this_board->i_IorangeBase3);
+ printk("\nioremap end");
+ }
+
+ //##
+
+ if (irq > 0) {
+ if (comedi_request_irq(irq, v_ADDI_Interrupt, IRQF_SHARED,
+ c_Identifier, dev) < 0) {
+ printk(", unable to allocate IRQ %u, DISABLING IT",
+ irq);
+ irq = 0; /* Can't use IRQ */
+ } else {
+ rt_printk("\nirq=%u", irq);
+ }
+ } else {
+ rt_printk(", IRQ disabled");
+ }
+
+ printk("\nOption %d %d %d\n", it->options[0], it->options[1],
+ it->options[2]);
+ dev->irq = irq;
+
+ // Read eepeom and fill boardtype Structure
+
+ if (this_board->i_PCIEeprom) {
+ printk("\nPCI Eeprom used");
+ if (!(strcmp(this_board->pc_EepromChip, "S5920"))) {
+ // Set 3 wait stait
+ if (!(strcmp(this_board->pc_DriverName, "apci035"))) {
+ outl(0x80808082, devpriv->i_IobaseAmcc + 0x60);
+ } else {
+ outl(0x83838383, devpriv->i_IobaseAmcc + 0x60);
+ }
+ // Enable the interrupt for the controler
+ dw_Dummy = inl(devpriv->i_IobaseAmcc + 0x38);
+ outl(dw_Dummy | 0x2000, devpriv->i_IobaseAmcc + 0x38);
+ printk("\nEnable the interrupt for the controler");
+ }
+ printk("\nRead Eeprom");
+ i_EepromReadMainHeader(io_addr[0], this_board->pc_EepromChip,
+ dev);
+ } else {
+ printk("\nPCI Eeprom unused");
+ }
+
+ if (it->options[2] > 0) {
+ devpriv->us_UseDma = ADDI_DISABLE;
+ } else {
+ devpriv->us_UseDma = ADDI_ENABLE;
+ }
+
+ if (this_board->i_Dma) {
+ printk("\nDMA used");
+ if (devpriv->us_UseDma == ADDI_ENABLE) {
+ // alloc DMA buffers
+ devpriv->b_DmaDoubleBuffer = 0;
+ for (i = 0; i < 2; i++) {
+ for (pages = 4; pages >= 0; pages--) {
+ if ((devpriv->ul_DmaBufferVirtual[i] =
+ (void *)
+ __get_free_pages
+ (GFP_KERNEL, pages))) {
+ break;
+ }
+ }
+ if (devpriv->ul_DmaBufferVirtual[i]) {
+ devpriv->ui_DmaBufferPages[i] = pages;
+ devpriv->ui_DmaBufferSize[i] =
+ PAGE_SIZE * pages;
+ devpriv->ui_DmaBufferSamples[i] =
+ devpriv->
+ ui_DmaBufferSize[i] >> 1;
+ devpriv->ul_DmaBufferHw[i] =
+ virt_to_bus((void *)devpriv->
+ ul_DmaBufferVirtual[i]);
+ }
+ }
+ if (!devpriv->ul_DmaBufferVirtual[0]) {
+ rt_printk
+ (", Can't allocate DMA buffer, DMA disabled!");
+ devpriv->us_UseDma = ADDI_DISABLE;
+ }
+
+ if (devpriv->ul_DmaBufferVirtual[1]) {
+ devpriv->b_DmaDoubleBuffer = 1;
+ }
+ }
+
+ if ((devpriv->us_UseDma == ADDI_ENABLE)) {
+ rt_printk("\nDMA ENABLED\n");
+ } else {
+ printk("\nDMA DISABLED\n");
+ }
+ }
+
+ if (!strcmp(this_board->pc_DriverName, "apci1710")) {
+#ifdef CONFIG_APCI_1710
+ i_ADDI_AttachPCI1710(dev);
+
+ // save base address
+ devpriv->s_BoardInfos.ui_Address = io_addr[2];
+#endif
+ } else {
+ //Update-0.7.57->0.7.68dev->n_subdevices = 7;
+ n_subdevices = 7;
+ if ((ret = alloc_subdevices(dev, n_subdevices)) < 0)
+ return ret;
+
+ // Allocate and Initialise AI Subdevice Structures
+ s = dev->subdevices + 0;
+ if ((this_board->i_NbrAiChannel)
+ || (this_board->i_NbrAiChannelDiff)) {
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags =
+ SDF_READABLE | SDF_RT | SDF_COMMON | SDF_GROUND
+ | SDF_DIFF;
+ if (this_board->i_NbrAiChannel) {
+ s->n_chan = this_board->i_NbrAiChannel;
+ devpriv->b_SingelDiff = 0;
+ } else {
+ s->n_chan = this_board->i_NbrAiChannelDiff;
+ devpriv->b_SingelDiff = 1;
+ }
+ s->maxdata = this_board->i_AiMaxdata;
+ s->len_chanlist = this_board->i_AiChannelList;
+ s->range_table = this_board->pr_AiRangelist;
+
+ /* Set the initialisation flag */
+ devpriv->b_AiInitialisation = 1;
+
+ s->insn_config =
+ this_board->i_hwdrv_InsnConfigAnalogInput;
+ s->insn_read = this_board->i_hwdrv_InsnReadAnalogInput;
+ s->insn_write =
+ this_board->i_hwdrv_InsnWriteAnalogInput;
+ s->insn_bits = this_board->i_hwdrv_InsnBitsAnalogInput;
+ s->do_cmdtest =
+ this_board->i_hwdrv_CommandTestAnalogInput;
+ s->do_cmd = this_board->i_hwdrv_CommandAnalogInput;
+ s->cancel = this_board->i_hwdrv_CancelAnalogInput;
+
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ // Allocate and Initialise AO Subdevice Structures
+ s = dev->subdevices + 1;
+ if (this_board->i_NbrAoChannel) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags =
+ SDF_WRITEABLE | SDF_GROUND | SDF_COMMON |
+ SDF_RT;
+ s->n_chan = this_board->i_NbrAoChannel;
+ s->maxdata = this_board->i_AoMaxdata;
+ s->len_chanlist = this_board->i_NbrAoChannel;
+ s->range_table = this_board->pr_AoRangelist;
+ s->insn_config =
+ this_board->i_hwdrv_InsnConfigAnalogOutput;
+ s->insn_write =
+ this_board->i_hwdrv_InsnWriteAnalogOutput;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+ // Allocate and Initialise DI Subdevice Structures
+ s = dev->subdevices + 2;
+ if (this_board->i_NbrDiChannel) {
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags =
+ SDF_READABLE | SDF_RT | SDF_GROUND | SDF_COMMON;
+ s->n_chan = this_board->i_NbrDiChannel;
+ s->maxdata = 1;
+ s->len_chanlist = this_board->i_NbrDiChannel;
+ s->range_table = &range_digital;
+ s->io_bits = 0; /* all bits input */
+ s->insn_config =
+ this_board->i_hwdrv_InsnConfigDigitalInput;
+ s->insn_read = this_board->i_hwdrv_InsnReadDigitalInput;
+ s->insn_write =
+ this_board->i_hwdrv_InsnWriteDigitalInput;
+ s->insn_bits = this_board->i_hwdrv_InsnBitsDigitalInput;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+ // Allocate and Initialise DO Subdevice Structures
+ s = dev->subdevices + 3;
+ if (this_board->i_NbrDoChannel) {
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags =
+ SDF_READABLE | SDF_WRITEABLE | SDF_RT |
+ SDF_GROUND | SDF_COMMON;
+ s->n_chan = this_board->i_NbrDoChannel;
+ s->maxdata = this_board->i_DoMaxdata;
+ s->len_chanlist = this_board->i_NbrDoChannel;
+ s->range_table = &range_digital;
+ s->io_bits = 0xf; /* all bits output */
+
+ s->insn_config = this_board->i_hwdrv_InsnConfigDigitalOutput; //for digital output memory..
+ s->insn_write =
+ this_board->i_hwdrv_InsnWriteDigitalOutput;
+ s->insn_bits =
+ this_board->i_hwdrv_InsnBitsDigitalOutput;
+ s->insn_read =
+ this_board->i_hwdrv_InsnReadDigitalOutput;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ // Allocate and Initialise Timer Subdevice Structures
+ s = dev->subdevices + 4;
+ if (this_board->i_Timer) {
+ s->type = COMEDI_SUBD_TIMER;
+ s->subdev_flags =
+ SDF_WRITEABLE | SDF_RT | SDF_GROUND |
+ SDF_COMMON;
+ s->n_chan = 1;
+ s->maxdata = 0;
+ s->len_chanlist = 1;
+ s->range_table = &range_digital;
+
+ s->insn_write = this_board->i_hwdrv_InsnWriteTimer;
+ s->insn_read = this_board->i_hwdrv_InsnReadTimer;
+ s->insn_config = this_board->i_hwdrv_InsnConfigTimer;
+ s->insn_bits = this_board->i_hwdrv_InsnBitsTimer;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ // Allocate and Initialise TTL
+ s = dev->subdevices + 5;
+ if (this_board->i_NbrTTLChannel) {
+ s->type = COMEDI_SUBD_TTLIO;
+ s->subdev_flags =
+ SDF_WRITEABLE | SDF_READABLE | SDF_RT |
+ SDF_GROUND | SDF_COMMON;
+ s->n_chan = this_board->i_NbrTTLChannel;
+ s->maxdata = 1;
+ s->io_bits = 0; /* all bits input */
+ s->len_chanlist = this_board->i_NbrTTLChannel;
+ s->range_table = &range_digital;
+ s->insn_config = this_board->i_hwdr_ConfigInitTTLIO;
+ s->insn_bits = this_board->i_hwdr_ReadTTLIOBits;
+ s->insn_read = this_board->i_hwdr_ReadTTLIOAllPortValue;
+ s->insn_write = this_board->i_hwdr_WriteTTLIOChlOnOff;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* EEPROM */
+ s = dev->subdevices + 6;
+ if (this_board->i_PCIEeprom) {
+ s->type = COMEDI_SUBD_MEMORY;
+ s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
+ s->n_chan = 256;
+ s->maxdata = 0xffff;
+ s->insn_read = i_ADDIDATA_InsnReadEeprom;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+ }
+
+ printk("\ni_ADDI_Attach end\n");
+ i_ADDI_Reset(dev);
+ devpriv->b_ValidDriver = 1;
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name : static int i_ADDI_Detach(struct comedi_device *dev) |
+| |
+| |
++----------------------------------------------------------------------------+
+| Task : Deallocates resources of the addi_common driver |
+| Free the DMA buffers, unregister irq. |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| |
+| |
++----------------------------------------------------------------------------+
+| Return Value : 0 |
+| |
++----------------------------------------------------------------------------+
+*/
+
+static int i_ADDI_Detach(struct comedi_device * dev)
+{
+
+ if (dev->private) {
+ if (devpriv->b_ValidDriver) {
+ i_ADDI_Reset(dev);
+ }
+
+ if (dev->irq) {
+ comedi_free_irq(dev->irq, dev);
+ }
+
+ if ((devpriv->ps_BoardInfo->pc_EepromChip == NULL)
+ || (strcmp(devpriv->ps_BoardInfo->pc_EepromChip,
+ ADDIDATA_9054) != 0)) {
+ if (devpriv->allocated) {
+ i_pci_card_free(devpriv->amcc);
+ }
+
+ if (devpriv->ul_DmaBufferVirtual[0]) {
+ free_pages((unsigned long)devpriv->
+ ul_DmaBufferVirtual[0],
+ devpriv->ui_DmaBufferPages[0]);
+ }
+
+ if (devpriv->ul_DmaBufferVirtual[1]) {
+ free_pages((unsigned long)devpriv->
+ ul_DmaBufferVirtual[1],
+ devpriv->ui_DmaBufferPages[1]);
+ }
+ } else {
+ iounmap((void *)devpriv->dw_AiBase);
+
+ if (devpriv->allocated) {
+ i_pci_card_free(devpriv->amcc);
+ }
+ }
+
+ if (pci_list_builded) {
+ //v_pci_card_list_cleanup(PCI_VENDOR_ID_AMCC);
+ v_pci_card_list_cleanup(this_board->i_VendorId);
+ pci_list_builded = 0;
+ }
+ }
+
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name : static int i_ADDI_Reset(struct comedi_device *dev) |
+| |
++----------------------------------------------------------------------------+
+| Task : Disables all interrupts, Resets digital output to low, |
+| Set all analog output to low |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| |
+| |
++----------------------------------------------------------------------------+
+| Return Value : 0 |
+| |
++----------------------------------------------------------------------------+
+*/
+
+static int i_ADDI_Reset(struct comedi_device * dev)
+{
+
+ this_board->i_hwdrv_Reset(dev);
+ return 0;
+}
+
+// Interrupt function
+/*
++----------------------------------------------------------------------------+
+| Function name : |
+|static void v_ADDI_Interrupt(int irq, void *d PT_REGS_ARG) |
+| |
++----------------------------------------------------------------------------+
+| Task : Registerd interrupt routine |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : int irq |
+| |
+| |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+static irqreturn_t v_ADDI_Interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ this_board->v_hwdrv_Interrupt(irq, d);
+ return IRQ_RETVAL(1);
+}
+
+// EEPROM Read Function
+/*
++----------------------------------------------------------------------------+
+| Function name : |
+|INT i_ADDIDATA_InsnReadEeprom(struct comedi_device *dev,struct comedi_subdevice *s,
+ struct comedi_insn *insn,unsigned int *data)
+| |
++----------------------------------------------------------------------------+
+| Task : Read 256 words from EEPROM |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters :(struct comedi_device *dev,struct comedi_subdevice *s,
+ struct comedi_insn *insn,unsigned int *data) |
+| |
+| |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+static int i_ADDIDATA_InsnReadEeprom(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ WORD w_Data;
+ WORD w_Address;
+ w_Address = CR_CHAN(insn->chanspec); // address to be read as 0,1,2,3...255
+
+ w_Data = w_EepromReadWord(devpriv->i_IobaseAmcc,
+ this_board->pc_EepromChip, 0x100 + (2 * w_Address));
+ data[0] = w_Data;
+ //multiplied by 2 bcozinput will be like 0,1,2...255
+ return insn->n;
+
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/addi_common.h b/drivers/staging/comedi/drivers/addi-data/addi_common.h
new file mode 100644
index 000000000000..19df5c1444a4
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/addi_common.h
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/kmod.h>
+#include <linux/uaccess.h>
+#include "../../comedidev.h"
+#include "addi_amcc_s5933.h"
+
+#define ERROR -1
+#define SUCCESS 1
+
+/* variable type definition */
+typedef unsigned char BYTE, *PBYTE;
+typedef short SHORT, *PSHORT;
+typedef unsigned short USHORT, *PUSHORT;
+typedef unsigned short WORD, *PWORD;
+typedef int INT, *PINT;;
+typedef unsigned int UINT, *PUINT;
+typedef int LONG, *PLONG; /* 32-bit */
+typedef unsigned int ULONG, *PULONG; /* 32-bit */
+typedef unsigned int DWORD, *PDWORD; /* 32-bit */
+typedef unsigned long ULONG_PTR;
+
+typedef const struct comedi_lrange *PCRANGE;
+
+#define LOBYTE(W) (BYTE)((W) & 0xFF)
+#define HIBYTE(W) (BYTE)(((W) >> 8) & 0xFF)
+#define MAKEWORD(H, L) (USHORT)((L) | ((H) << 8))
+#define LOWORD(W) (USHORT)((W) & 0xFFFF)
+#define HIWORD(W) (USHORT)(((W) >> 16) & 0xFFFF)
+#define MAKEDWORD(H, L) (UINT)((L) | ((H) << 16))
+
+#define ADDI_ENABLE 1
+#define ADDI_DISABLE 0
+#define APCI1710_SAVE_INTERRUPT 1
+
+#define ADDIDATA_EEPROM 1
+#define ADDIDATA_NO_EEPROM 0
+#define ADDIDATA_93C76 "93C76"
+#define ADDIDATA_S5920 "S5920"
+#define ADDIDATA_S5933 "S5933"
+#define ADDIDATA_9054 "9054"
+
+/* ADDIDATA Enable Disable */
+#define ADDIDATA_ENABLE 1
+#define ADDIDATA_DISABLE 0
+
+/* Structures */
+
+/* structure for the boardtype */
+typedef struct {
+ const char *pc_DriverName; // driver name
+ INT i_VendorId; //PCI vendor a device ID of card
+ INT i_DeviceId;
+ INT i_IorangeBase0;
+ INT i_IorangeBase1;
+ INT i_IorangeBase2; // base 2 range
+ INT i_IorangeBase3; // base 3 range
+ INT i_PCIEeprom; // eeprom present or not
+ char *pc_EepromChip; // type of chip
+ INT i_NbrAiChannel; // num of A/D chans
+ INT i_NbrAiChannelDiff; // num of A/D chans in diff mode
+ INT i_AiChannelList; // len of chanlist
+ INT i_NbrAoChannel; // num of D/A chans
+ INT i_AiMaxdata; // resolution of A/D
+ INT i_AoMaxdata; // resolution of D/A
+ PCRANGE pr_AiRangelist; // rangelist for A/D
+ PCRANGE pr_AoRangelist; // rangelist for D/A
+
+ INT i_NbrDiChannel; // Number of DI channels
+ INT i_NbrDoChannel; // Number of DO channels
+ INT i_DoMaxdata; // data to set all chanels high
+
+ INT i_NbrTTLChannel; // Number of TTL channels
+ PCRANGE pr_TTLRangelist; // rangelist for TTL
+
+ INT i_Dma; // dma present or not
+ INT i_Timer; // timer subdevice present or not
+ BYTE b_AvailableConvertUnit;
+ UINT ui_MinAcquisitiontimeNs; // Minimum Acquisition in Nano secs
+ UINT ui_MinDelaytimeNs; // Minimum Delay in Nano secs
+
+ /* interrupt and reset */
+ void (*v_hwdrv_Interrupt)(int irq, void *d);
+ int (*i_hwdrv_Reset)(struct comedi_device *dev);
+
+ /* Subdevice functions */
+
+ /* ANALOG INPUT */
+ int (*i_hwdrv_InsnConfigAnalogInput)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+ int (*i_hwdrv_InsnReadAnalogInput)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+ int (*i_hwdrv_InsnWriteAnalogInput)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+ int (*i_hwdrv_InsnBitsAnalogInput)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+ int (*i_hwdrv_CommandTestAnalogInput)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd);
+ int (*i_hwdrv_CommandAnalogInput)(struct comedi_device *dev,
+ struct comedi_subdevice *s);
+ int (*i_hwdrv_CancelAnalogInput)(struct comedi_device *dev,
+ struct comedi_subdevice *s);
+
+ /* Analog Output */
+ int (*i_hwdrv_InsnConfigAnalogOutput)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+ int (*i_hwdrv_InsnWriteAnalogOutput)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+ int (*i_hwdrv_InsnBitsAnalogOutput)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+
+ /* Digital Input */
+ int (*i_hwdrv_InsnConfigDigitalInput) (struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+ int (*i_hwdrv_InsnReadDigitalInput) (struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+ int (*i_hwdrv_InsnWriteDigitalInput) (struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+ int (*i_hwdrv_InsnBitsDigitalInput) (struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+
+ /* Digital Output */
+ int (*i_hwdrv_InsnConfigDigitalOutput)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+ int (*i_hwdrv_InsnWriteDigitalOutput)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+ int (*i_hwdrv_InsnBitsDigitalOutput)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+ int (*i_hwdrv_InsnReadDigitalOutput)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+
+ /* TIMER */
+ int (*i_hwdrv_InsnConfigTimer)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+ int (*i_hwdrv_InsnWriteTimer)(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn,
+ unsigned int *data);
+ int (*i_hwdrv_InsnReadTimer)(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+ int (*i_hwdrv_InsnBitsTimer)(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+ /* TTL IO */
+ int (*i_hwdr_ConfigInitTTLIO)(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn,
+ unsigned int *data);
+ int (*i_hwdr_ReadTTLIOBits)(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+ int (*i_hwdr_ReadTTLIOAllPortValue)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+ int (*i_hwdr_WriteTTLIOChlOnOff)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+} boardtype;
+
+//MODULE INFO STRUCTURE
+
+typedef union {
+ /* Incremental counter infos */
+ struct {
+ union {
+ struct {
+ BYTE b_ModeRegister1;
+ BYTE b_ModeRegister2;
+ BYTE b_ModeRegister3;
+ BYTE b_ModeRegister4;
+ } s_ByteModeRegister;
+ DWORD dw_ModeRegister1_2_3_4;
+ } s_ModeRegister;
+
+ struct {
+ unsigned int b_IndexInit:1;
+ unsigned int b_CounterInit:1;
+ unsigned int b_ReferenceInit:1;
+ unsigned int b_IndexInterruptOccur:1;
+ unsigned int b_CompareLogicInit:1;
+ unsigned int b_FrequencyMeasurementInit:1;
+ unsigned int b_FrequencyMeasurementEnable:1;
+ } s_InitFlag;
+
+ } s_SiemensCounterInfo;
+
+ /* SSI infos */
+ struct {
+ BYTE b_SSIProfile;
+ BYTE b_PositionTurnLength;
+ BYTE b_TurnCptLength;
+ BYTE b_SSIInit;
+ } s_SSICounterInfo;
+
+ /* TTL I/O infos */
+ struct {
+ BYTE b_TTLInit;
+ BYTE b_PortConfiguration[4];
+ } s_TTLIOInfo;
+
+ /* Digital I/O infos */
+ struct {
+ BYTE b_DigitalInit;
+ BYTE b_ChannelAMode;
+ BYTE b_ChannelBMode;
+ BYTE b_OutputMemoryEnabled;
+ DWORD dw_OutputMemory;
+ } s_DigitalIOInfo;
+
+ /*********************/
+ /* 82X54 timer infos */
+ /*********************/
+
+ struct {
+ struct {
+ BYTE b_82X54Init;
+ BYTE b_InputClockSelection;
+ BYTE b_InputClockLevel;
+ BYTE b_OutputLevel;
+ BYTE b_HardwareGateLevel;
+ DWORD dw_ConfigurationWord;
+ } s_82X54TimerInfo[3];
+ BYTE b_InterruptMask;
+ } s_82X54ModuleInfo;
+
+ /*********************/
+ /* Chronometer infos */
+ /*********************/
+
+ struct {
+ BYTE b_ChronoInit;
+ BYTE b_InterruptMask;
+ BYTE b_PCIInputClock;
+ BYTE b_TimingUnit;
+ BYTE b_CycleMode;
+ double d_TimingInterval;
+ DWORD dw_ConfigReg;
+ } s_ChronoModuleInfo;
+
+ /***********************/
+ /* Pulse encoder infos */
+ /***********************/
+
+ struct {
+ struct {
+ BYTE b_PulseEncoderInit;
+ } s_PulseEncoderInfo[4];
+ DWORD dw_SetRegister;
+ DWORD dw_ControlRegister;
+ DWORD dw_StatusRegister;
+ } s_PulseEncoderModuleInfo;
+
+ /* Tor conter infos */
+ struct {
+ struct {
+ BYTE b_TorCounterInit;
+ BYTE b_TimingUnit;
+ BYTE b_InterruptEnable;
+ double d_TimingInterval;
+ ULONG ul_RealTimingInterval;
+ } s_TorCounterInfo[2];
+ BYTE b_PCIInputClock;
+ } s_TorCounterModuleInfo;
+
+ /* PWM infos */
+ struct {
+ struct {
+ BYTE b_PWMInit;
+ BYTE b_TimingUnit;
+ BYTE b_InterruptEnable;
+ double d_LowTiming;
+ double d_HighTiming;
+ ULONG ul_RealLowTiming;
+ ULONG ul_RealHighTiming;
+ } s_PWMInfo[2];
+ BYTE b_ClockSelection;
+ } s_PWMModuleInfo;
+
+ /* ETM infos */
+ struct {
+ struct {
+ BYTE b_ETMEnable;
+ BYTE b_ETMInterrupt;
+ } s_ETMInfo[2];
+ BYTE b_ETMInit;
+ BYTE b_TimingUnit;
+ BYTE b_ClockSelection;
+ double d_TimingInterval;
+ ULONG ul_Timing;
+ } s_ETMModuleInfo;
+
+ /* CDA infos */
+ struct {
+ BYTE b_CDAEnable;
+ BYTE b_CDAInterrupt;
+ BYTE b_CDAInit;
+ BYTE b_FctSelection;
+ BYTE b_CDAReadFIFOOverflow;
+ } s_CDAModuleInfo;
+
+} str_ModuleInfo;
+
+/* Private structure for the addi_apci3120 driver */
+typedef struct {
+
+ INT iobase;
+ INT i_IobaseAmcc; // base+size for AMCC chip
+ INT i_IobaseAddon; //addon base address
+ INT i_IobaseReserved;
+ ULONG_PTR dw_AiBase;
+ struct pcilst_struct *amcc; // ptr too AMCC data
+ BYTE allocated; // we have blocked card
+ BYTE b_ValidDriver; // driver is ok
+ BYTE b_AiContinuous; // we do unlimited AI
+ BYTE b_AiInitialisation;
+ UINT ui_AiActualScan; //how many scans we finished
+ UINT ui_AiBufferPtr; // data buffer ptr in samples
+ UINT ui_AiNbrofChannels; // how many channels is measured
+ UINT ui_AiScanLength; // Length of actual scanlist
+ UINT ui_AiActualScanPosition; // position in actual scan
+ PUINT pui_AiChannelList; // actual chanlist
+ UINT ui_AiChannelList[32]; // actual chanlist
+ BYTE b_AiChannelConfiguration[32]; // actual chanlist
+ UINT ui_AiReadData[32];
+ DWORD dw_AiInitialised;
+ UINT ui_AiTimer0; //Timer Constant for Timer0
+ UINT ui_AiTimer1; //Timer constant for Timer1
+ UINT ui_AiFlags;
+ UINT ui_AiDataLength;
+ short *AiData; // Pointer to sample data
+ UINT ui_AiNbrofScans; // number of scans to do
+ USHORT us_UseDma; // To use Dma or not
+ BYTE b_DmaDoubleBuffer; // we can use double buffering
+ UINT ui_DmaActualBuffer; // which buffer is used now
+ //*UPDATE-0.7.57->0.7.68
+ //ULONG ul_DmaBufferVirtual[2];// pointers to begin of DMA buffer
+ short *ul_DmaBufferVirtual[2]; // pointers to begin of DMA buffer
+ ULONG ul_DmaBufferHw[2]; // hw address of DMA buff
+ UINT ui_DmaBufferSize[2]; // size of dma buffer in bytes
+ UINT ui_DmaBufferUsesize[2]; // which size we may now used for transfer
+ UINT ui_DmaBufferSamples[2]; // size in samples
+ UINT ui_DmaBufferPages[2]; // number of pages in buffer
+ BYTE b_DigitalOutputRegister; // Digital Output Register
+ BYTE b_OutputMemoryStatus;
+ BYTE b_AnalogInputChannelNbr; // Analog input channel Nbr
+ BYTE b_AnalogOutputChannelNbr; // Analog input Output Nbr
+ BYTE b_TimerSelectMode; // Contain data written at iobase + 0C
+ BYTE b_ModeSelectRegister; // Contain data written at iobase + 0E
+ USHORT us_OutputRegister; // Contain data written at iobase + 0
+ BYTE b_InterruptState;
+ BYTE b_TimerInit; // Specify if InitTimerWatchdog was load
+ BYTE b_TimerStarted; // Specify if timer 2 is running or not
+ BYTE b_Timer2Mode; // Specify the timer 2 mode
+ BYTE b_Timer2Interrupt; //Timer2 interrupt enable or disable
+ BYTE b_AiCyclicAcquisition; // indicate cyclic acquisition
+ BYTE b_InterruptMode; // eoc eos or dma
+ BYTE b_EocEosInterrupt; // Enable disable eoc eos interrupt
+ UINT ui_EocEosConversionTime;
+ BYTE b_EocEosConversionTimeBase;
+ BYTE b_SingelDiff;
+ BYTE b_ExttrigEnable; /* To enable or disable external trigger */
+
+ /* Pointer to the current process */
+ struct task_struct *tsk_Current;
+ boardtype *ps_BoardInfo;
+
+ /* Hardware board infos for 1710 */
+ struct {
+ UINT ui_Address; /* Board address */
+ UINT ui_FlashAddress;
+ BYTE b_InterruptNbr; /* Board interrupt number */
+ BYTE b_SlotNumber; /* PCI slot number */
+ BYTE b_BoardVersion;
+ DWORD dw_MolduleConfiguration[4]; /* Module config */
+ } s_BoardInfos;
+
+ /* Interrupt infos */
+ struct {
+ ULONG ul_InterruptOccur; /* 0 : No interrupt occur */
+ /* > 0 : Interrupt occur */
+ UINT ui_Read; /* Read FIFO */
+ UINT ui_Write; /* Write FIFO */
+ struct {
+ BYTE b_OldModuleMask;
+ ULONG ul_OldInterruptMask; /* Interrupt mask */
+ ULONG ul_OldCounterLatchValue; /* Interrupt counter value */
+ } s_FIFOInterruptParameters[APCI1710_SAVE_INTERRUPT];
+ } s_InterruptParameters;
+
+ str_ModuleInfo s_ModuleInfo[4];
+ ULONG ul_TTLPortConfiguration[10];
+
+} addi_private;
+
+static unsigned short pci_list_builded; /* set to 1 when list of card is known */
+
+/* Function declarations */
+static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it);
+static int i_ADDI_Detach(struct comedi_device *dev);
+static int i_ADDI_Reset(struct comedi_device *dev);
+
+static irqreturn_t v_ADDI_Interrupt(int irq, void *d PT_REGS_ARG);
+static int i_ADDIDATA_InsnReadEeprom(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
diff --git a/drivers/staging/comedi/drivers/addi-data/addi_eeprom.c b/drivers/staging/comedi/drivers/addi-data/addi_eeprom.c
new file mode 100644
index 000000000000..a8a1bb2e8fa5
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/addi_eeprom.c
@@ -0,0 +1,1158 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstrasse 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-----------------------------------------------------------------------+
+ | Project : ADDI DATA | Compiler : GCC |
+ | Modulname : addi_eeprom.c | Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-----------------------------------------------------------------------+
+ | Description : ADDI EEPROM Module |
+ +-----------------------------------------------------------------------+
+ | UPDATE'S |
+ +-----------------------------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | | | |
+ | | | |
+ +----------+-----------+------------------------------------------------+
+*/
+
+#define NVCMD_BEGIN_READ (0x7 << 5 ) // nvRam begin read command
+#define NVCMD_LOAD_LOW (0x4 << 5 ) // nvRam load low command
+#define NVCMD_LOAD_HIGH (0x5 << 5 ) // nvRam load high command
+#define EE76_CMD_LEN 13 // bits in instructions
+#define EE_READ 0x0180 // 01 1000 0000 read instruction
+
+#define WORD unsigned short
+#define PWORD unsigned short *
+#define PDWORD unsigned int *
+
+#ifndef DWORD
+#define DWORD unsigned int
+#endif
+
+#define EEPROM_DIGITALINPUT 0
+#define EEPROM_DIGITALOUTPUT 1
+#define EEPROM_ANALOGINPUT 2
+#define EEPROM_ANALOGOUTPUT 3
+#define EEPROM_TIMER 4
+#define EEPROM_WATCHDOG 5
+#define EEPROM_TIMER_WATCHDOG_COUNTER 10
+
+struct str_Functionality {
+ BYTE b_Type;
+ WORD w_Address;
+};
+
+typedef struct {
+ WORD w_HeaderSize;
+ BYTE b_Nfunctions;
+ struct str_Functionality s_Functions[7];
+} str_MainHeader;
+
+typedef struct {
+ WORD w_Nchannel;
+ BYTE b_Interruptible;
+ WORD w_NinterruptLogic;
+} str_DigitalInputHeader;
+
+typedef struct {
+ WORD w_Nchannel;
+} str_DigitalOutputHeader;
+
+// used for timer as well as watchdog
+
+typedef struct {
+ WORD w_HeaderSize;
+ BYTE b_Resolution;
+ BYTE b_Mode; // in case of Watchdog it is functionality
+ WORD w_MinTiming;
+ BYTE b_TimeBase;
+} str_TimerDetails;
+typedef struct {
+
+ WORD w_Ntimer;
+ str_TimerDetails s_TimerDetails[4]; // supports 4 timers
+} str_TimerMainHeader;
+
+typedef struct {
+ WORD w_Nchannel;
+ BYTE b_Resolution;
+} str_AnalogOutputHeader;
+
+typedef struct {
+ WORD w_Nchannel;
+ WORD w_MinConvertTiming;
+ WORD w_MinDelayTiming;
+ BYTE b_HasDma;
+ BYTE b_Resolution;
+} str_AnalogInputHeader;
+
+ /*****************************************/
+ /* Read Header Functions */
+ /*****************************************/
+
+INT i_EepromReadMainHeader(WORD w_PCIBoardEepromAddress,
+ char *pc_PCIChipInformation, struct comedi_device *dev);
+
+INT i_EepromReadDigitalInputHeader(WORD w_PCIBoardEepromAddress,
+ char *pc_PCIChipInformation, WORD w_Address,
+ str_DigitalInputHeader * s_Header);
+
+INT i_EepromReadDigitalOutputHeader(WORD w_PCIBoardEepromAddress,
+ char *pc_PCIChipInformation, WORD w_Address,
+ str_DigitalOutputHeader * s_Header);
+
+INT i_EepromReadTimerHeader(WORD w_PCIBoardEepromAddress,
+ char *pc_PCIChipInformation, WORD w_Address,
+ str_TimerMainHeader * s_Header);
+
+INT i_EepromReadAnlogOutputHeader(WORD w_PCIBoardEepromAddress,
+ char *pc_PCIChipInformation, WORD w_Address,
+ str_AnalogOutputHeader * s_Header);
+
+INT i_EepromReadAnlogInputHeader(WORD w_PCIBoardEepromAddress,
+ char *pc_PCIChipInformation, WORD w_Address,
+ str_AnalogInputHeader * s_Header);
+
+ /******************************************/
+ /* Eeprom Specific Functions */
+ /******************************************/
+WORD w_EepromReadWord(WORD w_PCIBoardEepromAddress, char *pc_PCIChipInformation,
+ WORD w_EepromStartAddress);
+void v_EepromWaitBusy(WORD w_PCIBoardEepromAddress);
+void v_EepromClock76(DWORD dw_Address, DWORD dw_RegisterValue);
+void v_EepromWaitBusy(WORD w_PCIBoardEepromAddress);
+void v_EepromSendCommand76(DWORD dw_Address, DWORD dw_EepromCommand,
+ BYTE b_DataLengthInBits);
+void v_EepromCs76Read(DWORD dw_Address, WORD w_offset, PWORD pw_Value);
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : WORD w_EepromReadWord |
+| (WORD w_PCIBoardEepromAddress, |
+| char * pc_PCIChipInformation, |
+| WORD w_EepromStartAddress) |
++----------------------------------------------------------------------------+
+| Task : Read from eepromn a word |
++----------------------------------------------------------------------------+
+| Input Parameters : WORD w_PCIBoardEepromAddress : PCI eeprom address |
+| |
+| char *pc_PCIChipInformation : PCI Chip Type. |
+| |
+| WORD w_EepromStartAddress : Selected eeprom address |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : Read word value from eeprom |
++----------------------------------------------------------------------------+
+*/
+
+WORD w_EepromReadWord(WORD w_PCIBoardEepromAddress, char *pc_PCIChipInformation,
+ WORD w_EepromStartAddress)
+{
+
+ BYTE b_Counter = 0;
+
+ BYTE b_ReadByte = 0;
+
+ BYTE b_ReadLowByte = 0;
+
+ BYTE b_ReadHighByte = 0;
+
+ BYTE b_SelectedAddressLow = 0;
+
+ BYTE b_SelectedAddressHigh = 0;
+
+ WORD w_ReadWord = 0;
+
+ /**************************/
+
+ /* Test the PCI chip type */
+
+ /**************************/
+
+ if ((!strcmp(pc_PCIChipInformation, "S5920")) ||
+ (!strcmp(pc_PCIChipInformation, "S5933")))
+ {
+
+ for (b_Counter = 0; b_Counter < 2; b_Counter++)
+ {
+
+ b_SelectedAddressLow = (w_EepromStartAddress + b_Counter) % 256; //Read the low 8 bit part
+
+ b_SelectedAddressHigh = (w_EepromStartAddress + b_Counter) / 256; //Read the high 8 bit part
+
+ /************************************/
+
+ /* Select the load low address mode */
+
+ /************************************/
+
+ outb(NVCMD_LOAD_LOW, w_PCIBoardEepromAddress + 0x3F);
+
+ /****************/
+
+ /* Wait on busy */
+
+ /****************/
+
+ v_EepromWaitBusy(w_PCIBoardEepromAddress);
+
+ /************************/
+
+ /* Load the low address */
+
+ /************************/
+
+ outb(b_SelectedAddressLow,
+ w_PCIBoardEepromAddress + 0x3E);
+
+ /****************/
+
+ /* Wait on busy */
+
+ /****************/
+
+ v_EepromWaitBusy(w_PCIBoardEepromAddress);
+
+ /*************************************/
+
+ /* Select the load high address mode */
+
+ /*************************************/
+
+ outb(NVCMD_LOAD_HIGH, w_PCIBoardEepromAddress + 0x3F);
+
+ /****************/
+
+ /* Wait on busy */
+
+ /****************/
+
+ v_EepromWaitBusy(w_PCIBoardEepromAddress);
+
+ /*************************/
+
+ /* Load the high address */
+
+ /*************************/
+
+ outb(b_SelectedAddressHigh,
+ w_PCIBoardEepromAddress + 0x3E);
+
+ /****************/
+
+ /* Wait on busy */
+
+ /****************/
+
+ v_EepromWaitBusy(w_PCIBoardEepromAddress);
+
+ /************************/
+
+ /* Select the READ mode */
+
+ /************************/
+
+ outb(NVCMD_BEGIN_READ, w_PCIBoardEepromAddress + 0x3F);
+
+ /****************/
+
+ /* Wait on busy */
+
+ /****************/
+
+ v_EepromWaitBusy(w_PCIBoardEepromAddress);
+
+ /*****************************/
+
+ /* Read data into the EEPROM */
+
+ /*****************************/
+
+ b_ReadByte = inb(w_PCIBoardEepromAddress + 0x3E);
+
+ /****************/
+
+ /* Wait on busy */
+
+ /****************/
+
+ v_EepromWaitBusy(w_PCIBoardEepromAddress);
+
+ /*********************************/
+
+ /* Select the upper address part */
+
+ /*********************************/
+
+ if (b_Counter == 0)
+ {
+
+ b_ReadLowByte = b_ReadByte;
+
+ } // if(b_Counter==0)
+
+ else
+ {
+
+ b_ReadHighByte = b_ReadByte;
+
+ } // if(b_Counter==0)
+
+ } // for (b_Counter=0; b_Counter<2; b_Counter++)
+
+ w_ReadWord = (b_ReadLowByte | (((WORD) b_ReadHighByte) * 256));
+
+ } // end of if ((!strcmp(pc_PCIChipInformation, "S5920")) || (!strcmp(pc_PCIChipInformation, "S5933")))
+
+ if (!strcmp(pc_PCIChipInformation, "93C76"))
+ {
+
+ /*************************************/
+
+ /* Read 16 bit from the EEPROM 93C76 */
+
+ /*************************************/
+
+ v_EepromCs76Read(w_PCIBoardEepromAddress, w_EepromStartAddress,
+ &w_ReadWord);
+
+ }
+
+ return (w_ReadWord);
+
+}
+
+/*
+
++----------------------------------------------------------------------------+
+
+| Function Name : void v_EepromWaitBusy |
+
+| (WORD w_PCIBoardEepromAddress) |
+
++----------------------------------------------------------------------------+
+
+| Task : Wait the busy flag from PCI controller |
+
++----------------------------------------------------------------------------+
+
+| Input Parameters : WORD w_PCIBoardEepromAddress : PCI eeprom base address |
+
++----------------------------------------------------------------------------+
+
+| Output Parameters : - |
+
++----------------------------------------------------------------------------+
+
+| Return Value : - |
+
++----------------------------------------------------------------------------+
+
+*/
+
+void v_EepromWaitBusy(WORD w_PCIBoardEepromAddress)
+{
+
+ BYTE b_EepromBusy = 0;
+
+ do
+ {
+
+ /*************/
+
+ /* IMPORTANT */
+
+ /*************/
+
+ /************************************************************************/
+
+ /* An error has been written in the AMCC 5933 book at the page B-13 */
+
+ /* Ex: if you read a byte and look for the busy statusEEPROM=0x80 and */
+
+ /* the operator register is AMCC_OP_REG_MCSR+3 */
+
+ /* WORD read EEPROM=0x8000 andAMCC_OP_REG_MCSR+2 */
+
+ /* DWORD read EEPROM=0x80000000 and AMCC_OP_REG_MCSR */
+
+ /************************************************************************/
+
+ b_EepromBusy = inb(w_PCIBoardEepromAddress + 0x3F);
+ b_EepromBusy = b_EepromBusy & 0x80;
+
+ }
+ while (b_EepromBusy == 0x80);
+
+}
+
+/*
+
++---------------------------------------------------------------------------------+
+
+| Function Name : void v_EepromClock76(DWORD dw_Address, |
+
+| DWORD dw_RegisterValue) |
+
++---------------------------------------------------------------------------------+
+
+| Task : This function sends the clocking sequence to the EEPROM. |
+
++---------------------------------------------------------------------------------+
+
+| Input Parameters : DWORD dw_Address : PCI eeprom base address |
+
+| DWORD dw_RegisterValue : PCI eeprom register value to write.|
+
++---------------------------------------------------------------------------------+
+
+| Output Parameters : - |
+
++---------------------------------------------------------------------------------+
+
+| Return Value : - |
+
++---------------------------------------------------------------------------------+
+
+*/
+
+void v_EepromClock76(DWORD dw_Address, DWORD dw_RegisterValue)
+{
+
+ /************************/
+
+ /* Set EEPROM clock Low */
+
+ /************************/
+
+ outl(dw_RegisterValue & 0x6, dw_Address);
+
+ /***************/
+
+ /* Wait 0.1 ms */
+
+ /***************/
+
+ udelay(100);
+
+ /*************************/
+
+ /* Set EEPROM clock High */
+
+ /*************************/
+
+ outl(dw_RegisterValue | 0x1, dw_Address);
+
+ /***************/
+
+ /* Wait 0.1 ms */
+
+ /***************/
+
+ udelay(100);
+
+}
+
+/*
+
++---------------------------------------------------------------------------------+
+
+| Function Name : void v_EepromSendCommand76(DWORD dw_Address, |
+
+| DWORD dw_EepromCommand, |
+
+| BYTE b_DataLengthInBits) |
+
++---------------------------------------------------------------------------------+
+
+| Task : This function sends a Command to the EEPROM 93C76. |
+
++---------------------------------------------------------------------------------+
+
+| Input Parameters : DWORD dw_Address : PCI eeprom base address |
+
+| DWORD dw_EepromCommand : PCI eeprom command to write. |
+
+| BYTE b_DataLengthInBits : PCI eeprom command data length. |
+
++---------------------------------------------------------------------------------+
+
+| Output Parameters : - |
+
++---------------------------------------------------------------------------------+
+
+| Return Value : - |
+
++---------------------------------------------------------------------------------+
+
+*/
+
+void v_EepromSendCommand76(DWORD dw_Address, DWORD dw_EepromCommand,
+ BYTE b_DataLengthInBits)
+{
+
+ char c_BitPos = 0;
+
+ DWORD dw_RegisterValue = 0;
+
+ /*****************************/
+
+ /* Enable EEPROM Chip Select */
+
+ /*****************************/
+
+ dw_RegisterValue = 0x2;
+
+ /********************************************************************/
+
+ /* Toggle EEPROM's Chip select to get it out of Shift Register Mode */
+
+ /********************************************************************/
+
+ outl(dw_RegisterValue, dw_Address);
+
+ /***************/
+
+ /* Wait 0.1 ms */
+
+ /***************/
+
+ udelay(100);
+
+ /*******************************************/
+
+ /* Send EEPROM command - one bit at a time */
+
+ /*******************************************/
+
+ for (c_BitPos = (b_DataLengthInBits - 1); c_BitPos >= 0; c_BitPos--)
+ {
+
+ /**********************************/
+
+ /* Check if current bit is 0 or 1 */
+
+ /**********************************/
+
+ if (dw_EepromCommand & (1 << c_BitPos))
+ {
+
+ /***********/
+
+ /* Write 1 */
+
+ /***********/
+
+ dw_RegisterValue = dw_RegisterValue | 0x4;
+
+ }
+
+ else
+ {
+
+ /***********/
+
+ /* Write 0 */
+
+ /***********/
+
+ dw_RegisterValue = dw_RegisterValue & 0x3;
+
+ }
+
+ /*********************/
+
+ /* Write the command */
+
+ /*********************/
+
+ outl(dw_RegisterValue, dw_Address);
+
+ /***************/
+
+ /* Wait 0.1 ms */
+
+ /***************/
+
+ udelay(100);
+
+ /****************************/
+
+ /* Trigger the EEPROM clock */
+
+ /****************************/
+
+ v_EepromClock76(dw_Address, dw_RegisterValue);
+
+ }
+
+}
+
+/*
+
++---------------------------------------------------------------------------------+
+
+| Function Name : void v_EepromCs76Read(DWORD dw_Address, |
+
+| WORD w_offset, |
+
+| PWORD pw_Value) |
+
++---------------------------------------------------------------------------------+
+
+| Task : This function read a value from the EEPROM 93C76. |
+
++---------------------------------------------------------------------------------+
+
+| Input Parameters : DWORD dw_Address : PCI eeprom base address |
+
+| WORD w_offset : Offset of the adress to read |
+
+| PWORD pw_Value : PCI eeprom 16 bit read value. |
+
++---------------------------------------------------------------------------------+
+
+| Output Parameters : - |
+
++---------------------------------------------------------------------------------+
+
+| Return Value : - |
+
++---------------------------------------------------------------------------------+
+
+*/
+
+void v_EepromCs76Read(DWORD dw_Address, WORD w_offset, PWORD pw_Value)
+{
+
+ char c_BitPos = 0;
+
+ DWORD dw_RegisterValue = 0;
+
+ DWORD dw_RegisterValueRead = 0;
+
+ /*************************************************/
+
+ /* Send EEPROM read command and offset to EEPROM */
+
+ /*************************************************/
+
+ v_EepromSendCommand76(dw_Address, (EE_READ << 4) | (w_offset / 2),
+ EE76_CMD_LEN);
+
+ /*******************************/
+
+ /* Get the last register value */
+
+ /*******************************/
+
+ dw_RegisterValue = (((w_offset / 2) & 0x1) << 2) | 0x2;
+
+ /*****************************/
+
+ /* Set the 16-bit value of 0 */
+
+ /*****************************/
+
+ *pw_Value = 0;
+
+ /************************/
+
+ /* Get the 16-bit value */
+
+ /************************/
+
+ for (c_BitPos = 0; c_BitPos < 16; c_BitPos++)
+ {
+
+ /****************************/
+
+ /* Trigger the EEPROM clock */
+
+ /****************************/
+
+ v_EepromClock76(dw_Address, dw_RegisterValue);
+
+ /**********************/
+
+ /* Get the result bit */
+
+ /**********************/
+
+ dw_RegisterValueRead = inl(dw_Address);
+
+ /***************/
+
+ /* Wait 0.1 ms */
+
+ /***************/
+
+ udelay(100);
+
+ /***************************************/
+
+ /* Get bit value and shift into result */
+
+ /***************************************/
+
+ if (dw_RegisterValueRead & 0x8)
+ {
+
+ /**********/
+
+ /* Read 1 */
+
+ /**********/
+
+ *pw_Value = (*pw_Value << 1) | 0x1;
+
+ }
+
+ else
+ {
+
+ /**********/
+
+ /* Read 0 */
+
+ /**********/
+
+ *pw_Value = (*pw_Value << 1);
+
+ }
+
+ }
+
+ /*************************/
+
+ /* Clear all EEPROM bits */
+
+ /*************************/
+
+ dw_RegisterValue = 0x0;
+
+ /********************************************************************/
+
+ /* Toggle EEPROM's Chip select to get it out of Shift Register Mode */
+
+ /********************************************************************/
+
+ outl(dw_RegisterValue, dw_Address);
+
+ /***************/
+
+ /* Wait 0.1 ms */
+
+ /***************/
+
+ udelay(100);
+
+}
+
+ /******************************************/
+ /* EEPROM HEADER READ FUNCTIONS */
+ /******************************************/
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_EepromReadMainHeader(WORD w_PCIBoardEepromAddress, |
+| char * pc_PCIChipInformation,struct comedi_device *dev) |
++----------------------------------------------------------------------------+
+| Task : Read from eeprom Main Header |
++----------------------------------------------------------------------------+
+| Input Parameters : WORD w_PCIBoardEepromAddress : PCI eeprom address |
+| |
+| char *pc_PCIChipInformation : PCI Chip Type. |
+| |
+| struct comedi_device *dev : comedi device structure |
+| pointer |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0 |
++----------------------------------------------------------------------------+
+*/
+
+INT i_EepromReadMainHeader(WORD w_PCIBoardEepromAddress,
+ char *pc_PCIChipInformation, struct comedi_device *dev)
+{
+ WORD w_Temp, i, w_Count = 0;
+ UINT ui_Temp;
+ str_MainHeader s_MainHeader;
+ str_DigitalInputHeader s_DigitalInputHeader;
+ str_DigitalOutputHeader s_DigitalOutputHeader;
+ //str_TimerMainHeader s_TimerMainHeader,s_WatchdogMainHeader;
+ str_AnalogOutputHeader s_AnalogOutputHeader;
+ str_AnalogInputHeader s_AnalogInputHeader;
+
+ // Read size
+ s_MainHeader.w_HeaderSize =
+ w_EepromReadWord(w_PCIBoardEepromAddress, pc_PCIChipInformation,
+ 0x100 + 8);
+
+ // Read nbr of functionality
+ w_Temp = w_EepromReadWord(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation, 0x100 + 10);
+ s_MainHeader.b_Nfunctions = (BYTE) w_Temp & 0x00FF;
+
+ // Read functionality details
+ for (i = 0; i < s_MainHeader.b_Nfunctions; i++) {
+ // Read Type
+ w_Temp = w_EepromReadWord(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation, 0x100 + 12 + w_Count);
+ s_MainHeader.s_Functions[i].b_Type = (BYTE) w_Temp & 0x3F;
+ w_Count = w_Count + 2;
+ //Read Address
+ s_MainHeader.s_Functions[i].w_Address =
+ w_EepromReadWord(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation, 0x100 + 12 + w_Count);
+ w_Count = w_Count + 2;
+ }
+
+ // Display main header info
+ for (i = 0; i < s_MainHeader.b_Nfunctions; i++) {
+
+ switch (s_MainHeader.s_Functions[i].b_Type) {
+ case EEPROM_DIGITALINPUT:
+ i_EepromReadDigitalInputHeader(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation,
+ s_MainHeader.s_Functions[i].w_Address,
+ &s_DigitalInputHeader);
+ this_board->i_NbrDiChannel =
+ s_DigitalInputHeader.w_Nchannel;
+ break;
+
+ case EEPROM_DIGITALOUTPUT:
+ i_EepromReadDigitalOutputHeader(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation,
+ s_MainHeader.s_Functions[i].w_Address,
+ &s_DigitalOutputHeader);
+ this_board->i_NbrDoChannel =
+ s_DigitalOutputHeader.w_Nchannel;
+ ui_Temp = 0xffffffff;
+ this_board->i_DoMaxdata =
+ ui_Temp >> (32 - this_board->i_NbrDoChannel);
+ break;
+
+ case EEPROM_ANALOGINPUT:
+ i_EepromReadAnlogInputHeader(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation,
+ s_MainHeader.s_Functions[i].w_Address,
+ &s_AnalogInputHeader);
+ if (!(strcmp(this_board->pc_DriverName, "apci3200")))
+ this_board->i_NbrAiChannel =
+ s_AnalogInputHeader.w_Nchannel * 4;
+ else
+ this_board->i_NbrAiChannel =
+ s_AnalogInputHeader.w_Nchannel;
+ this_board->i_Dma = s_AnalogInputHeader.b_HasDma;
+ this_board->ui_MinAcquisitiontimeNs =
+ (UINT) s_AnalogInputHeader.w_MinConvertTiming *
+ 1000;
+ this_board->ui_MinDelaytimeNs =
+ (UINT) s_AnalogInputHeader.w_MinDelayTiming *
+ 1000;
+ ui_Temp = 0xffff;
+ this_board->i_AiMaxdata =
+ ui_Temp >> (16 -
+ s_AnalogInputHeader.b_Resolution);
+ break;
+
+ case EEPROM_ANALOGOUTPUT:
+ i_EepromReadAnlogOutputHeader(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation,
+ s_MainHeader.s_Functions[i].w_Address,
+ &s_AnalogOutputHeader);
+ this_board->i_NbrAoChannel =
+ s_AnalogOutputHeader.w_Nchannel;
+ ui_Temp = 0xffff;
+ this_board->i_AoMaxdata =
+ ui_Temp >> (16 -
+ s_AnalogOutputHeader.b_Resolution);
+ break;
+
+ case EEPROM_TIMER:
+ this_board->i_Timer = 1; //Timer subdevice present
+ break;
+
+ case EEPROM_WATCHDOG:
+ this_board->i_Timer = 1; //Timer subdevice present
+ break;
+
+ case EEPROM_TIMER_WATCHDOG_COUNTER:
+ this_board->i_Timer = 1; //Timer subdevice present
+ }
+ }
+
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_EepromReadDigitalInputHeader(WORD |
+| w_PCIBoardEepromAddress,char *pc_PCIChipInformation, |
+| WORD w_Address,str_DigitalInputHeader *s_Header) |
+| |
++----------------------------------------------------------------------------+
+| Task : Read Digital Input Header |
++----------------------------------------------------------------------------+
+| Input Parameters : WORD w_PCIBoardEepromAddress : PCI eeprom address |
+| |
+| char *pc_PCIChipInformation : PCI Chip Type. |
+| |
+| str_DigitalInputHeader *s_Header: Digita Input Header |
+| Pointer |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0 |
++----------------------------------------------------------------------------+
+*/
+INT i_EepromReadDigitalInputHeader(WORD w_PCIBoardEepromAddress,
+ char *pc_PCIChipInformation, WORD w_Address,
+ str_DigitalInputHeader * s_Header)
+{
+ WORD w_Temp;
+
+ // read nbr of channels
+ s_Header->w_Nchannel =
+ w_EepromReadWord(w_PCIBoardEepromAddress, pc_PCIChipInformation,
+ 0x100 + w_Address + 6);
+
+ // interruptible or not
+ w_Temp = w_EepromReadWord(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation, 0x100 + w_Address + 8);
+ s_Header->b_Interruptible = (BYTE) (w_Temp >> 7) & 0x01;
+
+// How many interruptible logic
+ s_Header->w_NinterruptLogic =
+ w_EepromReadWord(w_PCIBoardEepromAddress, pc_PCIChipInformation,
+ 0x100 + w_Address + 10);
+
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_EepromReadDigitalOutputHeader(WORD |
+| w_PCIBoardEepromAddress,char *pc_PCIChipInformation, |
+| WORD w_Address,str_DigitalOutputHeader *s_Header) |
+| |
++----------------------------------------------------------------------------+
+| Task : Read Digital Output Header |
++----------------------------------------------------------------------------+
+| Input Parameters : WORD w_PCIBoardEepromAddress : PCI eeprom address |
+| |
+| char *pc_PCIChipInformation : PCI Chip Type. |
+| |
+| str_DigitalOutputHeader *s_Header: Digital Output Header|
+| Pointer |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0 |
++----------------------------------------------------------------------------+
+*/
+INT i_EepromReadDigitalOutputHeader(WORD w_PCIBoardEepromAddress,
+ char *pc_PCIChipInformation, WORD w_Address,
+ str_DigitalOutputHeader * s_Header)
+{
+// Read Nbr channels
+ s_Header->w_Nchannel =
+ w_EepromReadWord(w_PCIBoardEepromAddress, pc_PCIChipInformation,
+ 0x100 + w_Address + 6);
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_EepromReadTimerHeader(WORD w_PCIBoardEepromAddress, |
+| char *pc_PCIChipInformation,WORD w_Address, |
+| str_TimerMainHeader *s_Header) |
++----------------------------------------------------------------------------+
+| Task : Read Timer or Watchdog Header |
++----------------------------------------------------------------------------+
+| Input Parameters : WORD w_PCIBoardEepromAddress : PCI eeprom address |
+| |
+| char *pc_PCIChipInformation : PCI Chip Type. |
+| |
+| str_TimerMainHeader *s_Header: Timer Header |
+| Pointer |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0 |
++----------------------------------------------------------------------------+
+*/
+INT i_EepromReadTimerHeader(WORD w_PCIBoardEepromAddress,
+ char *pc_PCIChipInformation, WORD w_Address,
+ str_TimerMainHeader * s_Header)
+{
+
+ WORD i, w_Size = 0, w_Temp;
+
+//Read No of Timer
+ s_Header->w_Ntimer =
+ w_EepromReadWord(w_PCIBoardEepromAddress, pc_PCIChipInformation,
+ 0x100 + w_Address + 6);
+//Read header size
+
+ for (i = 0; i < s_Header->w_Ntimer; i++) {
+ s_Header->s_TimerDetails[i].w_HeaderSize =
+ w_EepromReadWord(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation,
+ 0x100 + w_Address + 8 + w_Size + 0);
+ w_Temp = w_EepromReadWord(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation,
+ 0x100 + w_Address + 8 + w_Size + 2);
+
+ //Read Resolution
+ s_Header->s_TimerDetails[i].b_Resolution =
+ (BYTE) (w_Temp >> 10) & 0x3F;
+
+ //Read Mode
+ s_Header->s_TimerDetails[i].b_Mode =
+ (BYTE) (w_Temp >> 4) & 0x3F;
+
+ w_Temp = w_EepromReadWord(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation,
+ 0x100 + w_Address + 8 + w_Size + 4);
+
+ //Read MinTiming
+ s_Header->s_TimerDetails[i].w_MinTiming = (w_Temp >> 6) & 0x3FF;
+
+ //Read Timebase
+ s_Header->s_TimerDetails[i].b_TimeBase = (BYTE) (w_Temp) & 0x3F;
+ w_Size += s_Header->s_TimerDetails[i].w_HeaderSize;
+ }
+
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_EepromReadAnlogOutputHeader(WORD |
+| w_PCIBoardEepromAddress,char *pc_PCIChipInformation, |
+| WORD w_Address,str_AnalogOutputHeader *s_Header) |
++----------------------------------------------------------------------------+
+| Task : Read Nalog Output Header |
++----------------------------------------------------------------------------+
+| Input Parameters : WORD w_PCIBoardEepromAddress : PCI eeprom address |
+| |
+| char *pc_PCIChipInformation : PCI Chip Type. |
+| |
+| str_AnalogOutputHeader *s_Header:Anlog Output Header |
+| Pointer |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0 |
++----------------------------------------------------------------------------+
+*/
+
+INT i_EepromReadAnlogOutputHeader(WORD w_PCIBoardEepromAddress,
+ char *pc_PCIChipInformation, WORD w_Address,
+ str_AnalogOutputHeader * s_Header)
+{
+ WORD w_Temp;
+ // No of channels for 1st hard component
+ w_Temp = w_EepromReadWord(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation, 0x100 + w_Address + 10);
+ s_Header->w_Nchannel = (w_Temp >> 4) & 0x03FF;
+ // Resolution for 1st hard component
+ w_Temp = w_EepromReadWord(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation, 0x100 + w_Address + 16);
+ s_Header->b_Resolution = (BYTE) (w_Temp >> 8) & 0xFF;
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_EepromReadAnlogInputHeader(WORD |
+| w_PCIBoardEepromAddress,char *pc_PCIChipInformation, |
+| WORD w_Address,str_AnalogInputHeader *s_Header) |
++----------------------------------------------------------------------------+
+| Task : Read Nalog Output Header |
++----------------------------------------------------------------------------+
+| Input Parameters : WORD w_PCIBoardEepromAddress : PCI eeprom address |
+| |
+| char *pc_PCIChipInformation : PCI Chip Type. |
+| |
+| str_AnalogInputHeader *s_Header:Anlog Input Header |
+| Pointer |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0 |
++----------------------------------------------------------------------------+
+*/
+
+// Reads only for ONE hardware component
+INT i_EepromReadAnlogInputHeader(WORD w_PCIBoardEepromAddress,
+ char *pc_PCIChipInformation, WORD w_Address,
+ str_AnalogInputHeader * s_Header)
+{
+ WORD w_Temp, w_Offset;
+ w_Temp = w_EepromReadWord(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation, 0x100 + w_Address + 10);
+ s_Header->w_Nchannel = (w_Temp >> 4) & 0x03FF;
+ s_Header->w_MinConvertTiming =
+ w_EepromReadWord(w_PCIBoardEepromAddress, pc_PCIChipInformation,
+ 0x100 + w_Address + 16);
+ s_Header->w_MinDelayTiming =
+ w_EepromReadWord(w_PCIBoardEepromAddress, pc_PCIChipInformation,
+ 0x100 + w_Address + 30);
+ w_Temp = w_EepromReadWord(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation, 0x100 + w_Address + 20);
+ s_Header->b_HasDma = (w_Temp >> 13) & 0x01; // whether dma present or not
+
+ w_Temp = w_EepromReadWord(w_PCIBoardEepromAddress, pc_PCIChipInformation, 0x100 + w_Address + 72); // reading Y
+ w_Temp = w_Temp & 0x00FF;
+ if (w_Temp) //Y>0
+ {
+ w_Offset = 74 + (2 * w_Temp) + (10 * (1 + (w_Temp / 16))); // offset of first analog input single header
+ w_Offset = w_Offset + 2; // resolution
+ } else //Y=0
+ {
+ w_Offset = 74;
+ w_Offset = w_Offset + 2; // resolution
+ }
+
+// read Resolution
+ w_Temp = w_EepromReadWord(w_PCIBoardEepromAddress,
+ pc_PCIChipInformation, 0x100 + w_Address + w_Offset);
+ s_Header->b_Resolution = w_Temp & 0x001F; // last 5 bits
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/amcc_s5933_58.h b/drivers/staging/comedi/drivers/addi-data/amcc_s5933_58.h
new file mode 100644
index 000000000000..617dc087dcbf
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/amcc_s5933_58.h
@@ -0,0 +1,457 @@
+/*
+ Modified by umesh on 16th may 2001
+ Modified by sarath on 22nd may 2001
+*/
+
+/*
+ comedi/drivers/amcc_s5933_v_58.h
+
+ Stuff for AMCC S5933 PCI Controller
+
+ Author: Michal Dobes <majkl@tesnet.cz>
+
+ Inspirated from general-purpose AMCC S5933 PCI Matchmaker driver
+ made by Andrea Cisternino <acister@pcape1.pi.infn.it>
+ and as result of espionage from MITE code made by David A. Schleef.
+ Thanks to AMCC for their on-line documentation and bus master DMA
+ example.
+*/
+
+#ifndef _AMCC_S5933_H_
+#define _AMCC_S5933_H_
+
+#include <linux/pci.h>
+#include "../../comedidev.h"
+
+#ifdef PCI_SUPPORT_VER1
+#error Sorry, no support for 2.1.55 and older! :-((((
+#endif
+
+/***********Added by sarath for compatibility with APCI3120
+
+*************************/
+
+#define FIFO_ADVANCE_ON_BYTE_2 0x20000000 // written on base0
+
+#define AMWEN_ENABLE 0x02 // added for step 6 dma written on base2
+#define A2P_FIFO_WRITE_ENABLE 0x01
+
+#define AGCSTS_TC_ENABLE 0x10000000 // Added for transfer count enable bit
+
+// ADDON RELATED ADDITIONS
+// Constant
+#define APCI3120_ENABLE_TRANSFER_ADD_ON_LOW 0x00
+#define APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH 0x1200
+#define APCI3120_A2P_FIFO_MANAGEMENT 0x04000400L
+#define APCI3120_AMWEN_ENABLE 0x02
+#define APCI3120_A2P_FIFO_WRITE_ENABLE 0x01
+#define APCI3120_FIFO_ADVANCE_ON_BYTE_2 0x20000000L
+#define APCI3120_ENABLE_WRITE_TC_INT 0x00004000L
+#define APCI3120_CLEAR_WRITE_TC_INT 0x00040000L
+#define APCI3120_DISABLE_AMWEN_AND_A2P_FIFO_WRITE 0x0
+#define APCI3120_DISABLE_BUS_MASTER_ADD_ON 0x0
+#define APCI3120_DISABLE_BUS_MASTER_PCI 0x0
+
+ // ADD_ON ::: this needed since apci supports 16 bit interface to add on
+#define APCI3120_ADD_ON_AGCSTS_LOW 0x3C
+#define APCI3120_ADD_ON_AGCSTS_HIGH APCI3120_ADD_ON_AGCSTS_LOW + 2
+#define APCI3120_ADD_ON_MWAR_LOW 0x24
+#define APCI3120_ADD_ON_MWAR_HIGH APCI3120_ADD_ON_MWAR_LOW + 2
+#define APCI3120_ADD_ON_MWTC_LOW 0x058
+#define APCI3120_ADD_ON_MWTC_HIGH APCI3120_ADD_ON_MWTC_LOW + 2
+
+// AMCC
+#define APCI3120_AMCC_OP_MCSR 0x3C
+#define APCI3120_AMCC_OP_REG_INTCSR 0x38
+
+/*******from here all upward definitions are added by sarath */
+
+/****************************************************************************/
+/* AMCC Operation Register Offsets - PCI */
+/****************************************************************************/
+
+#define AMCC_OP_REG_OMB1 0x00
+#define AMCC_OP_REG_OMB2 0x04
+#define AMCC_OP_REG_OMB3 0x08
+#define AMCC_OP_REG_OMB4 0x0c
+#define AMCC_OP_REG_IMB1 0x10
+#define AMCC_OP_REG_IMB2 0x14
+#define AMCC_OP_REG_IMB3 0x18
+#define AMCC_OP_REG_IMB4 0x1c
+#define AMCC_OP_REG_FIFO 0x20
+#define AMCC_OP_REG_MWAR 0x24
+#define AMCC_OP_REG_MWTC 0x28
+#define AMCC_OP_REG_MRAR 0x2c
+#define AMCC_OP_REG_MRTC 0x30
+#define AMCC_OP_REG_MBEF 0x34
+#define AMCC_OP_REG_INTCSR 0x38
+#define AMCC_OP_REG_INTCSR_SRC (AMCC_OP_REG_INTCSR + 2) /* INT source */
+#define AMCC_OP_REG_INTCSR_FEC (AMCC_OP_REG_INTCSR + 3) /* FIFO ctrl */
+#define AMCC_OP_REG_MCSR 0x3c
+#define AMCC_OP_REG_MCSR_NVDATA (AMCC_OP_REG_MCSR + 2) /* Data in byte 2 */
+#define AMCC_OP_REG_MCSR_NVCMD (AMCC_OP_REG_MCSR + 3) /* Command in byte 3 */
+
+#define AMCC_FIFO_DEPTH_DWORD 8
+#define AMCC_FIFO_DEPTH_BYTES (8 * sizeof (u32))
+
+/****************************************************************************/
+/* AMCC Operation Registers Size - PCI */
+/****************************************************************************/
+
+#define AMCC_OP_REG_SIZE 64 /* in bytes */
+
+/****************************************************************************/
+/* AMCC Operation Register Offsets - Add-on */
+/****************************************************************************/
+
+#define AMCC_OP_REG_AIMB1 0x00
+#define AMCC_OP_REG_AIMB2 0x04
+#define AMCC_OP_REG_AIMB3 0x08
+#define AMCC_OP_REG_AIMB4 0x0c
+#define AMCC_OP_REG_AOMB1 0x10
+#define AMCC_OP_REG_AOMB2 0x14
+#define AMCC_OP_REG_AOMB3 0x18
+#define AMCC_OP_REG_AOMB4 0x1c
+#define AMCC_OP_REG_AFIFO 0x20
+#define AMCC_OP_REG_AMWAR 0x24
+#define AMCC_OP_REG_APTA 0x28
+#define AMCC_OP_REG_APTD 0x2c
+#define AMCC_OP_REG_AMRAR 0x30
+#define AMCC_OP_REG_AMBEF 0x34
+#define AMCC_OP_REG_AINT 0x38
+#define AMCC_OP_REG_AGCSTS 0x3c
+#define AMCC_OP_REG_AMWTC 0x58
+#define AMCC_OP_REG_AMRTC 0x5c
+
+/****************************************************************************/
+/* AMCC - Add-on General Control/Status Register */
+/****************************************************************************/
+
+#define AGCSTS_CONTROL_MASK 0xfffff000
+#define AGCSTS_NV_ACC_MASK 0xe0000000
+#define AGCSTS_RESET_MASK 0x0e000000
+#define AGCSTS_NV_DA_MASK 0x00ff0000
+#define AGCSTS_BIST_MASK 0x0000f000
+#define AGCSTS_STATUS_MASK 0x000000ff
+#define AGCSTS_TCZERO_MASK 0x000000c0
+#define AGCSTS_FIFO_ST_MASK 0x0000003f
+
+#define AGCSTS_RESET_MBFLAGS 0x08000000
+#define AGCSTS_RESET_P2A_FIFO 0x04000000
+#define AGCSTS_RESET_A2P_FIFO 0x02000000
+#define AGCSTS_RESET_FIFOS (AGCSTS_RESET_A2P_FIFO | AGCSTS_RESET_P2A_FIFO)
+
+#define AGCSTS_A2P_TCOUNT 0x00000080
+#define AGCSTS_P2A_TCOUNT 0x00000040
+
+#define AGCSTS_FS_P2A_EMPTY 0x00000020
+#define AGCSTS_FS_P2A_HALF 0x00000010
+#define AGCSTS_FS_P2A_FULL 0x00000008
+
+#define AGCSTS_FS_A2P_EMPTY 0x00000004
+#define AGCSTS_FS_A2P_HALF 0x00000002
+#define AGCSTS_FS_A2P_FULL 0x00000001
+
+/****************************************************************************/
+/* AMCC - Add-on Interrupt Control/Status Register */
+/****************************************************************************/
+
+#define AINT_INT_MASK 0x00ff0000
+#define AINT_SEL_MASK 0x0000ffff
+#define AINT_IS_ENSEL_MASK 0x00001f1f
+
+#define AINT_INT_ASSERTED 0x00800000
+#define AINT_BM_ERROR 0x00200000
+#define AINT_BIST_INT 0x00100000
+
+#define AINT_RT_COMPLETE 0x00080000
+#define AINT_WT_COMPLETE 0x00040000
+
+#define AINT_OUT_MB_INT 0x00020000
+#define AINT_IN_MB_INT 0x00010000
+
+#define AINT_READ_COMPL 0x00008000
+#define AINT_WRITE_COMPL 0x00004000
+
+#define AINT_OMB_ENABLE 0x00001000
+#define AINT_OMB_SELECT 0x00000c00
+#define AINT_OMB_BYTE 0x00000300
+
+#define AINT_IMB_ENABLE 0x00000010
+#define AINT_IMB_SELECT 0x0000000c
+#define AINT_IMB_BYTE 0x00000003
+
+/* Enable Bus Mastering */
+#define EN_A2P_TRANSFERS 0x00000400
+/* FIFO Flag Reset */
+#define RESET_A2P_FLAGS 0x04000000L
+/* FIFO Relative Priority */
+#define A2P_HI_PRIORITY 0x00000100L
+/* Identify Interrupt Sources */
+#define ANY_S593X_INT 0x00800000L
+#define READ_TC_INT 0x00080000L
+#define WRITE_TC_INT 0x00040000L
+#define IN_MB_INT 0x00020000L
+#define MASTER_ABORT_INT 0x00100000L
+#define TARGET_ABORT_INT 0x00200000L
+#define BUS_MASTER_INT 0x00200000L
+
+/****************************************************************************/
+
+struct pcilst_struct {
+ struct pcilst_struct *next;
+ int used;
+ struct pci_dev *pcidev;
+ unsigned short vendor;
+ unsigned short device;
+ unsigned int master;
+ unsigned char pci_bus;
+ unsigned char pci_slot;
+ unsigned char pci_func;
+ unsigned int io_addr[5];
+ unsigned int irq;
+};
+
+struct pcilst_struct *amcc_devices; // ptr to root list of all amcc devices
+
+/****************************************************************************/
+
+void v_pci_card_list_init(unsigned short pci_vendor, char display);
+void v_pci_card_list_cleanup(unsigned short pci_vendor);
+struct pcilst_struct *ptr_find_free_pci_card_by_device(unsigned short vendor_id,
+ unsigned short
+ device_id);
+int i_find_free_pci_card_by_position(unsigned short vendor_id,
+ unsigned short device_id,
+ unsigned short pci_bus,
+ unsigned short pci_slot,
+ struct pcilst_struct **card);
+struct pcilst_struct *ptr_select_and_alloc_pci_card(unsigned short vendor_id,
+ unsigned short device_id,
+ unsigned short pci_bus,
+ unsigned short pci_slot);
+
+int i_pci_card_alloc(struct pcilst_struct *amcc);
+int i_pci_card_free(struct pcilst_struct *amcc);
+void v_pci_card_list_display(void);
+int i_pci_card_data(struct pcilst_struct *amcc,
+ unsigned char *pci_bus, unsigned char *pci_slot,
+ unsigned char *pci_func, unsigned short *io_addr,
+ unsigned short *irq, unsigned short *master);
+
+/****************************************************************************/
+
+/* build list of amcc cards in this system */
+void v_pci_card_list_init(unsigned short pci_vendor, char display)
+{
+ struct pci_dev *pcidev;
+ struct pcilst_struct *amcc, *last;
+ int i;
+
+ amcc_devices = NULL;
+ last = NULL;
+
+ pci_for_each_dev(pcidev) {
+ if (pcidev->vendor == pci_vendor) {
+ amcc = kmalloc(sizeof(*amcc), GFP_KERNEL);
+ memset(amcc, 0, sizeof(*amcc));
+
+ amcc->pcidev = pcidev;
+ if (last) {
+ last->next = amcc;
+ } else {
+ amcc_devices = amcc;
+ }
+ last = amcc;
+
+ amcc->vendor = pcidev->vendor;
+ amcc->device = pcidev->device;
+#if 0
+ amcc->master = pcidev->master; // how get this information under 2.4 kernels?
+#endif
+ amcc->pci_bus = pcidev->bus->number;
+ amcc->pci_slot = PCI_SLOT(pcidev->devfn);
+ amcc->pci_func = PCI_FUNC(pcidev->devfn);
+ for (i = 0; i < 5; i++)
+ amcc->io_addr[i] =
+ pcidev->resource[i].start & ~3UL;
+ amcc->irq = pcidev->irq;
+ }
+ }
+
+ if (display)
+ v_pci_card_list_display();
+}
+
+/****************************************************************************/
+/* free up list of amcc cards in this system */
+void v_pci_card_list_cleanup(unsigned short pci_vendor)
+{
+ struct pcilst_struct *amcc, *next;
+
+ for (amcc = amcc_devices; amcc; amcc = next) {
+ next = amcc->next;
+ kfree(amcc);
+ }
+
+ amcc_devices = NULL;
+}
+
+/****************************************************************************/
+/* find first unused card with this device_id */
+struct pcilst_struct *ptr_find_free_pci_card_by_device(unsigned short vendor_id,
+ unsigned short device_id)
+{
+ struct pcilst_struct *amcc, *next;
+
+ for (amcc = amcc_devices; amcc; amcc = next) {
+ next = amcc->next;
+ if ((!amcc->used) && (amcc->device == device_id)
+ && (amcc->vendor == vendor_id))
+ return amcc;
+
+ }
+
+ return NULL;
+}
+
+/****************************************************************************/
+/* find card on requested position */
+int i_find_free_pci_card_by_position(unsigned short vendor_id,
+ unsigned short device_id,
+ unsigned short pci_bus,
+ unsigned short pci_slot,
+ struct pcilst_struct **card)
+{
+ struct pcilst_struct *amcc, *next;
+
+ *card = NULL;
+ for (amcc = amcc_devices; amcc; amcc = next) {
+ next = amcc->next;
+ if ((amcc->vendor == vendor_id) && (amcc->device == device_id)
+ && (amcc->pci_bus == pci_bus)
+ && (amcc->pci_slot == pci_slot)) {
+ if (!(amcc->used)) {
+ *card = amcc;
+ return 0; // ok, card is found
+ } else {
+ rt_printk
+ (" - \nCard on requested position is used b:s %d:%d!\n",
+ pci_bus, pci_slot);
+ return 2; // card exist but is used
+ }
+ }
+ }
+
+ return 1; // no card found
+}
+
+/****************************************************************************/
+/* mark card as used */
+int i_pci_card_alloc(struct pcilst_struct *amcc)
+{
+ if (!amcc)
+ return -1;
+
+ if (amcc->used)
+ return 1;
+ amcc->used = 1;
+ return 0;
+}
+
+/****************************************************************************/
+/* mark card as free */
+int i_pci_card_free(struct pcilst_struct *amcc)
+{
+ if (!amcc)
+ return -1;
+
+ if (!amcc->used)
+ return 1;
+ amcc->used = 0;
+ return 0;
+}
+
+/****************************************************************************/
+/* display list of found cards */
+void v_pci_card_list_display(void)
+{
+ struct pcilst_struct *amcc, *next;
+
+ printk("List of pci cards\n");
+ printk("bus:slot:func vendor device master io_amcc io_daq irq used\n");
+
+ for (amcc = amcc_devices; amcc; amcc = next) {
+ next = amcc->next;
+ printk
+ ("%2d %2d %2d 0x%4x 0x%4x %3s 0x%4x 0x%4x %2d %2d\n",
+ amcc->pci_bus, amcc->pci_slot, amcc->pci_func,
+ amcc->vendor, amcc->device, amcc->master ? "yes" : "no",
+ amcc->io_addr[0], amcc->io_addr[2], amcc->irq, amcc->used);
+
+ }
+}
+
+/****************************************************************************/
+/* return all card information for driver */
+int i_pci_card_data(struct pcilst_struct *amcc,
+ unsigned char *pci_bus, unsigned char *pci_slot,
+ unsigned char *pci_func, unsigned short *io_addr,
+ unsigned short *irq, unsigned short *master)
+{
+ int i;
+
+ if (!amcc)
+ return -1;
+ *pci_bus = amcc->pci_bus;
+ *pci_slot = amcc->pci_slot;
+ *pci_func = amcc->pci_func;
+ for (i = 0; i < 5; i++)
+ io_addr[i] = amcc->io_addr[i];
+ *irq = amcc->irq;
+ *master = amcc->master;
+ return 0;
+}
+
+/****************************************************************************/
+/* select and alloc card */
+struct pcilst_struct *ptr_select_and_alloc_pci_card(unsigned short vendor_id,
+ unsigned short device_id,
+ unsigned short pci_bus,
+ unsigned short pci_slot)
+{
+ struct pcilst_struct *card;
+
+ if ((pci_bus < 1) & (pci_slot < 1)) { // use autodetection
+ if ((card = ptr_find_free_pci_card_by_device(vendor_id,
+ device_id)) ==
+ NULL) {
+ rt_printk(" - Unused card not found in system!\n");
+ return NULL;
+ }
+ } else {
+ switch (i_find_free_pci_card_by_position(vendor_id, device_id,
+ pci_bus, pci_slot,
+ &card)) {
+ case 1:
+ rt_printk
+ (" - Card not found on requested position b:s %d:%d!\n",
+ pci_bus, pci_slot);
+ return NULL;
+ case 2:
+ rt_printk
+ (" - Card on requested position is used b:s %d:%d!\n",
+ pci_bus, pci_slot);
+ return NULL;
+ }
+ }
+
+ if (i_pci_card_alloc(card) != 0) {
+ rt_printk(" - Can't allocate card!\n");
+ return NULL;
+ }
+
+ return card;
+}
+
+#endif
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_APCI1710.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_APCI1710.c
new file mode 100644
index 000000000000..3f8929c09070
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_APCI1710.c
@@ -0,0 +1,1265 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-------------------------------+---------------------------------------+
+ | Project : APCI-1710 | Compiler : GCC |
+ | Module name : hwdrv_apci1710.c| Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-------------------------------+---------------------------------------+
+ | Description : Hardware Layer Acces For APCI-1710 |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +----------+-----------+------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | | | |
+ | | | |
+ | | | |
+ +----------+-----------+------------------------------------------------+
+*/
+#include "hwdrv_APCI1710.h"
+#include "APCI1710_Inp_cpt.c"
+
+#include "APCI1710_Ssi.c"
+#include "APCI1710_Tor.c"
+#include "APCI1710_Ttl.c"
+#include "APCI1710_Dig_io.c"
+#include "APCI1710_82x54.c"
+#include "APCI1710_Chrono.c"
+#include "APCI1710_Pwm.c"
+#include "APCI1710_INCCPT.c"
+
+void i_ADDI_AttachPCI1710(struct comedi_device * dev)
+{
+ struct comedi_subdevice *s;
+ int ret = 0;
+ int n_subdevices = 9;
+
+ //Update-0.7.57->0.7.68dev->n_subdevices = 9;
+ if ((ret = alloc_subdevices(dev, n_subdevices)) < 0)
+ return;
+
+ // Allocate and Initialise Timer Subdevice Structures
+ s = dev->subdevices + 0;
+
+ s->type = COMEDI_SUBD_TIMER;
+ s->subdev_flags = SDF_WRITEABLE | SDF_RT | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 3;
+ s->maxdata = 0;
+ s->len_chanlist = 3;
+ s->range_table = &range_digital;
+ s->insn_write = i_APCI1710_InsnWriteEnableDisableTimer;
+ s->insn_read = i_APCI1710_InsnReadAllTimerValue;
+ s->insn_config = i_APCI1710_InsnConfigInitTimer;
+ s->insn_bits = i_APCI1710_InsnBitsTimer;
+
+ // Allocate and Initialise DIO Subdevice Structures
+ s = dev->subdevices + 1;
+
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags =
+ SDF_WRITEABLE | SDF_READABLE | SDF_RT | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 7;
+ s->maxdata = 1;
+ s->len_chanlist = 7;
+ s->range_table = &range_digital;
+ s->insn_config = i_APCI1710_InsnConfigDigitalIO;
+ s->insn_read = i_APCI1710_InsnReadDigitalIOChlValue;
+ s->insn_bits = i_APCI1710_InsnBitsDigitalIOPortOnOff;
+ s->insn_write = i_APCI1710_InsnWriteDigitalIOChlOnOff;
+
+ // Allocate and Initialise Chrono Subdevice Structures
+ s = dev->subdevices + 2;
+
+ s->type = COMEDI_SUBD_CHRONO;
+ s->subdev_flags = SDF_WRITEABLE | SDF_RT | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 4;
+ s->maxdata = 0;
+ s->len_chanlist = 4;
+ s->range_table = &range_digital;
+ s->insn_write = i_APCI1710_InsnWriteEnableDisableChrono;
+ s->insn_read = i_APCI1710_InsnReadChrono;
+ s->insn_config = i_APCI1710_InsnConfigInitChrono;
+ s->insn_bits = i_APCI1710_InsnBitsChronoDigitalIO;
+
+ // Allocate and Initialise PWM Subdevice Structures
+ s = dev->subdevices + 3;
+ s->type = COMEDI_SUBD_PWM;
+ s->subdev_flags =
+ SDF_WRITEABLE | SDF_READABLE | SDF_RT | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 3;
+ s->maxdata = 1;
+ s->len_chanlist = 3;
+ s->range_table = &range_digital;
+ s->io_bits = 0; //all bits input
+ s->insn_config = i_APCI1710_InsnConfigPWM;
+ s->insn_read = i_APCI1710_InsnReadGetPWMStatus;
+ s->insn_write = i_APCI1710_InsnWritePWM;
+ s->insn_bits = i_APCI1710_InsnBitsReadPWMInterrupt;
+
+ // Allocate and Initialise TTLIO Subdevice Structures
+ s = dev->subdevices + 4;
+ s->type = COMEDI_SUBD_TTLIO;
+ s->subdev_flags =
+ SDF_WRITEABLE | SDF_READABLE | SDF_RT | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->len_chanlist = 8;
+ s->range_table = &range_apci1710_ttl; // to pass arguments in range
+ s->insn_config = i_APCI1710_InsnConfigInitTTLIO;
+ s->insn_bits = i_APCI1710_InsnBitsReadTTLIO;
+ s->insn_write = i_APCI1710_InsnWriteSetTTLIOChlOnOff;
+ s->insn_read = i_APCI1710_InsnReadTTLIOAllPortValue;
+
+ // Allocate and Initialise TOR Subdevice Structures
+ s = dev->subdevices + 5;
+ s->type = COMEDI_SUBD_TOR;
+ s->subdev_flags =
+ SDF_WRITEABLE | SDF_READABLE | SDF_RT | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->len_chanlist = 8;
+ s->range_table = &range_digital;
+ s->io_bits = 0; //all bits input
+ s->insn_config = i_APCI1710_InsnConfigInitTorCounter;
+ s->insn_read = i_APCI1710_InsnReadGetTorCounterInitialisation;
+ s->insn_write = i_APCI1710_InsnWriteEnableDisableTorCounter;
+ s->insn_bits = i_APCI1710_InsnBitsGetTorCounterProgressStatusAndValue;
+
+ // Allocate and Initialise SSI Subdevice Structures
+ s = dev->subdevices + 6;
+ s->type = COMEDI_SUBD_SSI;
+ s->subdev_flags =
+ SDF_WRITEABLE | SDF_READABLE | SDF_RT | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->len_chanlist = 4;
+ s->range_table = &range_apci1710_ssi;
+ s->insn_config = i_APCI1710_InsnConfigInitSSI;
+ s->insn_read = i_APCI1710_InsnReadSSIValue;
+ s->insn_bits = i_APCI1710_InsnBitsSSIDigitalIO;
+
+ // Allocate and Initialise PULSEENCODER Subdevice Structures
+ s = dev->subdevices + 7;
+ s->type = COMEDI_SUBD_PULSEENCODER;
+ s->subdev_flags =
+ SDF_WRITEABLE | SDF_READABLE | SDF_RT | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->len_chanlist = 4;
+ s->range_table = &range_digital;
+ s->insn_config = i_APCI1710_InsnConfigInitPulseEncoder;
+ s->insn_write = i_APCI1710_InsnWriteEnableDisablePulseEncoder;
+ s->insn_bits = i_APCI1710_InsnBitsReadWritePulseEncoder;
+ s->insn_read = i_APCI1710_InsnReadInterruptPulseEncoder;
+
+ // Allocate and Initialise INCREMENTALCOUNTER Subdevice Structures
+ s = dev->subdevices + 8;
+ s->type = COMEDI_SUBD_INCREMENTALCOUNTER;
+ s->subdev_flags =
+ SDF_WRITEABLE | SDF_READABLE | SDF_RT | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 500;
+ s->maxdata = 1;
+ s->len_chanlist = 500;
+ s->range_table = &range_apci1710_inccpt;
+ s->insn_config = i_APCI1710_InsnConfigINCCPT;
+ s->insn_write = i_APCI1710_InsnWriteINCCPT;
+ s->insn_read = i_APCI1710_InsnReadINCCPT;
+ s->insn_bits = i_APCI1710_InsnBitsINCCPT;
+}
+
+int i_APCI1710_Reset(struct comedi_device * dev);
+void v_APCI1710_Interrupt(int irq, void *d);
+//for 1710
+
+int i_APCI1710_Reset(struct comedi_device * dev)
+{
+ int ret;
+ DWORD dw_Dummy;
+
+ /*********************************/
+ /* Read all module configuration */
+ /*********************************/
+ ret = inl(devpriv->s_BoardInfos.ui_Address + 60);
+ devpriv->s_BoardInfos.dw_MolduleConfiguration[0] = ret;
+
+ ret = inl(devpriv->s_BoardInfos.ui_Address + 124);
+ devpriv->s_BoardInfos.dw_MolduleConfiguration[1] = ret;
+
+ ret = inl(devpriv->s_BoardInfos.ui_Address + 188);
+ devpriv->s_BoardInfos.dw_MolduleConfiguration[2] = ret;
+
+ ret = inl(devpriv->s_BoardInfos.ui_Address + 252);
+ devpriv->s_BoardInfos.dw_MolduleConfiguration[3] = ret;
+
+ // outl(0x80808082,devpriv->s_BoardInfos.ui_Address+0x60);
+ outl(0x83838383, devpriv->s_BoardInfos.ui_Address + 0x60);
+
+ devpriv->s_BoardInfos.b_BoardVersion = 1;
+
+ // Enable the interrupt for the controler
+ dw_Dummy = inl(devpriv->s_BoardInfos.ui_Address + 0x38);
+ outl(dw_Dummy | 0x2000, devpriv->s_BoardInfos.ui_Address + 0x38);
+
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function's Name : __void__ v_APCI1710_InterruptFunction |
+| (BYTE b_Interrupt, __CPPARGS) |
++----------------------------------------------------------------------------+
+| Task : APCI-1710 interrupt function |
++----------------------------------------------------------------------------+
+| Input Parameters : BYTE b_Interrupt : Interrupt number |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0 : OK |
+| -1 : Error |
++----------------------------------------------------------------------------+
+*/
+
+void v_APCI1710_Interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ BYTE b_ModuleCpt = 0;
+ BYTE b_InterruptFlag = 0;
+ BYTE b_PWMCpt = 0;
+ BYTE b_TorCounterCpt = 0;
+ BYTE b_PulseIncoderCpt = 0;
+ UINT ui_16BitValue;
+ ULONG ul_InterruptLatchReg = 0;
+ ULONG ul_LatchRegisterValue = 0;
+ ULONG ul_82X54InterruptStatus;
+ ULONG ul_StatusRegister;
+
+ str_ModuleInfo *ps_ModuleInfo;
+
+ printk("APCI1710 Interrupt\n");
+ for (b_ModuleCpt = 0; b_ModuleCpt < 4; b_ModuleCpt++, ps_ModuleInfo++) {
+
+ /**************************/
+ /* 1199/0225 to 0100/0226 */
+ /**************************/
+ ps_ModuleInfo = &devpriv->s_ModuleInfo[b_ModuleCpt];
+
+ /***********************/
+ /* Test if 82X54 timer */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModuleCpt] &
+ 0xFFFF0000UL) == APCI1710_82X54_TIMER) {
+
+ //printk("TIMER Interrupt Occurred\n");
+ ul_82X54InterruptStatus = inl(devpriv->s_BoardInfos.
+ ui_Address + 12 + (64 * b_ModuleCpt));
+
+ /***************************/
+ /* Test if interrupt occur */
+ /***************************/
+
+ if ((ul_82X54InterruptStatus & ps_ModuleInfo->
+ s_82X54ModuleInfo.
+ b_InterruptMask) != 0) {
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.
+ ui_Write].
+ ul_OldInterruptMask =
+ (ul_82X54InterruptStatus &
+ ps_ModuleInfo->s_82X54ModuleInfo.
+ b_InterruptMask) << 4;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.
+ ui_Write].
+ b_OldModuleMask = 1 << b_ModuleCpt;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.
+ ui_Write].ul_OldCounterLatchValue = 0;
+
+ devpriv->
+ s_InterruptParameters.
+ ul_InterruptOccur++;
+
+ /****************************/
+ /* Increment the write FIFO */
+ /****************************/
+
+ devpriv->
+ s_InterruptParameters.
+ ui_Write = (devpriv->
+ s_InterruptParameters.
+ ui_Write + 1) % APCI1710_SAVE_INTERRUPT;
+
+ b_InterruptFlag = 1;
+
+ /**********************/
+ /* Call user function */
+ /**********************/
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current, 0);
+
+ } // if ((ul_82X54InterruptStatus & 0x7) != 0)
+ } // 82X54 timer
+
+ /***************************/
+ /* Test if increm. counter */
+ /***************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModuleCpt] &
+ 0xFFFF0000UL) == APCI1710_INCREMENTAL_COUNTER) {
+
+ ul_InterruptLatchReg = inl(devpriv->s_BoardInfos.
+ ui_Address + (64 * b_ModuleCpt));
+
+ /*********************/
+ /* Test if interrupt */
+ /*********************/
+
+ if ((ul_InterruptLatchReg & 0x22) && (ps_ModuleInfo->
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2 & 0x80)) {
+ /************************************/
+ /* Test if strobe latch I interrupt */
+ /************************************/
+
+ if (ul_InterruptLatchReg & 2) {
+ ul_LatchRegisterValue =
+ inl(devpriv->s_BoardInfos.
+ ui_Address + 4 +
+ (64 * b_ModuleCpt));
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].ul_OldInterruptMask =
+ 1UL;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].b_OldModuleMask =
+ 1 << b_ModuleCpt;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].
+ ul_OldCounterLatchValue =
+ ul_LatchRegisterValue;
+
+ devpriv->
+ s_InterruptParameters.
+ ul_InterruptOccur++;
+
+ /****************************/
+ /* 0899/0224 to 1199/0225 */
+ /****************************/
+ /* Increment the write FIFO */
+ /****************************/
+
+ devpriv->
+ s_InterruptParameters.
+ ui_Write = (devpriv->
+ s_InterruptParameters.
+ ui_Write +
+ 1) % APCI1710_SAVE_INTERRUPT;
+
+ b_InterruptFlag = 1;
+
+ /**********************/
+ /* Call user function */
+ /**********************/
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current,
+ 0);
+
+ }
+
+ /*************************************/
+ /* Test if strobe latch II interrupt */
+ /*************************************/
+
+ if (ul_InterruptLatchReg & 0x20) {
+
+ ul_LatchRegisterValue =
+ inl(devpriv->s_BoardInfos.
+ ui_Address + 8 +
+ (64 * b_ModuleCpt));
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].ul_OldInterruptMask =
+ 2UL;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].b_OldModuleMask =
+ 1 << b_ModuleCpt;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].
+ ul_OldCounterLatchValue =
+ ul_LatchRegisterValue;
+
+ devpriv->
+ s_InterruptParameters.
+ ul_InterruptOccur++;
+
+ /****************************/
+ /* 0899/0224 to 1199/0225 */
+ /****************************/
+ /* Increment the write FIFO */
+ /****************************/
+
+ devpriv->
+ s_InterruptParameters.
+ ui_Write = (devpriv->
+ s_InterruptParameters.
+ ui_Write +
+ 1) % APCI1710_SAVE_INTERRUPT;
+
+ b_InterruptFlag = 1;
+
+ /**********************/
+ /* Call user function */
+ /**********************/
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current,
+ 0);
+
+ }
+ }
+
+ ul_InterruptLatchReg = inl(devpriv->s_BoardInfos.
+ ui_Address + 24 + (64 * b_ModuleCpt));
+
+ /***************************/
+ /* Test if index interrupt */
+ /***************************/
+
+ if (ul_InterruptLatchReg & 0x8) {
+ ps_ModuleInfo->
+ s_SiemensCounterInfo.
+ s_InitFlag.b_IndexInterruptOccur = 1;
+
+ if (ps_ModuleInfo->
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister2 &
+ APCI1710_INDEX_AUTO_MODE) {
+
+ outl(ps_ModuleInfo->
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ dw_ModeRegister1_2_3_4,
+ devpriv->s_BoardInfos.
+ ui_Address + 20 +
+ (64 * b_ModuleCpt));
+ }
+
+ /*****************************/
+ /* Test if interrupt enabled */
+ /*****************************/
+
+ if ((ps_ModuleInfo->
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3 &
+ APCI1710_ENABLE_INDEX_INT) ==
+ APCI1710_ENABLE_INDEX_INT) {
+ devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].ul_OldInterruptMask =
+ 4UL;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].b_OldModuleMask =
+ 1 << b_ModuleCpt;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].
+ ul_OldCounterLatchValue =
+ ul_LatchRegisterValue;
+
+ devpriv->
+ s_InterruptParameters.
+ ul_InterruptOccur++;
+
+ /****************************/
+ /* 0899/0224 to 1199/0225 */
+ /****************************/
+ /* Increment the write FIFO */
+ /****************************/
+
+ devpriv->
+ s_InterruptParameters.
+ ui_Write = (devpriv->
+ s_InterruptParameters.
+ ui_Write +
+ 1) % APCI1710_SAVE_INTERRUPT;
+
+ b_InterruptFlag = 1;
+
+ /**********************/
+ /* Call user function */
+ /**********************/
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current,
+ 0);
+
+ }
+ }
+
+ /*****************************/
+ /* Test if compare interrupt */
+ /*****************************/
+
+ if (ul_InterruptLatchReg & 0x10) {
+ /*****************************/
+ /* Test if interrupt enabled */
+ /*****************************/
+
+ if ((ps_ModuleInfo->
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister3 &
+ APCI1710_ENABLE_COMPARE_INT) ==
+ APCI1710_ENABLE_COMPARE_INT) {
+ devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].ul_OldInterruptMask =
+ 8UL;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].b_OldModuleMask =
+ 1 << b_ModuleCpt;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].
+ ul_OldCounterLatchValue =
+ ul_LatchRegisterValue;
+
+ devpriv->
+ s_InterruptParameters.
+ ul_InterruptOccur++;
+
+ /****************************/
+ /* 0899/0224 to 1199/0225 */
+ /****************************/
+ /* Increment the write FIFO */
+ /****************************/
+
+ devpriv->
+ s_InterruptParameters.
+ ui_Write = (devpriv->
+ s_InterruptParameters.
+ ui_Write +
+ 1) % APCI1710_SAVE_INTERRUPT;
+
+ b_InterruptFlag = 1;
+
+ /**********************/
+ /* Call user function */
+ /**********************/
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current,
+ 0);
+
+ }
+ }
+
+ /*******************************************/
+ /* Test if frequency measurement interrupt */
+ /*******************************************/
+
+ if (ul_InterruptLatchReg & 0x20) {
+ /*******************/
+ /* Read the status */
+ /*******************/
+
+ ul_StatusRegister = inl(devpriv->s_BoardInfos.
+ ui_Address + 32 + (64 * b_ModuleCpt));
+
+ /******************/
+ /* Read the value */
+ /******************/
+
+ ul_LatchRegisterValue =
+ inl(devpriv->s_BoardInfos.ui_Address +
+ 28 + (64 * b_ModuleCpt));
+
+ switch ((ul_StatusRegister >> 1) & 3) {
+ case 0:
+ /*************************/
+ /* Test the counter mode */
+ /*************************/
+
+ if ((devpriv->s_ModuleInfo[b_ModuleCpt].
+ s_SiemensCounterInfo.
+ s_ModeRegister.
+ s_ByteModeRegister.
+ b_ModeRegister1 &
+ APCI1710_16BIT_COUNTER)
+ == APCI1710_16BIT_COUNTER) {
+ /****************************************/
+ /* Test if 16-bit counter 1 pulse occur */
+ /****************************************/
+
+ if ((ul_LatchRegisterValue &
+ 0xFFFFU) != 0) {
+ ui_16BitValue =
+ (UINT)
+ ul_LatchRegisterValue
+ & 0xFFFFU;
+ ul_LatchRegisterValue =
+ (ul_LatchRegisterValue
+ & 0xFFFF0000UL)
+ | (0xFFFFU -
+ ui_16BitValue);
+ }
+
+ /****************************************/
+ /* Test if 16-bit counter 2 pulse occur */
+ /****************************************/
+
+ if ((ul_LatchRegisterValue &
+ 0xFFFF0000UL) !=
+ 0) {
+ ui_16BitValue =
+ (UINT) (
+ (ul_LatchRegisterValue
+ >> 16) &
+ 0xFFFFU);
+ ul_LatchRegisterValue =
+ (ul_LatchRegisterValue
+ & 0xFFFFUL) |
+ ((0xFFFFU -
+ ui_16BitValue)
+ << 16);
+ }
+ } else {
+ if (ul_LatchRegisterValue != 0) {
+ ul_LatchRegisterValue =
+ 0xFFFFFFFFUL -
+ ul_LatchRegisterValue;
+ }
+ }
+ break;
+
+ case 1:
+ /****************************************/
+ /* Test if 16-bit counter 2 pulse occur */
+ /****************************************/
+
+ if ((ul_LatchRegisterValue &
+ 0xFFFF0000UL) != 0) {
+ ui_16BitValue =
+ (UINT) (
+ (ul_LatchRegisterValue
+ >> 16) &
+ 0xFFFFU);
+ ul_LatchRegisterValue =
+ (ul_LatchRegisterValue &
+ 0xFFFFUL) | ((0xFFFFU -
+ ui_16BitValue)
+ << 16);
+ }
+ break;
+
+ case 2:
+ /****************************************/
+ /* Test if 16-bit counter 1 pulse occur */
+ /****************************************/
+
+ if ((ul_LatchRegisterValue & 0xFFFFU) !=
+ 0) {
+ ui_16BitValue =
+ (UINT)
+ ul_LatchRegisterValue &
+ 0xFFFFU;
+ ul_LatchRegisterValue =
+ (ul_LatchRegisterValue &
+ 0xFFFF0000UL) | (0xFFFFU
+ - ui_16BitValue);
+ }
+ break;
+ }
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.
+ ui_Write].
+ ul_OldInterruptMask = 0x10000UL;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.
+ ui_Write].
+ b_OldModuleMask = 1 << b_ModuleCpt;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters[devpriv->
+ s_InterruptParameters.
+ ui_Write].
+ ul_OldCounterLatchValue =
+ ul_LatchRegisterValue;
+
+ devpriv->
+ s_InterruptParameters.
+ ul_InterruptOccur++;
+
+ /****************************/
+ /* 0899/0224 to 1199/0225 */
+ /****************************/
+ /* Increment the write FIFO */
+ /****************************/
+
+ devpriv->
+ s_InterruptParameters.
+ ui_Write = (devpriv->
+ s_InterruptParameters.
+ ui_Write + 1) % APCI1710_SAVE_INTERRUPT;
+
+ b_InterruptFlag = 1;
+
+ /**********************/
+ /* Call user function */
+ /**********************/
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current, 0);
+
+ }
+ } // Incremental counter
+
+ /***************/
+ /* Test if CDA */
+ /***************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModuleCpt] &
+ 0xFFFF0000UL) == APCI1710_CDA) {
+ /******************************************/
+ /* Test if CDA enable and functionality 0 */
+ /******************************************/
+
+ if ((devpriv->s_ModuleInfo[b_ModuleCpt].
+ s_CDAModuleInfo.
+ b_CDAEnable == APCI1710_ENABLE)
+ && (devpriv->s_ModuleInfo[b_ModuleCpt].
+ s_CDAModuleInfo.b_FctSelection == 0)) {
+ /****************************/
+ /* Get the interrupt status */
+ /****************************/
+
+ ul_StatusRegister = inl(devpriv->s_BoardInfos.
+ ui_Address + 16 + (64 * b_ModuleCpt));
+ /***************************/
+ /* Test if interrupt occur */
+ /***************************/
+
+ if (ul_StatusRegister & 1) {
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].ul_OldInterruptMask =
+ 0x80000UL;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].b_OldModuleMask =
+ 1 << b_ModuleCpt;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].
+ ul_OldCounterLatchValue = 0;
+
+ devpriv->
+ s_InterruptParameters.
+ ul_InterruptOccur++;
+
+ /****************************/
+ /* Increment the write FIFO */
+ /****************************/
+
+ devpriv->
+ s_InterruptParameters.
+ ui_Write = (devpriv->
+ s_InterruptParameters.
+ ui_Write +
+ 1) % APCI1710_SAVE_INTERRUPT;
+
+ b_InterruptFlag = 1;
+
+ /**********************/
+ /* Call user function */
+ /**********************/
+
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current,
+ 0);
+
+ } // if (ul_StatusRegister & 1)
+
+ }
+ } // CDA
+
+ /***********************/
+ /* Test if PWM counter */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModuleCpt] &
+ 0xFFFF0000UL) == APCI1710_PWM) {
+ for (b_PWMCpt = 0; b_PWMCpt < 2; b_PWMCpt++) {
+ /*************************************/
+ /* Test if PWM interrupt initialised */
+ /*************************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModuleCpt].
+ s_PWMModuleInfo.
+ s_PWMInfo[b_PWMCpt].
+ b_InterruptEnable == APCI1710_ENABLE) {
+ /*****************************/
+ /* Read the interrupt status */
+ /*****************************/
+
+ ul_StatusRegister =
+ inl(devpriv->s_BoardInfos.
+ ui_Address + 16 +
+ (20 * b_PWMCpt) +
+ (64 * b_ModuleCpt));
+
+ /***************************/
+ /* Test if interrupt occur */
+ /***************************/
+
+ if (ul_StatusRegister & 0x1) {
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->
+ s_InterruptParameters.
+ ui_Write].
+ ul_OldInterruptMask =
+ 0x4000UL << b_PWMCpt;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->
+ s_InterruptParameters.
+ ui_Write].
+ b_OldModuleMask =
+ 1 << b_ModuleCpt;
+
+ devpriv->
+ s_InterruptParameters.
+ ul_InterruptOccur++;
+
+ /****************************/
+ /* Increment the write FIFO */
+ /****************************/
+
+ devpriv->
+ s_InterruptParameters.
+ ui_Write = (devpriv->
+ s_InterruptParameters.
+ ui_Write +
+ 1) %
+ APCI1710_SAVE_INTERRUPT;
+
+ b_InterruptFlag = 1;
+
+ /**********************/
+ /* Call user function */
+ /**********************/
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO,
+ devpriv->tsk_Current,
+ 0);
+
+ } // if (ul_StatusRegister & 0x1)
+ } // if (APCI1710_ENABLE)
+ } // for (b_PWMCpt == 0; b_PWMCpt < 0; b_PWMCpt ++)
+ } // PWM counter
+
+ /***********************/
+ /* Test if tor counter */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModuleCpt] &
+ 0xFFFF0000UL) == APCI1710_TOR_COUNTER) {
+ for (b_TorCounterCpt = 0; b_TorCounterCpt < 2;
+ b_TorCounterCpt++) {
+ /*************************************/
+ /* Test if tor interrupt initialised */
+ /*************************************/
+
+ if (devpriv->
+ s_ModuleInfo[b_ModuleCpt].
+ s_TorCounterModuleInfo.
+ s_TorCounterInfo[b_TorCounterCpt].
+ b_InterruptEnable == APCI1710_ENABLE) {
+ /*****************************/
+ /* Read the interrupt status */
+ /*****************************/
+
+ ul_StatusRegister =
+ inl(devpriv->s_BoardInfos.
+ ui_Address + 12 +
+ (16 * b_TorCounterCpt) +
+ (64 * b_ModuleCpt));
+
+ /***************************/
+ /* Test if interrupt occur */
+ /***************************/
+
+ if (ul_StatusRegister & 0x1) {
+ /******************************/
+ /* Read the tor counter value */
+ /******************************/
+
+ ul_LatchRegisterValue =
+ inl(devpriv->
+ s_BoardInfos.
+ ui_Address + 0 +
+ (16 * b_TorCounterCpt) +
+ (64 * b_ModuleCpt));
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->
+ s_InterruptParameters.
+ ui_Write].
+ ul_OldInterruptMask =
+ 0x1000UL <<
+ b_TorCounterCpt;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->
+ s_InterruptParameters.
+ ui_Write].
+ b_OldModuleMask =
+ 1 << b_ModuleCpt;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->
+ s_InterruptParameters.
+ ui_Write].
+ ul_OldCounterLatchValue
+ = ul_LatchRegisterValue;
+
+ devpriv->
+ s_InterruptParameters.
+ ul_InterruptOccur++;
+
+ /****************************/
+ /* Increment the write FIFO */
+ /****************************/
+
+ devpriv->
+ s_InterruptParameters.
+ ui_Write = (devpriv->
+ s_InterruptParameters.
+ ui_Write +
+ 1) %
+ APCI1710_SAVE_INTERRUPT;
+
+ b_InterruptFlag = 1;
+
+ /**********************/
+ /* Call user function */
+ /**********************/
+
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO,
+ devpriv->tsk_Current,
+ 0);
+ } // if (ul_StatusRegister & 0x1)
+ } // if (APCI1710_ENABLE)
+ } // for (b_TorCounterCpt == 0; b_TorCounterCpt < 0; b_TorCounterCpt ++)
+ } // Tor counter
+
+ /***********************/
+ /* Test if chronometer */
+ /***********************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModuleCpt] &
+ 0xFFFF0000UL) == APCI1710_CHRONOMETER) {
+
+ //printk("APCI1710 Chrono Interrupt\n");
+ /*****************************/
+ /* Read the interrupt status */
+ /*****************************/
+
+ ul_InterruptLatchReg = inl(devpriv->s_BoardInfos.
+ ui_Address + 12 + (64 * b_ModuleCpt));
+
+ /***************************/
+ /* Test if interrupt occur */
+ /***************************/
+
+ if ((ul_InterruptLatchReg & 0x8) == 0x8) {
+ /****************************/
+ /* Clear the interrupt flag */
+ /****************************/
+
+ outl(0, devpriv->s_BoardInfos.
+ ui_Address + 32 + (64 * b_ModuleCpt));
+
+ /***************************/
+ /* Test if continuous mode */
+ /***************************/
+
+ if (ps_ModuleInfo->
+ s_ChronoModuleInfo.
+ b_CycleMode == APCI1710_ENABLE) {
+ /********************/
+ /* Clear the status */
+ /********************/
+
+ outl(0, devpriv->s_BoardInfos.
+ ui_Address + 36 +
+ (64 * b_ModuleCpt));
+ }
+
+ /*************************/
+ /* Read the timing value */
+ /*************************/
+
+ ul_LatchRegisterValue =
+ inl(devpriv->s_BoardInfos.ui_Address +
+ 4 + (64 * b_ModuleCpt));
+
+ /*****************************/
+ /* Test if interrupt enabled */
+ /*****************************/
+
+ if (ps_ModuleInfo->
+ s_ChronoModuleInfo.b_InterruptMask) {
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].ul_OldInterruptMask =
+ 0x80;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].b_OldModuleMask =
+ 1 << b_ModuleCpt;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->s_InterruptParameters.
+ ui_Write].
+ ul_OldCounterLatchValue =
+ ul_LatchRegisterValue;
+
+ devpriv->
+ s_InterruptParameters.
+ ul_InterruptOccur++;
+
+ /****************************/
+ /* Increment the write FIFO */
+ /****************************/
+
+ devpriv->
+ s_InterruptParameters.
+ ui_Write = (devpriv->
+ s_InterruptParameters.
+ ui_Write +
+ 1) % APCI1710_SAVE_INTERRUPT;
+
+ b_InterruptFlag = 1;
+
+ /**********************/
+ /* Call user function */
+ /**********************/
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current,
+ 0);
+
+ }
+ }
+ } // Chronometer
+
+ /*************************/
+ /* Test if pulse encoder */
+ /*************************/
+
+ if ((devpriv->s_BoardInfos.
+ dw_MolduleConfiguration[b_ModuleCpt] &
+ 0xFFFF0000UL) == APCI1710_PULSE_ENCODER) {
+ /****************************/
+ /* Read the status register */
+ /****************************/
+
+ ul_StatusRegister = inl(devpriv->s_BoardInfos.
+ ui_Address + 20 + (64 * b_ModuleCpt));
+
+ if (ul_StatusRegister & 0xF) {
+ for (b_PulseIncoderCpt = 0;
+ b_PulseIncoderCpt < 4;
+ b_PulseIncoderCpt++) {
+ /*************************************/
+ /* Test if pulse encoder initialised */
+ /*************************************/
+
+ if ((ps_ModuleInfo->
+ s_PulseEncoderModuleInfo.
+ s_PulseEncoderInfo
+ [b_PulseIncoderCpt].
+ b_PulseEncoderInit == 1)
+ && (((ps_ModuleInfo->s_PulseEncoderModuleInfo.dw_SetRegister >> b_PulseIncoderCpt) & 1) == 1) && (((ul_StatusRegister >> (b_PulseIncoderCpt)) & 1) == 1)) {
+ devpriv->s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->
+ s_InterruptParameters.
+ ui_Write].
+ ul_OldInterruptMask =
+ 0x100UL <<
+ b_PulseIncoderCpt;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->
+ s_InterruptParameters.
+ ui_Write].
+ b_OldModuleMask =
+ 1 << b_ModuleCpt;
+
+ devpriv->
+ s_InterruptParameters.
+ s_FIFOInterruptParameters
+ [devpriv->
+ s_InterruptParameters.
+ ui_Write].
+ ul_OldCounterLatchValue
+ = ul_LatchRegisterValue;
+
+ devpriv->
+ s_InterruptParameters.
+ ul_InterruptOccur++;
+
+ /****************************/
+ /* 0899/0224 to 1199/0225 */
+ /****************************/
+ /* Increment the write FIFO */
+ /****************************/
+
+ devpriv->
+ s_InterruptParameters.
+ ui_Write = (devpriv->
+ s_InterruptParameters.
+ ui_Write +
+ 1) %
+ APCI1710_SAVE_INTERRUPT;
+
+ b_InterruptFlag = 1;
+
+ /**********************/
+ /* Call user function */
+ /**********************/
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO,
+ devpriv->tsk_Current,
+ 0);
+
+ }
+ }
+ }
+ } //pulse encoder
+
+ }
+ return;
+
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_APCI1710.h b/drivers/staging/comedi/drivers/addi-data/hwdrv_APCI1710.h
new file mode 100644
index 000000000000..998cbba8afeb
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_APCI1710.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+#define COMEDI_SUBD_TTLIO 11 /* Digital Input Output But TTL */
+#define COMEDI_SUBD_PWM 12 /* Pulse width Measurement */
+#define COMEDI_SUBD_SSI 13 /* Synchronous serial interface */
+#define COMEDI_SUBD_TOR 14 /* Tor counter */
+#define COMEDI_SUBD_CHRONO 15 /* Chrono meter */
+#define COMEDI_SUBD_PULSEENCODER 16 /* Pulse Encoder INP CPT */
+#define COMEDI_SUBD_INCREMENTALCOUNTER 17 /* Incremental Counter */
+
+#define APCI1710_BOARD_NAME "apci1710"
+#define APCI1710_BOARD_VENDOR_ID 0x10E8
+#define APCI1710_BOARD_DEVICE_ID 0x818F
+#define APCI1710_ADDRESS_RANGE 256
+#define APCI1710_CONFIG_ADDRESS_RANGE 8
+#define APCI1710_INCREMENTAL_COUNTER 0x53430000UL
+#define APCI1710_SSI_COUNTER 0x53490000UL
+#define APCI1710_TTL_IO 0x544C0000UL
+#define APCI1710_DIGITAL_IO 0x44490000UL
+#define APCI1710_82X54_TIMER 0x49430000UL
+#define APCI1710_CHRONOMETER 0x43480000UL
+#define APCI1710_PULSE_ENCODER 0x495A0000UL
+#define APCI1710_TOR_COUNTER 0x544F0000UL
+#define APCI1710_PWM 0x50570000UL
+#define APCI1710_ETM 0x45540000UL
+#define APCI1710_CDA 0x43440000UL
+#define APCI1710_DISABLE 0
+#define APCI1710_ENABLE 1
+#define APCI1710_SYNCHRONOUS_MODE 1
+#define APCI1710_ASYNCHRONOUS_MODE 0
+
+//MODULE INFO STRUCTURE
+
+static const struct comedi_lrange range_apci1710_ttl = { 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1)
+ }
+};
+
+static const struct comedi_lrange range_apci1710_ssi = { 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1)
+ }
+};
+
+static const struct comedi_lrange range_apci1710_inccpt = { 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1)
+ }
+};
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci035.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci035.c
new file mode 100644
index 000000000000..e4527926c2df
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci035.c
@@ -0,0 +1,600 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-------------------------------+---------------------------------------+
+ | Project : APCI-035 | Compiler : GCC |
+ | Module name : hwdrv_apci035.c | Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-------------------------------+---------------------------------------+
+ | Description : Hardware Layer Acces For APCI-035 |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +----------+-----------+------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | | | |
+ | | | |
+ | | | |
+ +----------+-----------+------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+#include "hwdrv_apci035.h"
+INT i_WatchdogNbr = 0;
+INT i_Temp = 0;
+INT i_Flag = 1;
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI035_ConfigTimerWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Configures The Timer , Counter or Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| data[0] : 0 Configure As Timer |
+| 1 Configure As Watchdog |
+ data[1] : Watchdog number
+| data[2] : Time base Unit |
+| data[3] : Reload Value |
+ data[4] : External Trigger |
+ 1:Enable
+ 0:Disable
+ data[5] :External Trigger Level
+ 00 Trigger Disabled
+ 01 Trigger Enabled (Low level)
+ 10 Trigger Enabled (High Level)
+ 11 Trigger Enabled (High/Low level)
+ data[6] : External Gate |
+ 1:Enable
+ 0:Disable
+ data[7] : External Gate level
+ 00 Gate Disabled
+ 01 Gate Enabled (Low level)
+ 10 Gate Enabled (High Level)
+ data[8] :Warning Relay
+ 1: ENABLE
+ 0: DISABLE
+ data[9] :Warning Delay available
+ data[10] :Warning Relay Time unit
+ data[11] :Warning Relay Time Reload value
+ data[12] :Reset Relay
+ 1 : ENABLE
+ 0 : DISABLE
+ data[13] :Interrupt
+ 1 : ENABLE
+ 0 : DISABLE
+
+|
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI035_ConfigTimerWatchdog(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Status = 0;
+ UINT ui_Command = 0;
+ UINT ui_Mode = 0;
+ i_Temp = 0;
+ devpriv->tsk_Current = current;
+ devpriv->b_TimerSelectMode = data[0];
+ i_WatchdogNbr = data[1];
+ if (data[0] == 0) {
+ ui_Mode = 2;
+ } else {
+ ui_Mode = 0;
+ }
+//ui_Command = inl(devpriv->iobase+((i_WatchdogNbr-1)*32)+12);
+ ui_Command = 0;
+//ui_Command = ui_Command & 0xFFFFF9FEUL;
+ outl(ui_Command, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+ ui_Command = 0;
+ ui_Command = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+/************************/
+/* Set the reload value */
+/************************/
+ outl(data[3], devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 4);
+/*********************/
+/* Set the time unit */
+/*********************/
+ outl(data[2], devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 8);
+ if (data[0] == ADDIDATA_TIMER) {
+
+ /******************************/
+ /* Set the mode : */
+ /* - Disable the hardware */
+ /* - Disable the counter mode */
+ /* - Disable the warning */
+ /* - Disable the reset */
+ /* - Enable the timer mode */
+ /* - Set the timer mode */
+ /******************************/
+
+ ui_Command =
+ (ui_Command & 0xFFF719E2UL) | ui_Mode << 13UL | 0x10UL;
+
+ } //if (data[0] == ADDIDATA_TIMER)
+ else {
+ if (data[0] == ADDIDATA_WATCHDOG) {
+
+ /******************************/
+ /* Set the mode : */
+ /* - Disable the hardware */
+ /* - Disable the counter mode */
+ /* - Disable the warning */
+ /* - Disable the reset */
+ /* - Disable the timer mode */
+ /******************************/
+
+ ui_Command = ui_Command & 0xFFF819E2UL;
+
+ } else {
+ printk("\n The parameter for Timer/watchdog selection is in error\n");
+ return -EINVAL;
+ }
+ }
+ outl(ui_Command, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+ ui_Command = 0;
+ ui_Command = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+/********************************/
+/* Disable the hardware trigger */
+/********************************/
+ ui_Command = ui_Command & 0xFFFFF89FUL;
+ if (data[4] == ADDIDATA_ENABLE) {
+ /**********************************/
+ /* Set the hardware trigger level */
+ /**********************************/
+ ui_Command = ui_Command | (data[5] << 5);
+ }
+ outl(ui_Command, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+ ui_Command = 0;
+ ui_Command = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+/*****************************/
+/* Disable the hardware gate */
+/*****************************/
+ ui_Command = ui_Command & 0xFFFFF87FUL;
+ if (data[6] == ADDIDATA_ENABLE) {
+/*******************************/
+/* Set the hardware gate level */
+/*******************************/
+ ui_Command = ui_Command | (data[7] << 7);
+ }
+ outl(ui_Command, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+ ui_Command = 0;
+ ui_Command = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+/*******************************/
+/* Disable the hardware output */
+/*******************************/
+ ui_Command = ui_Command & 0xFFFFF9FBUL;
+/*********************************/
+/* Set the hardware output level */
+/*********************************/
+ ui_Command = ui_Command | (data[8] << 2);
+ outl(ui_Command, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+ if (data[9] == ADDIDATA_ENABLE) {
+ /************************/
+ /* Set the reload value */
+ /************************/
+ outl(data[11],
+ devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 24);
+ /**********************/
+ /* Set the time unite */
+ /**********************/
+ outl(data[10],
+ devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 28);
+ }
+
+ ui_Command = 0;
+ ui_Command = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+ /*******************************/
+ /* Disable the hardware output */
+ /*******************************/
+ ui_Command = ui_Command & 0xFFFFF9F7UL;
+ /*********************************/
+ /* Set the hardware output level */
+ /*********************************/
+ ui_Command = ui_Command | (data[12] << 3);
+ outl(ui_Command, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+ /*************************************/
+ /** Enable the watchdog interrupt **/
+ /*************************************/
+ ui_Command = 0;
+ ui_Command = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+/*******************************/
+/* Set the interrupt selection */
+/*******************************/
+ ui_Status = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 16);
+
+ ui_Command = (ui_Command & 0xFFFFF9FDUL) | (data[13] << 1);
+ outl(ui_Command, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI035_StartStopWriteTimerWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Start / Stop The Selected Timer , or Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| data[0] : 0 - Stop Selected Timer/Watchdog |
+| 1 - Start Selected Timer/Watchdog |
+| 2 - Trigger Selected Timer/Watchdog |
+| 3 - Stop All Timer/Watchdog |
+| 4 - Start All Timer/Watchdog |
+| 5 - Trigger All Timer/Watchdog |
+| |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI035_StartStopWriteTimerWatchdog(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Command = 0;
+ INT i_Count = 0;
+ if (data[0] == 1) {
+ ui_Command =
+ inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+ /**********************/
+ /* Start the hardware */
+ /**********************/
+ ui_Command = (ui_Command & 0xFFFFF9FFUL) | 0x1UL;
+ outl(ui_Command,
+ devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+ } // if (data[0]==1)
+ if (data[0] == 2) {
+ ui_Command =
+ inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+ /***************************/
+ /* Set the trigger command */
+ /***************************/
+ ui_Command = (ui_Command & 0xFFFFF9FFUL) | 0x200UL;
+ outl(ui_Command,
+ devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+ }
+
+ if (data[0] == 0) //Stop The Watchdog
+ {
+ //Stop The Watchdog
+ ui_Command = 0;
+ //ui_Command = inl(devpriv->iobase+((i_WatchdogNbr-1)*32)+12);
+ //ui_Command = ui_Command & 0xFFFFF9FEUL;
+ outl(ui_Command,
+ devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
+ } // if (data[1]==0)
+ if (data[0] == 3) //stop all Watchdogs
+ {
+ ui_Command = 0;
+ for (i_Count = 1; i_Count <= 4; i_Count++) {
+ if (devpriv->b_TimerSelectMode == ADDIDATA_WATCHDOG) {
+ ui_Command = 0x2UL;
+ } else {
+ ui_Command = 0x10UL;
+ }
+ i_WatchdogNbr = i_Count;
+ outl(ui_Command,
+ devpriv->iobase + ((i_WatchdogNbr - 1) * 32) +
+ 0);
+ }
+
+ }
+ if (data[0] == 4) //start all Watchdogs
+ {
+ ui_Command = 0;
+ for (i_Count = 1; i_Count <= 4; i_Count++) {
+ if (devpriv->b_TimerSelectMode == ADDIDATA_WATCHDOG) {
+ ui_Command = 0x1UL;
+ } else {
+ ui_Command = 0x8UL;
+ }
+ i_WatchdogNbr = i_Count;
+ outl(ui_Command,
+ devpriv->iobase + ((i_WatchdogNbr - 1) * 32) +
+ 0);
+ }
+ }
+ if (data[0] == 5) //trigger all Watchdogs
+ {
+ ui_Command = 0;
+ for (i_Count = 1; i_Count <= 4; i_Count++) {
+ if (devpriv->b_TimerSelectMode == ADDIDATA_WATCHDOG) {
+ ui_Command = 0x4UL;
+ } else {
+ ui_Command = 0x20UL;
+ }
+
+ i_WatchdogNbr = i_Count;
+ outl(ui_Command,
+ devpriv->iobase + ((i_WatchdogNbr - 1) * 32) +
+ 0);
+ }
+ i_Temp = 1;
+ }
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI035_ReadTimerWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read The Selected Timer , Counter or Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| |
++----------------------------------------------------------------------------+
+| Output Parameters : data[0] : software trigger status
+ data[1] : hardware trigger status
+| data[2] : Software clear status
+ data[3] : Overflow status
+ data[4] : Timer actual value
+
+
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI035_ReadTimerWatchdog(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Status = 0; // Status register
+ i_WatchdogNbr = insn->unused[0];
+ /******************/
+ /* Get the status */
+ /******************/
+ ui_Status = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 16);
+ /***********************************/
+ /* Get the software trigger status */
+ /***********************************/
+ data[0] = ((ui_Status >> 1) & 1);
+ /***********************************/
+ /* Get the hardware trigger status */
+ /***********************************/
+ data[1] = ((ui_Status >> 2) & 1);
+ /*********************************/
+ /* Get the software clear status */
+ /*********************************/
+ data[2] = ((ui_Status >> 3) & 1);
+ /***************************/
+ /* Get the overflow status */
+ /***************************/
+ data[3] = ((ui_Status >> 0) & 1);
+ if (devpriv->b_TimerSelectMode == ADDIDATA_TIMER) {
+ data[4] = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 0);
+
+ } // if (devpriv->b_TimerSelectMode==ADDIDATA_TIMER)
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI035_ConfigAnalogInput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Configures The Analog Input Subdevice |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s : Subdevice Pointer |
+| struct comedi_insn *insn : Insn Structure Pointer |
+| unsigned int *data : Data Pointer contains |
+| configuration parameters as below |
+| data[0] : Warning delay value
+| |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI035_ConfigAnalogInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ devpriv->tsk_Current = current;
+ outl(0x200 | 0, devpriv->iobase + 128 + 0x4);
+ outl(0, devpriv->iobase + 128 + 0);
+/********************************/
+/* Initialise the warning value */
+/********************************/
+ outl(0x300 | 0, devpriv->iobase + 128 + 0x4);
+ outl((data[0] << 8), devpriv->iobase + 128 + 0);
+ outl(0x200000UL, devpriv->iobase + 128 + 12);
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI035_ReadAnalogInput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read value of the selected channel |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_NoOfChannels : No Of Channels To read |
+| UINT *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
+| data[0] : Digital Value Of Input |
+| |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI035_ReadAnalogInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_CommandRegister = 0;
+/******************/
+/* Set the start */
+/******************/
+ ui_CommandRegister = 0x80000;
+ /******************************/
+ /* Write the command register */
+ /******************************/
+ outl(ui_CommandRegister, devpriv->iobase + 128 + 8);
+
+/***************************************/
+/* Read the digital value of the input */
+/***************************************/
+ data[0] = inl(devpriv->iobase + 128 + 28);
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI035_Reset(struct comedi_device *dev) |
+| |
++----------------------------------------------------------------------------+
+| Task :Resets the registers of the card |
++----------------------------------------------------------------------------+
+| Input Parameters : |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI035_Reset(struct comedi_device * dev)
+{
+ INT i_Count = 0;
+ for (i_Count = 1; i_Count <= 4; i_Count++) {
+ i_WatchdogNbr = i_Count;
+ outl(0x0, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 0); //stop all timers
+ }
+ outl(0x0, devpriv->iobase + 128 + 12); //Disable the warning delay
+
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : static void v_APCI035_Interrupt |
+| (int irq , void *d) |
++----------------------------------------------------------------------------+
+| Task : Interrupt processing Routine |
++----------------------------------------------------------------------------+
+| Input Parameters : int irq : irq number |
+| void *d : void pointer |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+static void v_APCI035_Interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ UINT ui_StatusRegister1 = 0;
+ UINT ui_StatusRegister2 = 0;
+ UINT ui_ReadCommand = 0;
+ UINT ui_ChannelNumber = 0;
+ UINT ui_DigitalTemperature = 0;
+ if (i_Temp == 1) {
+ i_WatchdogNbr = i_Flag;
+ i_Flag = i_Flag + 1;
+ }
+ /**************************************/
+ /* Read the interrupt status register of temperature Warning */
+ /**************************************/
+ ui_StatusRegister1 = inl(devpriv->iobase + 128 + 16);
+ /**************************************/
+ /* Read the interrupt status register for Watchdog/timer */
+ /**************************************/
+
+ ui_StatusRegister2 =
+ inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 20);
+
+ if ((((ui_StatusRegister1) & 0x8) == 0x8)) //Test if warning relay interrupt
+ {
+ /**********************************/
+ /* Disable the temperature warning */
+ /**********************************/
+ ui_ReadCommand = inl(devpriv->iobase + 128 + 12);
+ ui_ReadCommand = ui_ReadCommand & 0xFFDF0000UL;
+ outl(ui_ReadCommand, devpriv->iobase + 128 + 12);
+ /***************************/
+ /* Read the channel number */
+ /***************************/
+ ui_ChannelNumber = inl(devpriv->iobase + 128 + 60);
+ /**************************************/
+ /* Read the digital temperature value */
+ /**************************************/
+ ui_DigitalTemperature = inl(devpriv->iobase + 128 + 60);
+ send_sig(SIGIO, devpriv->tsk_Current, 0); // send signal to the sample
+ } //if (((ui_StatusRegister1 & 0x8) == 0x8))
+
+ else {
+ if ((ui_StatusRegister2 & 0x1) == 0x1) {
+ send_sig(SIGIO, devpriv->tsk_Current, 0); // send signal to the sample
+ }
+ } //else if (((ui_StatusRegister1 & 0x8) == 0x8))
+
+ return;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci035.h b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci035.h
new file mode 100644
index 000000000000..80d7c0d39aab
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci035.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+/* Card Specific information */
+#define APCI035_BOARD_VENDOR_ID 0x15B8
+#define APCI035_ADDRESS_RANGE 255
+
+INT i_TW_Number;
+struct {
+ INT i_Gain;
+ INT i_Polarity;
+ INT i_OffsetRange;
+ INT i_Coupling;
+ INT i_SingleDiff;
+ INT i_AutoCalibration;
+ UINT ui_ReloadValue;
+ UINT ui_TimeUnitReloadVal;
+ INT i_Interrupt;
+ INT i_ModuleSelection;
+} Config_Parameters_Main;
+
+/* ANALOG INPUT RANGE */
+struct comedi_lrange range_apci035_ai = { 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1)
+ }
+};
+
+/* Timer / Watchdog Related Defines */
+#define APCI035_TCW_SYNC_ENABLEDISABLE 0
+#define APCI035_TCW_RELOAD_VALUE 4
+#define APCI035_TCW_TIMEBASE 8
+#define APCI035_TCW_PROG 12
+#define APCI035_TCW_TRIG_STATUS 16
+#define APCI035_TCW_IRQ 20
+#define APCI035_TCW_WARN_TIMEVAL 24
+#define APCI035_TCW_WARN_TIMEBASE 28
+
+#define ADDIDATA_TIMER 0
+/* #define ADDIDATA_WATCHDOG 1 */
+
+#define APCI035_TW1 0
+#define APCI035_TW2 32
+#define APCI035_TW3 64
+#define APCI035_TW4 96
+
+#define APCI035_AI_OFFSET 0
+#define APCI035_TEMP 128
+#define APCI035_ALR_SEQ 4
+#define APCI035_START_STOP_INDEX 8
+#define APCI035_ALR_START_STOP 12
+#define APCI035_ALR_IRQ 16
+#define APCI035_EOS 20
+#define APCI035_CHAN_NO 24
+#define APCI035_CHAN_VAL 28
+#define APCI035_CONV_TIME_TIME_BASE 36
+#define APCI035_RELOAD_CONV_TIME_VAL 32
+#define APCI035_DELAY_TIME_TIME_BASE 44
+#define APCI035_RELOAD_DELAY_TIME_VAL 40
+#define ENABLE_EXT_TRIG 1
+#define ENABLE_EXT_GATE 2
+#define ENABLE_EXT_TRIG_GATE 3
+
+#define ANALOG_INPUT 0
+#define TEMPERATURE 1
+#define RESISTANCE 2
+
+#define ADDIDATA_GREATER_THAN_TEST 0
+#define ADDIDATA_LESS_THAN_TEST 1
+
+#define APCI035_MAXVOLT 2.5
+
+#define ADDIDATA_UNIPOLAR 1
+#define ADDIDATA_BIPOLAR 2
+
+/* ADDIDATA Enable Disable */
+#define ADDIDATA_ENABLE 1
+#define ADDIDATA_DISABLE 0
+
+/* Hardware Layer functions for Apci035 */
+
+/* TIMER */
+/* timer value is passed as u seconds */
+INT i_APCI035_ConfigTimerWatchdog(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI035_StartStopWriteTimerWatchdog(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI035_ReadTimerWatchdog(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+/* Temperature Related Defines (Analog Input Subdevice) */
+
+INT i_APCI035_ConfigAnalogInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI035_ReadAnalogInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+/* Interrupt */
+static void v_APCI035_Interrupt(int irq, void *d);
+
+/* Reset functions */
+INT i_APCI035_Reset(struct comedi_device *dev);
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1032.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1032.c
new file mode 100644
index 000000000000..32796ceb946e
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1032.c
@@ -0,0 +1,285 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-------------------------------+---------------------------------------+
+ | Project : APCI-1032 | Compiler : GCC |
+ | Module name : hwdrv_apci1032.c| Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-------------------------------+---------------------------------------+
+ | Description : Hardware Layer Acces For APCI-1032 |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +----------+-----------+------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | | | |
+ | | | |
+ | | | |
+ +----------+-----------+------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+#include "hwdrv_apci1032.h"
+#include <linux/delay.h>
+//Global variables
+UINT ui_InterruptStatus = 0;
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1032_ConfigDigitalInput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Configures the digital input Subdevice |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| unsigned int *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| data[0] : 1 Enable Digital Input Interrupt |
+| 0 Disable Digital Input Interrupt |
+| data[1] : 0 ADDIDATA Interrupt OR LOGIC |
+| : 1 ADDIDATA Interrupt AND LOGIC |
+| data[2] : Interrupt mask for the mode 1 |
+| data[3] : Interrupt mask for the mode 2 |
+| |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1032_ConfigDigitalInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_TmpValue;
+
+ ULONG ul_Command1 = 0;
+ ULONG ul_Command2 = 0;
+ devpriv->tsk_Current = current;
+
+ /*******************************/
+ /* Set the digital input logic */
+ /*******************************/
+ if (data[0] == ADDIDATA_ENABLE) {
+ ul_Command1 = ul_Command1 | data[2];
+ ul_Command2 = ul_Command2 | data[3];
+ outl(ul_Command1,
+ devpriv->iobase + APCI1032_DIGITAL_IP_INTERRUPT_MODE1);
+ outl(ul_Command2,
+ devpriv->iobase + APCI1032_DIGITAL_IP_INTERRUPT_MODE2);
+ if (data[1] == ADDIDATA_OR) {
+ outl(0x4, devpriv->iobase + APCI1032_DIGITAL_IP_IRQ);
+ ui_TmpValue =
+ inl(devpriv->iobase + APCI1032_DIGITAL_IP_IRQ);
+ } //if (data[1] == ADDIDATA_OR)
+ else {
+ outl(0x6, devpriv->iobase + APCI1032_DIGITAL_IP_IRQ);
+ } //else if(data[1] == ADDIDATA_OR)
+ } // if( data[0] == ADDIDATA_ENABLE)
+ else {
+ ul_Command1 = ul_Command1 & 0xFFFF0000;
+ ul_Command2 = ul_Command2 & 0xFFFF0000;
+ outl(ul_Command1,
+ devpriv->iobase + APCI1032_DIGITAL_IP_INTERRUPT_MODE1);
+ outl(ul_Command2,
+ devpriv->iobase + APCI1032_DIGITAL_IP_INTERRUPT_MODE2);
+ outl(0x0, devpriv->iobase + APCI1032_DIGITAL_IP_IRQ);
+ } //else if ( data[0] == ADDIDATA_ENABLE)
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1032_Read1DigitalInput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Return the status of the digital input |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_Channel : Channel number to read |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI1032_Read1DigitalInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_TmpValue = 0;
+ UINT ui_Channel;
+ ui_Channel = CR_CHAN(insn->chanspec);
+ if (ui_Channel >= 0 && ui_Channel <= 31) {
+ ui_TmpValue = (UINT) inl(devpriv->iobase + APCI1032_DIGITAL_IP);
+ // since only 1 channel reqd to bring it to last bit it is rotated
+ // 8 +(chan - 1) times then ANDed with 1 for last bit.
+ *data = (ui_TmpValue >> ui_Channel) & 0x1;
+ } //if(ui_Channel >= 0 && ui_Channel <=31)
+ else {
+ //comedi_error(dev," \n chan spec wrong\n");
+ return -EINVAL; // "sorry channel spec wrong "
+ } //else if(ui_Channel >= 0 && ui_Channel <=31)
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1032_ReadMoreDigitalInput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Return the status of the Requested digital inputs |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_NoOfChannels : No Of Channels To be Read |
+| UINT *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1032_ReadMoreDigitalInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_PortValue = data[0];
+ UINT ui_Mask = 0;
+ UINT ui_NoOfChannels;
+
+ ui_NoOfChannels = CR_CHAN(insn->chanspec);
+ if (data[1] == 0) {
+ *data = (UINT) inl(devpriv->iobase + APCI1032_DIGITAL_IP);
+ switch (ui_NoOfChannels) {
+ case 2:
+ ui_Mask = 3;
+ *data = (*data >> (2 * ui_PortValue)) & ui_Mask;
+ break;
+ case 4:
+ ui_Mask = 15;
+ *data = (*data >> (4 * ui_PortValue)) & ui_Mask;
+ break;
+ case 8:
+ ui_Mask = 255;
+ *data = (*data >> (8 * ui_PortValue)) & ui_Mask;
+ break;
+ case 16:
+ ui_Mask = 65535;
+ *data = (*data >> (16 * ui_PortValue)) & ui_Mask;
+ break;
+ case 31:
+ break;
+ default:
+ //comedi_error(dev," \nchan spec wrong\n");
+ return -EINVAL; // "sorry channel spec wrong "
+ break;
+ } //switch(ui_NoOfChannels)
+ } //if(data[1]==0)
+ else {
+ if (data[1] == 1) {
+ *data = ui_InterruptStatus;
+ } //if(data[1]==1)
+ } //else if(data[1]==0)
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : static void v_APCI1032_Interrupt |
+| (int irq , void *d) |
++----------------------------------------------------------------------------+
+| Task : Interrupt handler for the interruptible digital inputs |
++----------------------------------------------------------------------------+
+| Input Parameters : int irq : irq number |
+| void *d : void pointer |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+static void v_APCI1032_Interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+
+ UINT ui_Temp;
+ //disable the interrupt
+ ui_Temp = inl(devpriv->iobase + APCI1032_DIGITAL_IP_IRQ);
+ outl(ui_Temp & APCI1032_DIGITAL_IP_INTERRUPT_DISABLE,
+ devpriv->iobase + APCI1032_DIGITAL_IP_IRQ);
+ ui_InterruptStatus =
+ inl(devpriv->iobase + APCI1032_DIGITAL_IP_INTERRUPT_STATUS);
+ ui_InterruptStatus = ui_InterruptStatus & 0X0000FFFF;
+ send_sig(SIGIO, devpriv->tsk_Current, 0); // send signal to the sample
+ outl(ui_Temp, devpriv->iobase + APCI1032_DIGITAL_IP_IRQ); //enable the interrupt
+ return;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1032_Reset(struct comedi_device *dev) | |
++----------------------------------------------------------------------------+
+| Task :resets all the registers |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1032_Reset(struct comedi_device * dev)
+{
+ outl(0x0, devpriv->iobase + APCI1032_DIGITAL_IP_IRQ); //disable the interrupts
+ inl(devpriv->iobase + APCI1032_DIGITAL_IP_INTERRUPT_STATUS); //Reset the interrupt status register
+ outl(0x0, devpriv->iobase + APCI1032_DIGITAL_IP_INTERRUPT_MODE1); //Disable the and/or interrupt
+ outl(0x0, devpriv->iobase + APCI1032_DIGITAL_IP_INTERRUPT_MODE2);
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1032.h b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1032.h
new file mode 100644
index 000000000000..659ef85088cd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1032.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+/********* Definitions for APCI-1032 card *****/
+
+#define APCI1032_BOARD_VENDOR_ID 0x15B8
+#define APCI1032_ADDRESS_RANGE 20
+//DIGITAL INPUT DEFINE
+
+#define APCI1032_DIGITAL_IP 0
+#define APCI1032_DIGITAL_IP_INTERRUPT_MODE1 4
+#define APCI1032_DIGITAL_IP_INTERRUPT_MODE2 8
+#define APCI1032_DIGITAL_IP_IRQ 16
+
+//Digital Input IRQ Function Selection
+#define ADDIDATA_OR 0
+#define ADDIDATA_AND 1
+
+//Digital Input Interrupt Status
+#define APCI1032_DIGITAL_IP_INTERRUPT_STATUS 12
+
+//Digital Input Interrupt Enable Disable.
+#define APCI1032_DIGITAL_IP_INTERRUPT_ENABLE 0x4
+#define APCI1032_DIGITAL_IP_INTERRUPT_DISABLE 0xFFFFFFFB
+
+//ADDIDATA Enable Disable
+
+#define ADDIDATA_ENABLE 1
+#define ADDIDATA_DISABLE 0
+
+// Hardware Layer functions for Apci1032
+
+//DI
+// for di read
+
+INT i_APCI1032_ConfigDigitalInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+INT i_APCI1032_Read1DigitalInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+INT i_APCI1032_ReadMoreDigitalInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+// Interrupt functions.....
+
+static void v_APCI1032_Interrupt(int irq, void *d);
+//Reset
+INT i_APCI1032_Reset(struct comedi_device *dev);
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1500.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1500.c
new file mode 100644
index 000000000000..2d6adcff41e3
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1500.c
@@ -0,0 +1,3045 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-------------------------------+---------------------------------------+
+ | Project : APCI-1500 | Compiler : GCC |
+ | Module name : hwdrv_apci1500.c| Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-------------------------------+---------------------------------------+
+ | Description : Hardware Layer Acces For APCI-1500 |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +----------+-----------+------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | | | |
+ | | | |
+ | | | |
+ +----------+-----------+------------------------------------------------+
+*/
+#include "hwdrv_apci1500.h"
+
+int i_TimerCounter1Init = 0;
+int i_TimerCounter2Init = 0;
+int i_WatchdogCounter3Init = 0;
+int i_Event1Status = 0, i_Event2Status = 0;
+int i_TimerCounterWatchdogInterrupt = 0;
+int i_Logic = 0, i_CounterLogic = 0;
+int i_InterruptMask = 0;
+int i_InputChannel = 0;
+int i_TimerCounter1Enabled = 0, i_TimerCounter2Enabled =
+ 0, i_WatchdogCounter3Enabled = 0;
+
+/*
+ +----------------------------------------------------------------------------+
+| Function Name : int i_APCI1500_ConfigDigitalInputEvent |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : An event can be generated for each port. |
+| The first event is related to the first 8 channels |
+| (port 1) and the second to the following 6 channels |
+| (port 2). An interrupt is generated when one or both |
+| events have occurred |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| unsigned int *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| data[0] :Number of the input port on |
+| which the event will take place |
+| (1 or 2)
+ data[1] : The event logic for port 1 has |
+| three possibilities |
+| :0 APCI1500_AND :This logic |
+| links |
+| the inputs |
+| with an AND |
+| logic. |
+| 1 APCI1500_OR :This logic |
+| links |
+| the inputs |
+| with a |
+| OR logic. |
+| 2 APCI1500_OR_PRIORITY |
+| :This logic |
+| links |
+| the inputs |
+| with a |
+| priority |
+| OR logic. |
+| Input 1 |
+| has the |
+| highest |
+| priority |
+| level and |
+| input 8 |
+| the smallest|
+| For the second port the user has|
+| 1 possibility: |
+| APCI1500_OR :This logic |
+| links |
+| the inputs |
+| with a |
+| polarity |
+| OR logic |
+| data[2] : These 8-character word for port1|
+| and 6-character word for port 2 |
+| give the mask of the event. |
+| Each place gives the state |
+| of the input channels and can |
+| have one of these six characters|
+| |
+| 0 : This input must be on 0 |
+| 1 : This input must be on 1 |
+| 2 : This input reacts to |
+| a falling edge |
+| 3 : This input reacts to a |
+| rising edge |
+| 4 : This input reacts to both edges |
+|
+| 5 : This input is not |
+| used for event |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1500_ConfigDigitalInputEvent(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ int i_PatternPolarity = 0, i_PatternTransition = 0, i_PatternMask = 0;
+ int i_MaxChannel = 0, i_Count = 0, i_EventMask = 0;
+ int i_PatternTransitionCount = 0, i_RegValue;
+ int i;
+
+ /*************************************************/
+ /* Selects the master interrupt control register */
+ /*************************************************/
+ outb(APCI1500_RW_MASTER_INTERRUPT_CONTROL,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /**********************************************/
+ /* Disables the main interrupt on the board */
+ /**********************************************/
+ outb(0x00, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ if (data[0] == 1) {
+ i_MaxChannel = 8;
+ } // if (data[0] == 1)
+ else {
+ if (data[0] == 2) {
+ i_MaxChannel = 6;
+ } // if(data[0]==2)
+ else {
+ printk("\nThe specified port event does not exist\n");
+ return -EINVAL;
+ } //else if(data[0]==2)
+ } //else if (data[0] == 1)
+ switch (data[1]) {
+ case 0:
+ data[1] = APCI1500_AND;
+ break;
+ case 1:
+ data[1] = APCI1500_OR;
+ break;
+ case 2:
+ data[1] = APCI1500_OR_PRIORITY;
+ break;
+ default:
+ printk("\nThe specified interrupt logic does not exist\n");
+ return -EINVAL;
+ } //switch(data[1]);
+
+ i_Logic = data[1];
+ for (i_Count = i_MaxChannel, i = 0; i_Count > 0; i_Count--, i++) {
+ i_EventMask = data[2 + i];
+ switch (i_EventMask) {
+ case 0:
+ i_PatternMask =
+ i_PatternMask | (1 << (i_MaxChannel - i_Count));
+ break;
+ case 1:
+ i_PatternMask =
+ i_PatternMask | (1 << (i_MaxChannel - i_Count));
+ i_PatternPolarity =
+ i_PatternPolarity | (1 << (i_MaxChannel -
+ i_Count));
+ break;
+ case 2:
+ i_PatternMask =
+ i_PatternMask | (1 << (i_MaxChannel - i_Count));
+ i_PatternTransition =
+ i_PatternTransition | (1 << (i_MaxChannel -
+ i_Count));
+ break;
+ case 3:
+ i_PatternMask =
+ i_PatternMask | (1 << (i_MaxChannel - i_Count));
+ i_PatternPolarity =
+ i_PatternPolarity | (1 << (i_MaxChannel -
+ i_Count));
+ i_PatternTransition =
+ i_PatternTransition | (1 << (i_MaxChannel -
+ i_Count));
+ break;
+ case 4:
+ i_PatternTransition =
+ i_PatternTransition | (1 << (i_MaxChannel -
+ i_Count));
+ break;
+ case 5:
+ break;
+ default:
+ printk("\nThe option indicated in the event mask does not exist\n");
+ return -EINVAL;
+ } // switch(i_EventMask)
+ } //for (i_Count = i_MaxChannel; i_Count >0;i_Count --)
+
+ if (data[0] == 1) {
+ /****************************/
+ /* Test the interrupt logic */
+ /****************************/
+
+ if (data[1] == APCI1500_AND ||
+ data[1] == APCI1500_OR ||
+ data[1] == APCI1500_OR_PRIORITY) {
+ /**************************************/
+ /* Tests if a transition was declared */
+ /* for a OR PRIORITY logic */
+ /**************************************/
+
+ if (data[1] == APCI1500_OR_PRIORITY
+ && i_PatternTransition != 0) {
+ /********************************************/
+ /* Transition error on an OR PRIORITY logic */
+ /********************************************/
+ printk("\nTransition error on an OR PRIORITY logic\n");
+ return -EINVAL;
+ } // if (data[1]== APCI1500_OR_PRIORITY && i_PatternTransition != 0)
+
+ /*************************************/
+ /* Tests if more than one transition */
+ /* was declared for an AND logic */
+ /*************************************/
+
+ if (data[1] == APCI1500_AND) {
+ for (i_Count = 0; i_Count < 8; i_Count++) {
+ i_PatternTransitionCount =
+ i_PatternTransitionCount +
+ ((i_PatternTransition >>
+ i_Count) & 0x1);
+
+ } //for (i_Count = 0; i_Count < 8; i_Count++)
+
+ if (i_PatternTransitionCount > 1) {
+ /****************************************/
+ /* Transition error on an AND logic */
+ /****************************************/
+ printk("\n Transition error on an AND logic\n");
+ return -EINVAL;
+ } // if (i_PatternTransitionCount > 1)
+ } // if (data[1]== APCI1500_AND)
+
+ /*****************************************************************/
+ /* Selects the APCI1500_RW_MASTER_CONFIGURATION_CONTROL register */
+ /*****************************************************************/
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /******************/
+ /* Disable Port A */
+ /******************/
+ outb(0xF0,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /**********************************************/
+ /* Selects the polarity register of port 1 */
+ /**********************************************/
+ outb(APCI1500_RW_PORT_A_PATTERN_POLARITY,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_PatternPolarity,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*********************************************/
+ /* Selects the pattern mask register of */
+ /* port 1 */
+ /*********************************************/
+ outb(APCI1500_RW_PORT_A_PATTERN_MASK,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_PatternMask,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /********************************************/
+ /* Selects the pattern transition register */
+ /* of port 1 */
+ /********************************************/
+ outb(APCI1500_RW_PORT_A_PATTERN_TRANSITION,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_PatternTransition,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /******************************************/
+ /* Selects the mode specification mask */
+ /* register of port 1 */
+ /******************************************/
+ outb(APCI1500_RW_PORT_A_SPECIFICATION,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue =
+ inb(devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /******************************************/
+ /* Selects the mode specification mask */
+ /* register of port 1 */
+ /******************************************/
+ outb(APCI1500_RW_PORT_A_SPECIFICATION,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /**********************/
+ /* Port A new mode */
+ /**********************/
+
+ i_RegValue = (i_RegValue & 0xF9) | data[1] | 0x9;
+ outb(i_RegValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ i_Event1Status = 1;
+
+ /*****************************************************************/
+ /* Selects the APCI1500_RW_MASTER_CONFIGURATION_CONTROL register */
+ /*****************************************************************/
+
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /*****************/
+ /* Enable Port A */
+ /*****************/
+ outb(0xF4,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ } // if(data[1]==APCI1500_AND||data[1]==APCI1500_OR||data[1]==APCI1500_OR_PRIORITY)
+ else {
+ printk("\nThe choice for interrupt logic does not exist\n");
+ return -EINVAL;
+ } // else }// if(data[1]==APCI1500_AND||data[1]==APCI1500_OR||data[1]==APCI1500_OR_PRIORITY)
+ } // if (data[0]== 1)
+
+ /************************************/
+ /* Test if event setting for port 2 */
+ /************************************/
+
+ if (data[0] == 2) {
+ /************************/
+ /* Test the event logic */
+ /************************/
+
+ if (data[1] == APCI1500_OR) {
+ /*****************************************************************/
+ /* Selects the APCI1500_RW_MASTER_CONFIGURATION_CONTROL register */
+ /*****************************************************************/
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /******************/
+ /* Disable Port B */
+ /******************/
+ outb(0x74,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /****************************************/
+ /* Selects the mode specification mask */
+ /* register of port B */
+ /****************************************/
+ outb(APCI1500_RW_PORT_B_SPECIFICATION,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue =
+ inb(devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /******************************************/
+ /* Selects the mode specification mask */
+ /* register of port B */
+ /******************************************/
+ outb(APCI1500_RW_PORT_B_SPECIFICATION,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue = i_RegValue & 0xF9;
+ outb(i_RegValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /**********************************/
+ /* Selects error channels 1 and 2 */
+ /**********************************/
+
+ i_PatternMask = (i_PatternMask | 0xC0);
+ i_PatternPolarity = (i_PatternPolarity | 0xC0);
+ i_PatternTransition = (i_PatternTransition | 0xC0);
+
+ /**********************************************/
+ /* Selects the polarity register of port 2 */
+ /**********************************************/
+ outb(APCI1500_RW_PORT_B_PATTERN_POLARITY,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_PatternPolarity,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /**********************************************/
+ /* Selects the pattern transition register */
+ /* of port 2 */
+ /**********************************************/
+ outb(APCI1500_RW_PORT_B_PATTERN_TRANSITION,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_PatternTransition,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /**********************************************/
+ /* Selects the pattern Mask register */
+ /* of port 2 */
+ /**********************************************/
+
+ outb(APCI1500_RW_PORT_B_PATTERN_MASK,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_PatternMask,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /******************************************/
+ /* Selects the mode specification mask */
+ /* register of port 2 */
+ /******************************************/
+ outb(APCI1500_RW_PORT_B_SPECIFICATION,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue =
+ inb(devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /******************************************/
+ /* Selects the mode specification mask */
+ /* register of port 2 */
+ /******************************************/
+ outb(APCI1500_RW_PORT_B_SPECIFICATION,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue = (i_RegValue & 0xF9) | 4;
+ outb(i_RegValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ i_Event2Status = 1;
+ /*****************************************************************/
+ /* Selects the APCI1500_RW_MASTER_CONFIGURATION_CONTROL register */
+ /*****************************************************************/
+
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /*****************/
+ /* Enable Port B */
+ /*****************/
+
+ outb(0xF4,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ } // if (data[1] == APCI1500_OR)
+ else {
+ printk("\nThe choice for interrupt logic does not exist\n");
+ return -EINVAL;
+ } //elseif (data[1] == APCI1500_OR)
+ } //if(data[0]==2)
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1500_StartStopInputEvent |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Allows or disallows a port event |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_Channel : Channel number to read |
+| unsigned int *data : Data Pointer to read status |
+ data[0] :0 Start input event
+ 1 Stop input event
+ data[1] :No of port (1 or 2)
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI1500_StartStopInputEvent(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i_Event1InterruptStatus = 0, i_Event2InterruptStatus =
+ 0, i_RegValue;
+ switch (data[0]) {
+ case START:
+ /*************************/
+ /* Tests the port number */
+ /*************************/
+
+ if (data[1] == 1 || data[1] == 2) {
+ /***************************/
+ /* Test if port 1 selected */
+ /***************************/
+
+ if (data[1] == 1) {
+ /*****************************/
+ /* Test if event initialised */
+ /*****************************/
+ if (i_Event1Status == 1) {
+ /*****************************************************************/
+ /* Selects the APCI1500_RW_MASTER_CONFIGURATION_CONTROL register */
+ /*****************************************************************/
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /******************/
+ /* Disable Port A */
+ /******************/
+ outb(0xF0,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /***************************************************/
+ /* Selects the command and status register of */
+ /* port 1 */
+ /***************************************************/
+ outb(APCI1500_RW_PORT_A_COMMAND_AND_STATUS, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*************************************/
+ /* Allows the pattern interrupt */
+ /*************************************/
+ outb(0xC0,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /*****************************************************************/
+ /* Selects the APCI1500_RW_MASTER_CONFIGURATION_CONTROL register */
+ /*****************************************************************/
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*****************/
+ /* Enable Port A */
+ /*****************/
+ outb(0xF4,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_Event1InterruptStatus = 1;
+ outb(APCI1500_RW_PORT_A_SPECIFICATION,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue =
+ inb(devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /* Selects the master interrupt control register */
+ /*************************************************/
+ outb(APCI1500_RW_MASTER_INTERRUPT_CONTROL, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /**********************************************/
+ /* Authorizes the main interrupt on the board */
+ /**********************************************/
+ outb(0xD0,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ } // if(i_Event1Status==1)
+ else {
+ printk("\nEvent 1 not initialised\n");
+ return -EINVAL;
+ } //else if(i_Event1Status==1)
+ } //if (data[1]==1)
+ if (data[1] == 2) {
+
+ if (i_Event2Status == 1) {
+ /*****************************************************************/
+ /* Selects the APCI1500_RW_MASTER_CONFIGURATION_CONTROL register */
+ /*****************************************************************/
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /******************/
+ /* Disable Port B */
+ /******************/
+ outb(0x74,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /***************************************************/
+ /* Selects the command and status register of */
+ /* port 2 */
+ /***************************************************/
+ outb(APCI1500_RW_PORT_B_COMMAND_AND_STATUS, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*************************************/
+ /* Allows the pattern interrupt */
+ /*************************************/
+ outb(0xC0,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /*****************************************************************/
+ /* Selects the APCI1500_RW_MASTER_CONFIGURATION_CONTROL register */
+ /*****************************************************************/
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*****************/
+ /* Enable Port B */
+ /*****************/
+ outb(0xF4,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /* Selects the master interrupt control register */
+ /*************************************************/
+ outb(APCI1500_RW_MASTER_INTERRUPT_CONTROL, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /**********************************************/
+ /* Authorizes the main interrupt on the board */
+ /**********************************************/
+ outb(0xD0,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_Event2InterruptStatus = 1;
+ } // if(i_Event2Status==1)
+ else {
+ printk("\nEvent 2 not initialised\n");
+ return -EINVAL;
+ } //else if(i_Event2Status==1)
+ } // if(data[1]==2)
+ } // if (data[1] == 1 || data[0] == 2)
+ else {
+ printk("\nThe port parameter is in error\n");
+ return -EINVAL;
+ } //else if (data[1] == 1 || data[0] == 2)
+
+ break;
+
+ case STOP:
+ /*************************/
+ /* Tests the port number */
+ /*************************/
+
+ if (data[1] == 1 || data[1] == 2) {
+ /***************************/
+ /* Test if port 1 selected */
+ /***************************/
+
+ if (data[1] == 1) {
+ /*****************************/
+ /* Test if event initialised */
+ /*****************************/
+ if (i_Event1Status == 1) {
+ /*****************************************************************/
+ /* Selects the APCI1500_RW_MASTER_CONFIGURATION_CONTROL register */
+ /*****************************************************************/
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /******************/
+ /* Disable Port A */
+ /******************/
+ outb(0xF0,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /***************************************************/
+ /* Selects the command and status register of */
+ /* port 1 */
+ /***************************************************/
+ outb(APCI1500_RW_PORT_A_COMMAND_AND_STATUS, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*************************************/
+ /* Inhibits the pattern interrupt */
+ /*************************************/
+ outb(0xE0,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /*****************************************************************/
+ /* Selects the APCI1500_RW_MASTER_CONFIGURATION_CONTROL register */
+ /*****************************************************************/
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*****************/
+ /* Enable Port A */
+ /*****************/
+ outb(0xF4,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_Event1InterruptStatus = 0;
+ } // if(i_Event1Status==1)
+ else {
+ printk("\nEvent 1 not initialised\n");
+ return -EINVAL;
+ } //else if(i_Event1Status==1)
+ } //if (data[1]==1)
+ if (data[1] == 2) {
+ /*****************************/
+ /* Test if event initialised */
+ /*****************************/
+ if (i_Event2Status == 1) {
+ /*****************************************************************/
+ /* Selects the APCI1500_RW_MASTER_CONFIGURATION_CONTROL register */
+ /*****************************************************************/
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /******************/
+ /* Disable Port B */
+ /******************/
+ outb(0x74,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /***************************************************/
+ /* Selects the command and status register of */
+ /* port 2 */
+ /***************************************************/
+ outb(APCI1500_RW_PORT_B_COMMAND_AND_STATUS, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*************************************/
+ /* Inhibits the pattern interrupt */
+ /*************************************/
+ outb(0xE0,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /*****************************************************************/
+ /* Selects the APCI1500_RW_MASTER_CONFIGURATION_CONTROL register */
+ /*****************************************************************/
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*****************/
+ /* Enable Port B */
+ /*****************/
+ outb(0xF4,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_Event2InterruptStatus = 0;
+ } // if(i_Event2Status==1)
+ else {
+ printk("\nEvent 2 not initialised\n");
+ return -EINVAL;
+ } //else if(i_Event2Status==1)
+ } //if(data[1]==2)
+
+ } // if (data[1] == 1 || data[1] == 2)
+ else {
+ printk("\nThe port parameter is in error\n");
+ return -EINVAL;
+ } //else if (data[1] == 1 || data[1] == 2)
+ break;
+ default:
+ printk("\nThe option of START/STOP logic does not exist\n");
+ return -EINVAL;
+ } //switch(data[0])
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1500_Initialisation |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Return the status of the digital input |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_Channel : Channel number to read |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI1500_Initialisation(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i_DummyRead = 0;
+ /******************/
+ /* Software reset */
+ /******************/
+ i_DummyRead = inb(devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ i_DummyRead = inb(devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(1, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*****************************************************/
+ /* Selects the master configuration control register */
+ /*****************************************************/
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0xF4, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*****************************************************/
+ /* Selects the mode specification register of port A */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_A_SPECIFICATION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0x10, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /* Selects the data path polarity register of port A */
+ outb(APCI1500_RW_PORT_A_DATA_PCITCH_POLARITY,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* High level of port A means 1 */
+ outb(0xFF, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /* Selects the data direction register of port A */
+ outb(APCI1500_RW_PORT_A_DATA_DIRECTION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* All bits used as inputs */
+ outb(0xFF, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the command and status register of port A */
+ outb(APCI1500_RW_PORT_A_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes IP and IUS */
+ outb(0x20, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the command and status register of port A */
+ outb(APCI1500_RW_PORT_A_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deactivates the interrupt management of port A: */
+ outb(0xE0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the handshake specification register of port A */
+ outb(APCI1500_RW_PORT_A_HANDSHAKE_SPECIFICATION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes the register */
+ outb(0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*****************************************************/
+ /* Selects the mode specification register of port B */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_B_SPECIFICATION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0x10, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the data path polarity register of port B */
+ outb(APCI1500_RW_PORT_B_DATA_PCITCH_POLARITY,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* A high level of port B means 1 */
+ outb(0x7F, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the data direction register of port B */
+ outb(APCI1500_RW_PORT_B_DATA_DIRECTION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* All bits used as inputs */
+ outb(0xFF, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the command and status register of port B */
+ outb(APCI1500_RW_PORT_B_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes IP and IUS */
+ outb(0x20, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the command and status register of port B */
+ outb(APCI1500_RW_PORT_B_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deactivates the interrupt management of port B: */
+ outb(0xE0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the handshake specification register of port B */
+ outb(APCI1500_RW_PORT_B_HANDSHAKE_SPECIFICATION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes the register */
+ outb(0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*****************************************************/
+ /* Selects the data path polarity register of port C */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_C_DATA_PCITCH_POLARITY,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* High level of port C means 1 */
+ outb(0x9, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the data direction register of port C */
+ outb(APCI1500_RW_PORT_C_DATA_DIRECTION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* All bits used as inputs except channel 1 */
+ outb(0x0E, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the special IO register of port C */
+ outb(APCI1500_RW_PORT_C_SPECIAL_IO_CONTROL,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes it */
+ outb(0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /******************************************************/
+ /* Selects the command and status register of timer 1 */
+ /******************************************************/
+ outb(APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes IP and IUS */
+ outb(0x20, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the command and status register of timer 1 */
+ outb(APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deactivates the interrupt management of timer 1 */
+ outb(0xE0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /******************************************************/
+ /* Selects the command and status register of timer 2 */
+ /******************************************************/
+ outb(APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes IP and IUS */
+ outb(0x20, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the command and status register of timer 2 */
+ outb(APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deactivates Timer 2 interrupt management: */
+ outb(0xE0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /******************************************************/
+ /* Selects the command and status register of timer 3 */
+ /******************************************************/
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes IP and IUS */
+ outb(0x20, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the command and status register of Timer 3 */
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deactivates interrupt management of timer 3: */
+ outb(0xE0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*************************************************/
+ /* Selects the master interrupt control register */
+ /*************************************************/
+ outb(APCI1500_RW_MASTER_INTERRUPT_CONTROL,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes all interrupts */
+ outb(0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1500_ReadMoreDigitalInput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Return the status of the Requested digital inputs |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_NoOfChannels : No Of Channels To be Read |
+| UINT *data : Data Pointer
+ data[0] : 0 Read a single channel
+ 1 read a port value
+ data[1] : port value
++----------------------------------------------------------------------------+
+| Output Parameters : -- data[0] :The read status value
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1500_ReadMoreDigitalInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_PortValue = data[1];
+ UINT ui_Mask = 0;
+ UINT ui_Channel;
+ UINT ui_TmpValue = 0;
+ ui_Channel = CR_CHAN(insn->chanspec);
+
+ switch (data[0]) {
+ case 0:
+ if (ui_Channel >= 0 && ui_Channel <= 15) {
+ ui_TmpValue =
+ (UINT) inw(devpriv->i_IobaseAddon +
+ APCI1500_DIGITAL_IP);
+ *data = (ui_TmpValue >> ui_Channel) & 0x1;
+ } //if(ui_Channel >= 0 && ui_Channel <=15)
+ else {
+ printk("\nThe channel specification are in error\n");
+ return -EINVAL; // "sorry channel spec wrong "
+ } //else if(ui_Channel >= 0 && ui_Channel <=15)
+ break;
+ case 1:
+
+ *data = (UINT) inw(devpriv->i_IobaseAddon +
+ APCI1500_DIGITAL_IP);
+ switch (ui_Channel) {
+ case 2:
+ ui_Mask = 3;
+ *data = (*data >> (2 * ui_PortValue)) & ui_Mask;
+ break;
+ case 4:
+ ui_Mask = 15;
+ *data = (*data >> (4 * ui_PortValue)) & ui_Mask;
+ break;
+ case 8:
+ ui_Mask = 255;
+ *data = (*data >> (8 * ui_PortValue)) & ui_Mask;
+ break;
+ case 15:
+ break;
+
+ default:
+ printk("\nSpecified channel cannot be read \n");
+ return -EINVAL; // "sorry channel spec wrong "
+ break;
+ } //switch(ui_Channel)
+ break;
+ default:
+ printk("\nThe specified functionality does not exist\n");
+ return -EINVAL;
+ } //switch(data[0])
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1500_ConfigDigitalOutputErrorInterrupt
+ (struct comedi_device *dev,struct comedi_subdevice *s struct comedi_insn
+ *insn,unsigned int *data) |
+| |
++----------------------------------------------------------------------------+
+| Task : Configures the digital output memory and the digital
+ output error interrupt |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| unsigned int *data : Data Pointer contains |
+| configuration parameters as below |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| data[0] :1:Memory on |
+| 0:Memory off |
+ data[1] :1 Enable the voltage error interrupt
+| :0 Disable the voltage error interrupt |
+| |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI1500_ConfigDigitalOutputErrorInterrupt(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ devpriv->b_OutputMemoryStatus = data[0];
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1500_WriteDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Writes port value To the selected port |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_NoOfChannels : No Of Channels To Write |
+| UINT *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1500_WriteDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ static UINT ui_Temp = 0;
+ UINT ui_Temp1;
+
+ UINT ui_NoOfChannel = CR_CHAN(insn->chanspec); // get the channel
+
+ if (!devpriv->b_OutputMemoryStatus) {
+ ui_Temp = 0;
+
+ } //if(!devpriv->b_OutputMemoryStatus )
+ if (data[3] == 0) {
+ if (data[1] == 0) {
+ data[0] = (data[0] << ui_NoOfChannel) | ui_Temp;
+ outw(data[0],
+ devpriv->i_IobaseAddon + APCI1500_DIGITAL_OP);
+ } //if(data[1]==0)
+ else {
+ if (data[1] == 1) {
+ switch (ui_NoOfChannel) {
+
+ case 2:
+ data[0] =
+ (data[0] << (2 *
+ data[2])) | ui_Temp;
+ break;
+
+ case 4:
+ data[0] =
+ (data[0] << (4 *
+ data[2])) | ui_Temp;
+ break;
+
+ case 8:
+ data[0] =
+ (data[0] << (8 *
+ data[2])) | ui_Temp;
+ break;
+
+ case 15:
+ data[0] = data[0] | ui_Temp;
+ break;
+
+ default:
+ comedi_error(dev, " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+
+ } //switch(ui_NoOfChannels)
+
+ outw(data[0],
+ devpriv->i_IobaseAddon +
+ APCI1500_DIGITAL_OP);
+ } // if(data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } //else if(data[1]==1)
+ } //elseif(data[1]==0)
+ } //if(data[3]==0)
+ else {
+ if (data[3] == 1) {
+ if (data[1] == 0) {
+ data[0] = ~data[0] & 0x1;
+ ui_Temp1 = 1;
+ ui_Temp1 = ui_Temp1 << ui_NoOfChannel;
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ (data[0] << ui_NoOfChannel) ^
+ 0xffffffff;
+ data[0] = data[0] & ui_Temp;
+ outw(data[0],
+ devpriv->i_IobaseAddon +
+ APCI1500_DIGITAL_OP);
+ } //if(data[1]==0)
+ else {
+ if (data[1] == 1) {
+ switch (ui_NoOfChannel) {
+
+ case 2:
+ data[0] = ~data[0] & 0x3;
+ ui_Temp1 = 3;
+ ui_Temp1 =
+ ui_Temp1 << 2 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (2 *
+ data
+ [2])) ^
+ 0xffffffff) & ui_Temp;
+ break;
+
+ case 4:
+ data[0] = ~data[0] & 0xf;
+ ui_Temp1 = 15;
+ ui_Temp1 =
+ ui_Temp1 << 4 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (4 *
+ data
+ [2])) ^
+ 0xffffffff) & ui_Temp;
+ break;
+
+ case 8:
+ data[0] = ~data[0] & 0xff;
+ ui_Temp1 = 255;
+ ui_Temp1 =
+ ui_Temp1 << 8 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (8 *
+ data
+ [2])) ^
+ 0xffffffff) & ui_Temp;
+ break;
+
+ case 15:
+ break;
+
+ default:
+ comedi_error(dev,
+ " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+
+ } //switch(ui_NoOfChannels)
+
+ outw(data[0],
+ devpriv->i_IobaseAddon +
+ APCI1500_DIGITAL_OP);
+ } // if(data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } //else if(data[1]==1)
+ } //elseif(data[1]==0)
+ } //if(data[3]==1);
+ else {
+ printk("\nSpecified functionality does not exist\n");
+ return -EINVAL;
+ } //if else data[3]==1)
+ } //if else data[3]==0)
+ ui_Temp = data[0];
+ return (insn->n);;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1500_ConfigCounterTimerWatchdog(comedi_device
+ *dev,struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data)|
+| |
++----------------------------------------------------------------------------+
+| Task : Configures The Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status data[0] : 2 APCI1500_1_8_KHZ
+| 1 APCI1500_3_6_KHZ |
+| 0 APCI1500_115_KHZ
+ data[1] : 0 Counter1/Timer1
+ 1 Counter2/Timer2
+ 2 Counter3/Watchdog
+ data[2] : 0 Counter
+ 1 Timer/Watchdog
+ data[3] : This parameter has |
+| two meanings. |
+| - If the counter/timer |
+| is used as a counter |
+| the limit value of |
+| the counter is given |
+| |
+| - If the counter/timer |
+| is used as a timer, |
+| the divider factor |
+| for the output is |
+| given.
+ data[4] : 0 APCI1500_CONTINUOUS
+ 1 APCI1500_SINGLE
+ data[5] : 0 Software Trigger
+ 1 Hardware Trigger
+
+ data[6] :0 Software gate
+ 1 Hardware gate
+ data[7] :0 Interrupt Disable
+ 1 Interrupt Enable
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI1500_ConfigCounterTimerWatchdog(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ int i_TimerCounterMode, i_MasterConfiguration;
+
+ devpriv->tsk_Current = current;
+
+//Selection of the input clock
+ if (data[0] == 0 || data[0] == 1 || data[0] == 2) {
+ outw(data[0], devpriv->i_IobaseAddon + APCI1500_CLK_SELECT);
+ } // if(data[0]==0||data[0]==1||data[0]==2)
+ else {
+ if (data[0] != 3) {
+ printk("\nThe option for input clock selection does not exist\n");
+ return -EINVAL;
+ } // if(data[0]!=3)
+ } //elseif(data[0]==0||data[0]==1||data[0]==2)
+ //Select the counter/timer
+ switch (data[1]) {
+ case COUNTER1:
+ //selecting counter or timer
+ switch (data[2]) {
+ case 0:
+ data[2] = APCI1500_COUNTER;
+ break;
+ case 1:
+ data[2] = APCI1500_TIMER;
+ break;
+ default:
+ printk("\nThis choice is not a timer nor a counter\n");
+ return -EINVAL;
+ } // switch(data[2])
+
+ //Selecting single or continuous mode
+ switch (data[4]) {
+ case 0:
+ data[4] = APCI1500_CONTINUOUS;
+ break;
+ case 1:
+ data[4] = APCI1500_SINGLE;
+ break;
+ default:
+ printk("\nThis option for single/continuous mode does not exist\n");
+ return -EINVAL;
+ } // switch(data[4])
+
+ i_TimerCounterMode = data[2] | data[4] | 7;
+ /*************************/
+ /* Test the reload value */
+ /*************************/
+
+ if ((data[3] >= 0) && (data[3] <= 65535)) {
+ if (data[7] == APCI1500_ENABLE
+ || data[7] == APCI1500_DISABLE) {
+
+ /************************************************/
+ /* Selects the mode register of timer/counter 1 */
+ /************************************************/
+ outb(APCI1500_RW_CPT_TMR1_MODE_SPECIFICATION,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /***********************/
+ /* Writes the new mode */
+ /***********************/
+ outb(i_TimerCounterMode,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /****************************************************/
+ /* Selects the constant register of timer/counter 1 */
+ /****************************************************/
+
+ outb(APCI1500_RW_CPT_TMR1_TIME_CST_LOW,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*************************/
+ /* Writes the low value */
+ /*************************/
+
+ outb(data[3],
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /****************************************************/
+ /* Selects the constant register of timer/counter 1 */
+ /****************************************************/
+
+ outb(APCI1500_RW_CPT_TMR1_TIME_CST_HIGH,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /**************************/
+ /* Writes the high value */
+ /**************************/
+
+ data[3] = data[3] >> 8;
+ outb(data[3],
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*********************************************/
+ /* Selects the master configuration register */
+ /*********************************************/
+
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /**********************/
+ /* Reads the register */
+ /**********************/
+
+ i_MasterConfiguration =
+ inb(devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /********************************************************/
+ /* Enables timer/counter 1 and triggers timer/counter 1 */
+ /********************************************************/
+
+ i_MasterConfiguration =
+ i_MasterConfiguration | 0x40;
+
+ /*********************************************/
+ /* Selects the master configuration register */
+ /*********************************************/
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /********************************/
+ /* Writes the new configuration */
+ /********************************/
+ outb(i_MasterConfiguration,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /****************************************/
+ /* Selects the commands register of */
+ /* timer/counter 1 */
+ /****************************************/
+
+ outb(APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /***************************/
+ /* Disable timer/counter 1 */
+ /***************************/
+
+ outb(0x0,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /****************************************/
+ /* Selects the commands register of */
+ /* timer/counter 1 */
+ /****************************************/
+ outb(APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /***************************/
+ /* Trigger timer/counter 1 */
+ /***************************/
+ outb(0x2,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ } //if(data[7]== APCI1500_ENABLE ||data[7]== APCI1500_DISABLE)
+ else {
+ printk("\nError in selection of interrupt enable or disable\n");
+ return -EINVAL;
+ } //elseif(data[7]== APCI1500_ENABLE ||data[7]== APCI1500_DISABLE)
+ } // if ((data[3]>= 0) && (data[3] <= 65535))
+ else {
+ printk("\nError in selection of reload value\n");
+ return -EINVAL;
+ } //else if ((data[3]>= 0) && (data[3] <= 65535))
+ i_TimerCounterWatchdogInterrupt = data[7];
+ i_TimerCounter1Init = 1;
+ break;
+
+ case COUNTER2: //selecting counter or timer
+ switch (data[2]) {
+ case 0:
+ data[2] = APCI1500_COUNTER;
+ break;
+ case 1:
+ data[2] = APCI1500_TIMER;
+ break;
+ default:
+ printk("\nThis choice is not a timer nor a counter\n");
+ return -EINVAL;
+ } // switch(data[2])
+
+ //Selecting single or continuous mode
+ switch (data[4]) {
+ case 0:
+ data[4] = APCI1500_CONTINUOUS;
+ break;
+ case 1:
+ data[4] = APCI1500_SINGLE;
+ break;
+ default:
+ printk("\nThis option for single/continuous mode does not exist\n");
+ return -EINVAL;
+ } // switch(data[4])
+
+ //Selecting software or hardware trigger
+ switch (data[5]) {
+ case 0:
+ data[5] = APCI1500_SOFTWARE_TRIGGER;
+ break;
+ case 1:
+ data[5] = APCI1500_HARDWARE_TRIGGER;
+ break;
+ default:
+ printk("\nThis choice for software or hardware trigger does not exist\n");
+ return -EINVAL;
+ } // switch(data[5])
+
+ //Selecting software or hardware gate
+ switch (data[6]) {
+ case 0:
+ data[6] = APCI1500_SOFTWARE_GATE;
+ break;
+ case 1:
+ data[6] = APCI1500_HARDWARE_GATE;
+ break;
+ default:
+ printk("\nThis choice for software or hardware gate does not exist\n");
+ return -EINVAL;
+ } // switch(data[6])
+
+ i_TimerCounterMode = data[2] | data[4] | data[5] | data[6] | 7;
+
+ /*************************/
+ /* Test the reload value */
+ /*************************/
+
+ if ((data[3] >= 0) && (data[3] <= 65535)) {
+ if (data[7] == APCI1500_ENABLE
+ || data[7] == APCI1500_DISABLE) {
+
+ /************************************************/
+ /* Selects the mode register of timer/counter 2 */
+ /************************************************/
+ outb(APCI1500_RW_CPT_TMR2_MODE_SPECIFICATION,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /***********************/
+ /* Writes the new mode */
+ /***********************/
+ outb(i_TimerCounterMode,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /****************************************************/
+ /* Selects the constant register of timer/counter 2 */
+ /****************************************************/
+
+ outb(APCI1500_RW_CPT_TMR2_TIME_CST_LOW,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*************************/
+ /* Writes the low value */
+ /*************************/
+
+ outb(data[3],
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /****************************************************/
+ /* Selects the constant register of timer/counter 2 */
+ /****************************************************/
+
+ outb(APCI1500_RW_CPT_TMR2_TIME_CST_HIGH,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /**************************/
+ /* Writes the high value */
+ /**************************/
+
+ data[3] = data[3] >> 8;
+ outb(data[3],
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*********************************************/
+ /* Selects the master configuration register */
+ /*********************************************/
+
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /**********************/
+ /* Reads the register */
+ /**********************/
+
+ i_MasterConfiguration =
+ inb(devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /********************************************************/
+ /* Enables timer/counter 2 and triggers timer/counter 2 */
+ /********************************************************/
+
+ i_MasterConfiguration =
+ i_MasterConfiguration | 0x20;
+
+ /*********************************************/
+ /* Selects the master configuration register */
+ /*********************************************/
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /********************************/
+ /* Writes the new configuration */
+ /********************************/
+ outb(i_MasterConfiguration,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /****************************************/
+ /* Selects the commands register of */
+ /* timer/counter 2 */
+ /****************************************/
+
+ outb(APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /***************************/
+ /* Disable timer/counter 2 */
+ /***************************/
+
+ outb(0x0,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /****************************************/
+ /* Selects the commands register of */
+ /* timer/counter 2 */
+ /****************************************/
+ outb(APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /***************************/
+ /* Trigger timer/counter 1 */
+ /***************************/
+ outb(0x2,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ } //if(data[7]== APCI1500_ENABLE ||data[7]== APCI1500_DISABLE)
+ else {
+ printk("\nError in selection of interrupt enable or disable\n");
+ return -EINVAL;
+ } //elseif(data[7]== APCI1500_ENABLE ||data[7]== APCI1500_DISABLE)
+ } // if ((data[3]>= 0) && (data[3] <= 65535))
+ else {
+ printk("\nError in selection of reload value\n");
+ return -EINVAL;
+ } //else if ((data[3]>= 0) && (data[3] <= 65535))
+ i_TimerCounterWatchdogInterrupt = data[7];
+ i_TimerCounter2Init = 1;
+ break;
+
+ case COUNTER3: //selecting counter or watchdog
+ switch (data[2]) {
+ case 0:
+ data[2] = APCI1500_COUNTER;
+ break;
+ case 1:
+ data[2] = APCI1500_WATCHDOG;
+ break;
+ default:
+ printk("\nThis choice is not a watchdog nor a counter\n");
+ return -EINVAL;
+ } // switch(data[2])
+
+ //Selecting single or continuous mode
+ switch (data[4]) {
+ case 0:
+ data[4] = APCI1500_CONTINUOUS;
+ break;
+ case 1:
+ data[4] = APCI1500_SINGLE;
+ break;
+ default:
+ printk("\nThis option for single/continuous mode does not exist\n");
+ return -EINVAL;
+ } // switch(data[4])
+
+ //Selecting software or hardware gate
+ switch (data[6]) {
+ case 0:
+ data[6] = APCI1500_SOFTWARE_GATE;
+ break;
+ case 1:
+ data[6] = APCI1500_HARDWARE_GATE;
+ break;
+ default:
+ printk("\nThis choice for software or hardware gate does not exist\n");
+ return -EINVAL;
+ } // switch(data[6])
+
+ /*****************************/
+ /* Test if used for watchdog */
+ /*****************************/
+
+ if (data[2] == APCI1500_WATCHDOG) {
+ /*****************************/
+ /* - Enables the output line */
+ /* - Enables retrigger */
+ /* - Pulses output */
+ /*****************************/
+ i_TimerCounterMode = data[2] | data[4] | 0x54;
+ } //if (data[2] == APCI1500_WATCHDOG)
+ else {
+ i_TimerCounterMode = data[2] | data[4] | data[6] | 7;
+ } //elseif (data[2] == APCI1500_WATCHDOG)
+ /*************************/
+ /* Test the reload value */
+ /*************************/
+
+ if ((data[3] >= 0) && (data[3] <= 65535)) {
+ if (data[7] == APCI1500_ENABLE
+ || data[7] == APCI1500_DISABLE) {
+
+ /************************************************/
+ /* Selects the mode register of watchdog/counter 3 */
+ /************************************************/
+ outb(APCI1500_RW_CPT_TMR3_MODE_SPECIFICATION,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /***********************/
+ /* Writes the new mode */
+ /***********************/
+ outb(i_TimerCounterMode,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /****************************************************/
+ /* Selects the constant register of watchdog/counter 3 */
+ /****************************************************/
+
+ outb(APCI1500_RW_CPT_TMR3_TIME_CST_LOW,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*************************/
+ /* Writes the low value */
+ /*************************/
+
+ outb(data[3],
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /****************************************************/
+ /* Selects the constant register of watchdog/counter 3 */
+ /****************************************************/
+
+ outb(APCI1500_RW_CPT_TMR3_TIME_CST_HIGH,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /**************************/
+ /* Writes the high value */
+ /**************************/
+
+ data[3] = data[3] >> 8;
+ outb(data[3],
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*********************************************/
+ /* Selects the master configuration register */
+ /*********************************************/
+
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /**********************/
+ /* Reads the register */
+ /**********************/
+
+ i_MasterConfiguration =
+ inb(devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /********************************************************/
+ /* Enables watchdog/counter 3 and triggers watchdog/counter 3 */
+ /********************************************************/
+
+ i_MasterConfiguration =
+ i_MasterConfiguration | 0x10;
+
+ /*********************************************/
+ /* Selects the master configuration register */
+ /*********************************************/
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /********************************/
+ /* Writes the new configuration */
+ /********************************/
+ outb(i_MasterConfiguration,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /********************/
+ /* Test if COUNTER */
+ /********************/
+ if (data[2] == APCI1500_COUNTER) {
+
+ /*************************************/
+ /* Selects the command register of */
+ /* watchdog/counter 3 */
+ /*************************************/
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /*************************************************/
+ /* Disable the watchdog/counter 3 and starts it */
+ /*************************************************/
+ outb(0x0,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*************************************/
+ /* Selects the command register of */
+ /* watchdog/counter 3 */
+ /*************************************/
+
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /*************************************************/
+ /* Trigger the watchdog/counter 3 and starts it */
+ /*************************************************/
+ outb(0x2,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ } //elseif(data[2]==APCI1500_COUNTER)
+
+ } //if(data[7]== APCI1500_ENABLE ||data[7]== APCI1500_DISABLE)
+ else {
+ printk("\nError in selection of interrupt enable or disable\n");
+ return -EINVAL;
+ } //elseif(data[7]== APCI1500_ENABLE ||data[7]== APCI1500_DISABLE)
+ } // if ((data[3]>= 0) && (data[3] <= 65535))
+ else {
+ printk("\nError in selection of reload value\n");
+ return -EINVAL;
+ } //else if ((data[3]>= 0) && (data[3] <= 65535))
+ i_TimerCounterWatchdogInterrupt = data[7];
+ i_WatchdogCounter3Init = 1;
+ break;
+
+ default:
+ printk("\nThe specified counter\timer option does not exist\n");
+ } //switch(data[1])
+ i_CounterLogic = data[2];
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1500_StartStopTriggerTimerCounterWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s,
+ struct comedi_insn *insn,unsigned int *data); |
++----------------------------------------------------------------------------+
+| Task : Start / Stop or trigger the timer counter or Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
+ data[0] : 0 Counter1/Timer1
+ 1 Counter2/Timer2
+ 2 Counter3/Watchdog
+ data[1] : 0 start
+ 1 stop
+ 2 Trigger
+ data[2] : 0 Counter
+ 1 Timer/Watchdog
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI1500_StartStopTriggerTimerCounterWatchdog(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ int i_CommandAndStatusValue;
+
+ switch (data[0]) {
+ case COUNTER1:
+ switch (data[1]) {
+ case START:
+ if (i_TimerCounter1Init == 1) {
+ if (i_TimerCounterWatchdogInterrupt == 1) {
+ i_CommandAndStatusValue = 0xC4; //Enable the interrupt
+ } // if(i_TimerCounterWatchdogInterrupt==1)
+ else {
+ i_CommandAndStatusValue = 0xE4; //disable the interrupt
+ } //elseif(i_TimerCounterWatchdogInterrupt==1)
+ /**************************/
+ /* Starts timer/counter 1 */
+ /**************************/
+ i_TimerCounter1Enabled = 1;
+ /********************************************/
+ /* Selects the commands and status register */
+ /********************************************/
+ outb(APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_CommandAndStatusValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ } //if( i_TimerCounter1Init==1)
+ else {
+ printk("\nCounter/Timer1 not configured\n");
+ return -EINVAL;
+ }
+ break;
+
+ case STOP:
+
+ /**************************/
+ /* Stop timer/counter 1 */
+ /**************************/
+
+ /********************************************/
+ /* Selects the commands and status register */
+ /********************************************/
+ outb(APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0x00,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_TimerCounter1Enabled = 0;
+ break;
+
+ case TRIGGER:
+ if (i_TimerCounter1Init == 1) {
+ if (i_TimerCounter1Enabled == 1) {
+ /************************/
+ /* Set Trigger and gate */
+ /************************/
+
+ i_CommandAndStatusValue = 0x6;
+ } //if( i_TimerCounter1Enabled==1)
+ else {
+ /***************/
+ /* Set Trigger */
+ /***************/
+
+ i_CommandAndStatusValue = 0x2;
+ } //elseif(i_TimerCounter1Enabled==1)
+
+ /********************************************/
+ /* Selects the commands and status register */
+ /********************************************/
+ outb(APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_CommandAndStatusValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ } //if( i_TimerCounter1Init==1)
+ else {
+ printk("\nCounter/Timer1 not configured\n");
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ printk("\nThe specified option for start/stop/trigger does not exist\n");
+ return -EINVAL;
+ } //switch(data[1])
+ break;
+
+ case COUNTER2:
+ switch (data[1]) {
+ case START:
+ if (i_TimerCounter2Init == 1) {
+ if (i_TimerCounterWatchdogInterrupt == 1) {
+ i_CommandAndStatusValue = 0xC4; //Enable the interrupt
+ } // if(i_TimerCounterWatchdogInterrupt==1)
+ else {
+ i_CommandAndStatusValue = 0xE4; //disable the interrupt
+ } //elseif(i_TimerCounterWatchdogInterrupt==1)
+ /**************************/
+ /* Starts timer/counter 2 */
+ /**************************/
+ i_TimerCounter2Enabled = 1;
+ /********************************************/
+ /* Selects the commands and status register */
+ /********************************************/
+ outb(APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_CommandAndStatusValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ } //if( i_TimerCounter2Init==1)
+ else {
+ printk("\nCounter/Timer2 not configured\n");
+ return -EINVAL;
+ }
+ break;
+
+ case STOP:
+
+ /**************************/
+ /* Stop timer/counter 2 */
+ /**************************/
+
+ /********************************************/
+ /* Selects the commands and status register */
+ /********************************************/
+ outb(APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0x00,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_TimerCounter2Enabled = 0;
+ break;
+ case TRIGGER:
+ if (i_TimerCounter2Init == 1) {
+ if (i_TimerCounter2Enabled == 1) {
+ /************************/
+ /* Set Trigger and gate */
+ /************************/
+
+ i_CommandAndStatusValue = 0x6;
+ } //if( i_TimerCounter2Enabled==1)
+ else {
+ /***************/
+ /* Set Trigger */
+ /***************/
+
+ i_CommandAndStatusValue = 0x2;
+ } //elseif(i_TimerCounter2Enabled==1)
+
+ /********************************************/
+ /* Selects the commands and status register */
+ /********************************************/
+ outb(APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_CommandAndStatusValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ } //if( i_TimerCounter2Init==1)
+ else {
+ printk("\nCounter/Timer2 not configured\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ printk("\nThe specified option for start/stop/trigger does not exist\n");
+ return -EINVAL;
+ } //switch(data[1])
+ break;
+ case COUNTER3:
+ switch (data[1]) {
+ case START:
+ if (i_WatchdogCounter3Init == 1) {
+
+ if (i_TimerCounterWatchdogInterrupt == 1) {
+ i_CommandAndStatusValue = 0xC4; //Enable the interrupt
+ } // if(i_TimerCounterWatchdogInterrupt==1)
+ else {
+ i_CommandAndStatusValue = 0xE4; //disable the interrupt
+ } //elseif(i_TimerCounterWatchdogInterrupt==1)
+ /**************************/
+ /* Starts Watchdog/counter 3 */
+ /**************************/
+ i_WatchdogCounter3Enabled = 1;
+ /********************************************/
+ /* Selects the commands and status register */
+ /********************************************/
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_CommandAndStatusValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ } // if( i_WatchdogCounter3init==1)
+ else {
+ printk("\nWatchdog/Counter3 not configured\n");
+ return -EINVAL;
+ }
+ break;
+
+ case STOP:
+
+ /**************************/
+ /* Stop Watchdog/counter 3 */
+ /**************************/
+
+ /********************************************/
+ /* Selects the commands and status register */
+ /********************************************/
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0x00,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_WatchdogCounter3Enabled = 0;
+ break;
+
+ case TRIGGER:
+ switch (data[2]) {
+ case 0: //triggering counter 3
+ if (i_WatchdogCounter3Init == 1) {
+ if (i_WatchdogCounter3Enabled == 1) {
+ /************************/
+ /* Set Trigger and gate */
+ /************************/
+
+ i_CommandAndStatusValue = 0x6;
+ } //if( i_WatchdogCounter3Enabled==1)
+ else {
+ /***************/
+ /* Set Trigger */
+ /***************/
+
+ i_CommandAndStatusValue = 0x2;
+ } //elseif(i_WatchdogCounter3Enabled==1)
+
+ /********************************************/
+ /* Selects the commands and status register */
+ /********************************************/
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_CommandAndStatusValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ } //if( i_WatchdogCounter3Init==1)
+ else {
+ printk("\nCounter3 not configured\n");
+ return -EINVAL;
+ }
+ break;
+ case 1:
+ //triggering Watchdog 3
+ if (i_WatchdogCounter3Init == 1) {
+
+ /********************************************/
+ /* Selects the commands and status register */
+ /********************************************/
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0x6,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ } //if( i_WatchdogCounter3Init==1)
+ else {
+ printk("\nWatchdog 3 not configured\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ printk("\nWrong choice of watchdog/counter3\n");
+ return -EINVAL;
+ } //switch(data[2])
+ break;
+ default:
+ printk("\nThe specified option for start/stop/trigger does not exist\n");
+ return -EINVAL;
+ } //switch(data[1])
+ break;
+ default:
+ printk("\nThe specified choice for counter/watchdog/timer does not exist\n");
+ return -EINVAL;
+ } //switch(data[0])
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1500_ReadCounterTimerWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,
+ unsigned int *data); |
++----------------------------------------------------------------------------+
+| Task : Read The Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
+ data[0] : 0 Counter1/Timer1
+ 1 Counter2/Timer2
+ 2 Counter3/Watchdog
+
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI1500_ReadCounterTimerWatchdog(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ int i_CommandAndStatusValue;
+ switch (data[0]) {
+ case COUNTER1:
+ //Read counter/timer1
+ if (i_TimerCounter1Init == 1) {
+ if (i_TimerCounter1Enabled == 1) {
+ /************************/
+ /* Set RCC and gate */
+ /************************/
+
+ i_CommandAndStatusValue = 0xC;
+ } //if( i_TimerCounter1Init==1)
+ else {
+ /***************/
+ /* Set RCC */
+ /***************/
+
+ i_CommandAndStatusValue = 0x8;
+ } //elseif(i_TimerCounter1Init==1)
+
+ /********************************************/
+ /* Selects the commands and status register */
+ /********************************************/
+ outb(APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_CommandAndStatusValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /***************************************/
+ /* Selects the counter register (high) */
+ /***************************************/
+ outb(APCI1500_R_CPT_TMR1_VALUE_HIGH,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ data[0] =
+ inb(devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ data[0] = data[0] << 8;
+ data[0] = data[0] & 0xff00;
+ outb(APCI1500_R_CPT_TMR1_VALUE_LOW,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ data[0] =
+ data[0] | inb(devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ } //if( i_TimerCounter1Init==1)
+ else {
+ printk("\nTimer/Counter1 not configured\n");
+ return -EINVAL;
+ } //elseif( i_TimerCounter1Init==1)
+ break;
+ case COUNTER2:
+ //Read counter/timer2
+ if (i_TimerCounter2Init == 1) {
+ if (i_TimerCounter2Enabled == 1) {
+ /************************/
+ /* Set RCC and gate */
+ /************************/
+
+ i_CommandAndStatusValue = 0xC;
+ } //if( i_TimerCounter2Init==1)
+ else {
+ /***************/
+ /* Set RCC */
+ /***************/
+
+ i_CommandAndStatusValue = 0x8;
+ } //elseif(i_TimerCounter2Init==1)
+
+ /********************************************/
+ /* Selects the commands and status register */
+ /********************************************/
+ outb(APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_CommandAndStatusValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /***************************************/
+ /* Selects the counter register (high) */
+ /***************************************/
+ outb(APCI1500_R_CPT_TMR2_VALUE_HIGH,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ data[0] =
+ inb(devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ data[0] = data[0] << 8;
+ data[0] = data[0] & 0xff00;
+ outb(APCI1500_R_CPT_TMR2_VALUE_LOW,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ data[0] =
+ data[0] | inb(devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ } //if( i_TimerCounter2Init==1)
+ else {
+ printk("\nTimer/Counter2 not configured\n");
+ return -EINVAL;
+ } //elseif( i_TimerCounter2Init==1)
+ break;
+ case COUNTER3:
+ //Read counter/watchdog2
+ if (i_WatchdogCounter3Init == 1) {
+ if (i_WatchdogCounter3Enabled == 1) {
+ /************************/
+ /* Set RCC and gate */
+ /************************/
+
+ i_CommandAndStatusValue = 0xC;
+ } //if( i_TimerCounter2Init==1)
+ else {
+ /***************/
+ /* Set RCC */
+ /***************/
+
+ i_CommandAndStatusValue = 0x8;
+ } //elseif(i_WatchdogCounter3Init==1)
+
+ /********************************************/
+ /* Selects the commands and status register */
+ /********************************************/
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_CommandAndStatusValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /***************************************/
+ /* Selects the counter register (high) */
+ /***************************************/
+ outb(APCI1500_R_CPT_TMR3_VALUE_HIGH,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ data[0] =
+ inb(devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ data[0] = data[0] << 8;
+ data[0] = data[0] & 0xff00;
+ outb(APCI1500_R_CPT_TMR3_VALUE_LOW,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ data[0] =
+ data[0] | inb(devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ } //if( i_WatchdogCounter3Init==1)
+ else {
+ printk("\nWatchdogCounter3 not configured\n");
+ return -EINVAL;
+ } //elseif( i_WatchdogCounter3Init==1)
+ break;
+ default:
+ printk("\nThe choice of timer/counter/watchdog does not exist\n");
+ return -EINVAL;
+ } //switch(data[0])
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1500_ReadInterruptMask |
+| (struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,
+ unsigned int *data); |
++----------------------------------------------------------------------------+
+| Task : Read the interrupt mask |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
+
+
++----------------------------------------------------------------------------+
+| Output Parameters : -- data[0]:The interrupt mask value data[1]:Channel no
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI1500_ReadInterruptMask(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = i_InterruptMask;
+ data[1] = i_InputChannel;
+ i_InterruptMask = 0;
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1500_ConfigureInterrupt |
+| (struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,
+ unsigned int *data); |
++----------------------------------------------------------------------------+
+| Task : Configures the interrupt registers |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer |
+
+
++----------------------------------------------------------------------------+
+| Output Parameters : --
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI1500_ConfigureInterrupt(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Status;
+ int i_RegValue;
+ int i_Constant;
+ devpriv->tsk_Current = current;
+ outl(0x0, devpriv->i_IobaseAmcc + 0x38);
+ if (data[0] == 1) {
+ i_Constant = 0xC0;
+ } //if(data[0]==1)
+ else {
+ if (data[0] == 0) {
+ i_Constant = 0x00;
+ } //if{data[0]==0)
+ else {
+ printk("\nThe parameter passed to driver is in error for enabling the voltage interrupt\n");
+ return -EINVAL;
+ } //else if(data[0]==0)
+ } //elseif(data[0]==1)
+
+ /*****************************************************/
+ /* Selects the mode specification register of port B */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_B_SPECIFICATION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue = inb(devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(APCI1500_RW_PORT_B_SPECIFICATION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*********************************************/
+ /* Writes the new configuration (APCI1500_OR) */
+ /*********************************************/
+ i_RegValue = (i_RegValue & 0xF9) | APCI1500_OR;
+
+ outb(i_RegValue, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*****************************************************/
+ /* Selects the command and status register of port B */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_B_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*****************************************/
+ /* Authorises the interrupt on the board */
+ /*****************************************/
+ outb(0xC0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /***************************************************/
+ /* Selects the pattern polarity register of port B */
+ /***************************************************/
+ outb(APCI1500_RW_PORT_B_PATTERN_POLARITY,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_Constant, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*****************************************************/
+ /* Selects the pattern transition register of port B */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_B_PATTERN_TRANSITION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_Constant, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /***********************************************/
+ /* Selects the pattern mask register of port B */
+ /***********************************************/
+ outb(APCI1500_RW_PORT_B_PATTERN_MASK,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(i_Constant, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*****************************************************/
+ /* Selects the command and status register of port A */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_A_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue = inb(devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(APCI1500_RW_PORT_A_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /***********************************/
+ /* Deletes the interrupt of port A */
+ /***********************************/
+
+ i_RegValue = (i_RegValue & 0x0F) | 0x20;
+ outb(i_RegValue, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*****************************************************/
+ /* Selects the command and status register of port B */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_B_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue = inb(devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(APCI1500_RW_PORT_B_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /***********************************/
+ /* Deletes the interrupt of port B */
+ /***********************************/
+
+ i_RegValue = (i_RegValue & 0x0F) | 0x20;
+ outb(i_RegValue, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*****************************************************/
+ /* Selects the command and status register of timer 1 */
+ /*****************************************************/
+ outb(APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue = inb(devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /***********************************/
+ /* Deletes the interrupt of timer 1 */
+ /***********************************/
+
+ i_RegValue = (i_RegValue & 0x0F) | 0x20;
+ outb(i_RegValue, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*****************************************************/
+ /* Selects the command and status register of timer 2 */
+ /*****************************************************/
+ outb(APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue = inb(devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /***********************************/
+ /* Deletes the interrupt of timer 2 */
+ /***********************************/
+
+ i_RegValue = (i_RegValue & 0x0F) | 0x20;
+ outb(i_RegValue, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*****************************************************/
+ /* Selects the command and status register of timer 3 */
+ /*****************************************************/
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue = inb(devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /***********************************/
+ /* Deletes the interrupt of timer 3 */
+ /***********************************/
+
+ i_RegValue = (i_RegValue & 0x0F) | 0x20;
+ outb(i_RegValue, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*************************************************/
+ /* Selects the master interrupt control register */
+ /*************************************************/
+ outb(APCI1500_RW_MASTER_INTERRUPT_CONTROL,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /**********************************************/
+ /* Authorizes the main interrupt on the board */
+ /**********************************************/
+ outb(0xD0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /***************************/
+ /* Enables the PCI interrupt */
+ /*****************************/
+ outl(0x3000, devpriv->i_IobaseAmcc + 0x38);
+ ui_Status = inl(devpriv->i_IobaseAmcc + 0x10);
+ ui_Status = inl(devpriv->i_IobaseAmcc + 0x38);
+ outl(0x23000, devpriv->i_IobaseAmcc + 0x38);
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : static void v_APCI1500_Interrupt |
+| (int irq , void *d) |
++----------------------------------------------------------------------------+
+| Task : Interrupt handler |
++----------------------------------------------------------------------------+
+| Input Parameters : int irq : irq number |
+| void *d : void pointer |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+static void v_APCI1500_Interrupt(int irq, void *d)
+{
+
+ struct comedi_device *dev = d;
+ UINT ui_InterruptStatus = 0;
+ int i_RegValue = 0;
+ i_InterruptMask = 0;
+
+ /***********************************/
+ /* Read the board interrupt status */
+ /***********************************/
+ ui_InterruptStatus = inl(devpriv->i_IobaseAmcc + 0x38);
+
+ /***************************************/
+ /* Test if board generated a interrupt */
+ /***************************************/
+ if ((ui_InterruptStatus & 0x800000) == 0x800000) {
+ /************************/
+ /* Disable all Interrupt */
+ /************************/
+ /*************************************************/
+ /* Selects the master interrupt control register */
+ /*************************************************/
+ //outb(APCI1500_RW_MASTER_INTERRUPT_CONTROL,devpriv->iobase+APCI1500_Z8536_CONTROL_REGISTER);
+ /**********************************************/
+ /* Disables the main interrupt on the board */
+ /**********************************************/
+ //outb(0x00,devpriv->iobase+APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*****************************************************/
+ /* Selects the command and status register of port A */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_A_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue =
+ inb(devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ if ((i_RegValue & 0x60) == 0x60) {
+ /*****************************************************/
+ /* Selects the command and status register of port A */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_A_COMMAND_AND_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /***********************************/
+ /* Deletes the interrupt of port A */
+ /***********************************/
+ i_RegValue = (i_RegValue & 0x0F) | 0x20;
+ outb(i_RegValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_InterruptMask = i_InterruptMask | 1;
+ if (i_Logic == APCI1500_OR_PRIORITY) {
+ outb(APCI1500_RW_PORT_A_SPECIFICATION,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue =
+ inb(devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ /***************************************************/
+ /* Selects the interrupt vector register of port A */
+ /***************************************************/
+ outb(APCI1500_RW_PORT_A_INTERRUPT_CONTROL,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue =
+ inb(devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+
+ i_InputChannel = 1 + (i_RegValue >> 1);
+
+ } // if(i_Logic==APCI1500_OR_PRIORITY)
+ else {
+ i_InputChannel = 0;
+ } //elseif(i_Logic==APCI1500_OR_PRIORITY)
+ } // if ((i_RegValue & 0x60) == 0x60)
+
+ /*****************************************************/
+ /* Selects the command and status register of port B */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_B_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue =
+ inb(devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ if ((i_RegValue & 0x60) == 0x60) {
+ /*****************************************************/
+ /* Selects the command and status register of port B */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_B_COMMAND_AND_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /***********************************/
+ /* Deletes the interrupt of port B */
+ /***********************************/
+ i_RegValue = (i_RegValue & 0x0F) | 0x20;
+ outb(i_RegValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ printk("\n\n\n");
+ /****************/
+ /* Reads port B */
+ /****************/
+ i_RegValue =
+ inb((UINT) devpriv->iobase +
+ APCI1500_Z8536_PORT_B);
+
+ i_RegValue = i_RegValue & 0xC0;
+ /**************************************/
+ /* Tests if this is an external error */
+ /**************************************/
+
+ if (i_RegValue) {
+ //Disable the interrupt
+ /*****************************************************/
+ /* Selects the command and status register of port B */
+ /*****************************************************/
+ outl(0x0, devpriv->i_IobaseAmcc + 0x38);
+
+ if (i_RegValue & 0x80) {
+ i_InterruptMask =
+ i_InterruptMask | 0x40;
+ } //if (i_RegValue & 0x80)
+
+ if (i_RegValue & 0x40) {
+ i_InterruptMask =
+ i_InterruptMask | 0x80;
+ } //if (i_RegValue & 0x40)
+ } // if (i_RegValue)
+ else {
+ i_InterruptMask = i_InterruptMask | 2;
+ } // if (i_RegValue)
+ } //if ((i_RegValue & 0x60) == 0x60)
+
+ /*****************************************************/
+ /* Selects the command and status register of timer 1 */
+ /*****************************************************/
+ outb(APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue =
+ inb(devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ if ((i_RegValue & 0x60) == 0x60) {
+ /*****************************************************/
+ /* Selects the command and status register of timer 1 */
+ /*****************************************************/
+ outb(APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /***********************************/
+ /* Deletes the interrupt of timer 1 */
+ /***********************************/
+ i_RegValue = (i_RegValue & 0x0F) | 0x20;
+ outb(i_RegValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_InterruptMask = i_InterruptMask | 4;
+ } // if ((i_RegValue & 0x60) == 0x60)
+ /*****************************************************/
+ /* Selects the command and status register of timer 2 */
+ /*****************************************************/
+ outb(APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue =
+ inb(devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ if ((i_RegValue & 0x60) == 0x60) {
+ /*****************************************************/
+ /* Selects the command and status register of timer 2 */
+ /*****************************************************/
+ outb(APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /***********************************/
+ /* Deletes the interrupt of timer 2 */
+ /***********************************/
+ i_RegValue = (i_RegValue & 0x0F) | 0x20;
+ outb(i_RegValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ i_InterruptMask = i_InterruptMask | 8;
+ } // if ((i_RegValue & 0x60) == 0x60)
+
+ /*****************************************************/
+ /* Selects the command and status register of timer 3 */
+ /*****************************************************/
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ i_RegValue =
+ inb(devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ if ((i_RegValue & 0x60) == 0x60) {
+ /*****************************************************/
+ /* Selects the command and status register of timer 3 */
+ /*****************************************************/
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ /***********************************/
+ /* Deletes the interrupt of timer 3 */
+ /***********************************/
+ i_RegValue = (i_RegValue & 0x0F) | 0x20;
+ outb(i_RegValue,
+ devpriv->iobase +
+ APCI1500_Z8536_CONTROL_REGISTER);
+ if (i_CounterLogic == APCI1500_COUNTER) {
+ i_InterruptMask = i_InterruptMask | 0x10;
+ } //if(i_CounterLogic==APCI1500_COUNTER)
+ else {
+ i_InterruptMask = i_InterruptMask | 0x20;
+ }
+ } // if ((i_RegValue & 0x60) == 0x60)
+
+ send_sig(SIGIO, devpriv->tsk_Current, 0); // send signal to the sample
+ /***********************/
+ /* Enable all Interrupts */
+ /***********************/
+
+ /*************************************************/
+ /* Selects the master interrupt control register */
+ /*************************************************/
+ outb(APCI1500_RW_MASTER_INTERRUPT_CONTROL,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /**********************************************/
+ /* Authorizes the main interrupt on the board */
+ /**********************************************/
+ outb(0xD0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ } // if ((ui_InterruptStatus & 0x800000) == 0x800000)
+ else {
+ printk("\nInterrupt from unknown source\n");
+
+ } //else if ((ui_InterruptStatus & 0x800000) == 0x800000)
+ return;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1500_Reset(struct comedi_device *dev) | |
++----------------------------------------------------------------------------+
+| Task :resets all the registers |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1500_Reset(struct comedi_device * dev)
+{
+ int i_DummyRead = 0;
+ i_TimerCounter1Init = 0;
+ i_TimerCounter2Init = 0;
+ i_WatchdogCounter3Init = 0;
+ i_Event1Status = 0;
+ i_Event2Status = 0;
+ i_TimerCounterWatchdogInterrupt = 0;
+ i_Logic = 0;
+ i_CounterLogic = 0;
+ i_InterruptMask = 0;
+ i_InputChannel = 0;;
+ i_TimerCounter1Enabled = 0;
+ i_TimerCounter2Enabled = 0;
+ i_WatchdogCounter3Enabled = 0;
+
+ /******************/
+ /* Software reset */
+ /******************/
+ i_DummyRead = inb(devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ i_DummyRead = inb(devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(1, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*****************************************************/
+ /* Selects the master configuration control register */
+ /*****************************************************/
+ outb(APCI1500_RW_MASTER_CONFIGURATION_CONTROL,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0xF4, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*****************************************************/
+ /* Selects the mode specification register of port A */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_A_SPECIFICATION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0x10, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /* Selects the data path polarity register of port A */
+ outb(APCI1500_RW_PORT_A_DATA_PCITCH_POLARITY,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* High level of port A means 1 */
+ outb(0xFF, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /* Selects the data direction register of port A */
+ outb(APCI1500_RW_PORT_A_DATA_DIRECTION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* All bits used as inputs */
+ outb(0xFF, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the command and status register of port A */
+ outb(APCI1500_RW_PORT_A_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes IP and IUS */
+ outb(0x20, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the command and status register of port A */
+ outb(APCI1500_RW_PORT_A_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deactivates the interrupt management of port A: */
+ outb(0xE0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the handshake specification register of port A */
+ outb(APCI1500_RW_PORT_A_HANDSHAKE_SPECIFICATION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes the register */
+ outb(0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*****************************************************/
+ /* Selects the mode specification register of port B */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_B_SPECIFICATION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ outb(0x10, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the data path polarity register of port B */
+ outb(APCI1500_RW_PORT_B_DATA_PCITCH_POLARITY,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* A high level of port B means 1 */
+ outb(0x7F, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the data direction register of port B */
+ outb(APCI1500_RW_PORT_B_DATA_DIRECTION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* All bits used as inputs */
+ outb(0xFF, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the command and status register of port B */
+ outb(APCI1500_RW_PORT_B_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes IP and IUS */
+ outb(0x20, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the command and status register of port B */
+ outb(APCI1500_RW_PORT_B_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deactivates the interrupt management of port B: */
+ outb(0xE0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the handshake specification register of port B */
+ outb(APCI1500_RW_PORT_B_HANDSHAKE_SPECIFICATION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes the register */
+ outb(0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+
+ /*****************************************************/
+ /* Selects the data path polarity register of port C */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_C_DATA_PCITCH_POLARITY,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* High level of port C means 1 */
+ outb(0x9, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the data direction register of port C */
+ outb(APCI1500_RW_PORT_C_DATA_DIRECTION,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* All bits used as inputs except channel 1 */
+ outb(0x0E, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the special IO register of port C */
+ outb(APCI1500_RW_PORT_C_SPECIAL_IO_CONTROL,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes it */
+ outb(0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /******************************************************/
+ /* Selects the command and status register of timer 1 */
+ /******************************************************/
+ outb(APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes IP and IUS */
+ outb(0x20, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the command and status register of timer 1 */
+ outb(APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deactivates the interrupt management of timer 1 */
+ outb(0xE0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /******************************************************/
+ /* Selects the command and status register of timer 2 */
+ /******************************************************/
+ outb(APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes IP and IUS */
+ outb(0x20, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the command and status register of timer 2 */
+ outb(APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deactivates Timer 2 interrupt management: */
+ outb(0xE0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /******************************************************/
+ /* Selects the command and status register of timer 3 */
+ /******************************************************/
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes IP and IUS */
+ outb(0x20, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Selects the command and status register of Timer 3 */
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deactivates interrupt management of timer 3: */
+ outb(0xE0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*************************************************/
+ /* Selects the master interrupt control register */
+ /*************************************************/
+ outb(APCI1500_RW_MASTER_INTERRUPT_CONTROL,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /* Deletes all interrupts */
+ outb(0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ //reset all the digital outputs
+ outw(0x0, devpriv->i_IobaseAddon + APCI1500_DIGITAL_OP);
+/*******************************/
+/* Disable the board interrupt */
+/*******************************/
+ /*************************************************/
+ /* Selects the master interrupt control register */
+ /*************************************************/
+ outb(APCI1500_RW_MASTER_INTERRUPT_CONTROL,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+/****************************/
+/* Deactivates all interrupts */
+/******************************/
+ outb(0, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ /*****************************************************/
+ /* Selects the command and status register of port A */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_A_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+/****************************/
+/* Deactivates all interrupts */
+/******************************/
+ outb(0x00, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+/*****************************************************/
+ /* Selects the command and status register of port B */
+ /*****************************************************/
+ outb(APCI1500_RW_PORT_B_COMMAND_AND_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+/****************************/
+/* Deactivates all interrupts */
+/******************************/
+ outb(0x00, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+/*****************************************************/
+ /* Selects the command and status register of timer 1 */
+ /*****************************************************/
+ outb(APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+/****************************/
+/* Deactivates all interrupts */
+/******************************/
+ outb(0x00, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+/*****************************************************/
+ /* Selects the command and status register of timer 2 */
+ /*****************************************************/
+ outb(APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+/****************************/
+/* Deactivates all interrupts */
+/******************************/
+ outb(0x00, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+/*****************************************************/
+/* Selects the command and status register of timer 3*/
+/*****************************************************/
+ outb(APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+/****************************/
+/* Deactivates all interrupts */
+/******************************/
+ outb(0x00, devpriv->iobase + APCI1500_Z8536_CONTROL_REGISTER);
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1500.h b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1500.h
new file mode 100644
index 000000000000..5d960b40a6a8
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1500.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+/********* Definitions for APCI-1500 card *****/
+
+// Card Specific information
+#define APCI1500_BOARD_VENDOR_ID 0x10e8
+#define APCI1500_ADDRESS_RANGE 4
+
+//DIGITAL INPUT-OUTPUT DEFINE
+
+#define APCI1500_DIGITAL_OP 2
+#define APCI1500_DIGITAL_IP 0
+#define APCI1500_AND 2
+#define APCI1500_OR 4
+#define APCI1500_OR_PRIORITY 6
+#define APCI1500_CLK_SELECT 0
+#define COUNTER1 0
+#define COUNTER2 1
+#define COUNTER3 2
+#define APCI1500_COUNTER 0x20
+#define APCI1500_TIMER 0
+#define APCI1500_WATCHDOG 0
+#define APCI1500_SINGLE 0
+#define APCI1500_CONTINUOUS 0x80
+#define APCI1500_DISABLE 0
+#define APCI1500_ENABLE 1
+#define APCI1500_SOFTWARE_TRIGGER 0x4
+#define APCI1500_HARDWARE_TRIGGER 0x10
+#define APCI1500_SOFTWARE_GATE 0
+#define APCI1500_HARDWARE_GATE 0x8
+#define START 0
+#define STOP 1
+#define TRIGGER 2
+
+/*
+ * Zillog I/O enumeration
+ */
+enum {
+ APCI1500_Z8536_PORT_C,
+ APCI1500_Z8536_PORT_B,
+ APCI1500_Z8536_PORT_A,
+ APCI1500_Z8536_CONTROL_REGISTER
+};
+
+/*
+ * Z8536 CIO Internal Address
+ */
+enum {
+ APCI1500_RW_MASTER_INTERRUPT_CONTROL,
+ APCI1500_RW_MASTER_CONFIGURATION_CONTROL,
+ APCI1500_RW_PORT_A_INTERRUPT_CONTROL,
+ APCI1500_RW_PORT_B_INTERRUPT_CONTROL,
+ APCI1500_RW_TIMER_COUNTER_INTERRUPT_VECTOR,
+ APCI1500_RW_PORT_C_DATA_PCITCH_POLARITY,
+ APCI1500_RW_PORT_C_DATA_DIRECTION,
+ APCI1500_RW_PORT_C_SPECIAL_IO_CONTROL,
+
+ APCI1500_RW_PORT_A_COMMAND_AND_STATUS,
+ APCI1500_RW_PORT_B_COMMAND_AND_STATUS,
+ APCI1500_RW_CPT_TMR1_CMD_STATUS,
+ APCI1500_RW_CPT_TMR2_CMD_STATUS,
+ APCI1500_RW_CPT_TMR3_CMD_STATUS,
+ APCI1500_RW_PORT_A_DATA,
+ APCI1500_RW_PORT_B_DATA,
+ APCI1500_RW_PORT_C_DATA,
+
+ APCI1500_R_CPT_TMR1_VALUE_HIGH,
+ APCI1500_R_CPT_TMR1_VALUE_LOW,
+ APCI1500_R_CPT_TMR2_VALUE_HIGH,
+ APCI1500_R_CPT_TMR2_VALUE_LOW,
+ APCI1500_R_CPT_TMR3_VALUE_HIGH,
+ APCI1500_R_CPT_TMR3_VALUE_LOW,
+ APCI1500_RW_CPT_TMR1_TIME_CST_HIGH,
+ APCI1500_RW_CPT_TMR1_TIME_CST_LOW,
+ APCI1500_RW_CPT_TMR2_TIME_CST_HIGH,
+ APCI1500_RW_CPT_TMR2_TIME_CST_LOW,
+ APCI1500_RW_CPT_TMR3_TIME_CST_HIGH,
+ APCI1500_RW_CPT_TMR3_TIME_CST_LOW,
+ APCI1500_RW_CPT_TMR1_MODE_SPECIFICATION,
+ APCI1500_RW_CPT_TMR2_MODE_SPECIFICATION,
+ APCI1500_RW_CPT_TMR3_MODE_SPECIFICATION,
+ APCI1500_R_CURRENT_VECTOR,
+
+ APCI1500_RW_PORT_A_SPECIFICATION,
+ APCI1500_RW_PORT_A_HANDSHAKE_SPECIFICATION,
+ APCI1500_RW_PORT_A_DATA_PCITCH_POLARITY,
+ APCI1500_RW_PORT_A_DATA_DIRECTION,
+ APCI1500_RW_PORT_A_SPECIAL_IO_CONTROL,
+ APCI1500_RW_PORT_A_PATTERN_POLARITY,
+ APCI1500_RW_PORT_A_PATTERN_TRANSITION,
+ APCI1500_RW_PORT_A_PATTERN_MASK,
+
+ APCI1500_RW_PORT_B_SPECIFICATION,
+ APCI1500_RW_PORT_B_HANDSHAKE_SPECIFICATION,
+ APCI1500_RW_PORT_B_DATA_PCITCH_POLARITY,
+ APCI1500_RW_PORT_B_DATA_DIRECTION,
+ APCI1500_RW_PORT_B_SPECIAL_IO_CONTROL,
+ APCI1500_RW_PORT_B_PATTERN_POLARITY,
+ APCI1500_RW_PORT_B_PATTERN_TRANSITION,
+ APCI1500_RW_PORT_B_PATTERN_MASK
+};
+
+ /*----------DIGITAL INPUT----------------*/
+static int i_APCI1500_Initialisation(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int i_APCI1500_ConfigDigitalInputEvent(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+
+static int i_APCI1500_StartStopInputEvent(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int i_APCI1500_ReadMoreDigitalInput(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+/*---------- DIGITAL OUTPUT------------*/
+static int i_APCI1500_ConfigDigitalOutputErrorInterrupt(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+static int i_APCI1500_WriteDigitalOutput(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+/*----------TIMER----------------*/
+static int i_APCI1500_ConfigCounterTimerWatchdog(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+static int i_APCI1500_StartStopTriggerTimerCounterWatchdog(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+static int i_APCI1500_ReadCounterTimerWatchdog(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+static int i_APCI1500_ReadInterruptMask(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+/*----------INTERRUPT HANDLER------*/
+static void v_APCI1500_Interrupt(int irq, void *d);
+static int i_APCI1500_ConfigureInterrupt(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+/*----------RESET---------------*/
+static int i_APCI1500_Reset(struct comedi_device *dev);
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1516.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1516.c
new file mode 100644
index 000000000000..5ae9a93844e7
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1516.c
@@ -0,0 +1,542 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-------------------------------+---------------------------------------+
+ | Project : APCI-1516 | Compiler : GCC |
+ | Module name : hwdrv_apci1516.c| Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-------------------------------+---------------------------------------+
+ | Description : Hardware Layer Acces For APCI-1516 |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +----------+-----------+------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | | | |
+ | | | |
+ | | | |
+ +----------+-----------+------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+#include "hwdrv_apci1516.h"
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1516_Read1DigitalInput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Return the status of the digital input |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI1516_Read1DigitalInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_TmpValue = 0;
+ UINT ui_Channel;
+ ui_Channel = CR_CHAN(insn->chanspec);
+ if (ui_Channel >= 0 && ui_Channel <= 7) {
+ ui_TmpValue = (UINT) inw(devpriv->iobase + APCI1516_DIGITAL_IP);
+ // since only 1 channel reqd to bring it to last bit it is rotated
+ // 8 +(chan - 1) times then ANDed with 1 for last bit.
+ *data = (ui_TmpValue >> ui_Channel) & 0x1;
+ } //if(ui_Channel >= 0 && ui_Channel <=7)
+ else {
+ //comedi_error(dev," \n chan spec wrong\n");
+ return -EINVAL; // "sorry channel spec wrong "
+ } //else if(ui_Channel >= 0 && ui_Channel <=7)
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1516_ReadMoreDigitalInput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Return the status of the Requested digital inputs |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1516_ReadMoreDigitalInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ UINT ui_PortValue = data[0];
+ UINT ui_Mask = 0;
+ UINT ui_NoOfChannels;
+
+ ui_NoOfChannels = CR_CHAN(insn->chanspec);
+
+ *data = (UINT) inw(devpriv->iobase + APCI1516_DIGITAL_IP);
+ switch (ui_NoOfChannels) {
+ case 2:
+ ui_Mask = 3;
+ *data = (*data >> (2 * ui_PortValue)) & ui_Mask;
+ break;
+ case 4:
+ ui_Mask = 15;
+ *data = (*data >> (4 * ui_PortValue)) & ui_Mask;
+ break;
+ case 7:
+ break;
+
+ default:
+ printk("\nWrong parameters\n");
+ return -EINVAL; // "sorry channel spec wrong "
+ break;
+ } //switch(ui_NoOfChannels)
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1516_ConfigDigitalOutput (struct comedi_device *dev,
+ struct comedi_subdevice *s struct comedi_insn *insn,unsigned int *data) |
+| |
++----------------------------------------------------------------------------+
+| Task : Configures The Digital Output Subdevice. |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| unsigned int *data : Data Pointer contains |
+| configuration parameters as below |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| data[0] :1:Memory on |
+| 0:Memory off |
+| |
+| |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI1516_ConfigDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ devpriv->b_OutputMemoryStatus = data[0];
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1516_WriteDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,
+ unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Writes port value To the selected port |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1516_WriteDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Temp, ui_Temp1;
+ UINT ui_NoOfChannel = CR_CHAN(insn->chanspec); // get the channel
+
+ printk("EL311003 : @=%x\n", devpriv->iobase + APCI1516_DIGITAL_OP);
+
+ if (devpriv->b_OutputMemoryStatus) {
+ ui_Temp = inw(devpriv->iobase + APCI1516_DIGITAL_OP);
+
+ } //if(devpriv->b_OutputMemoryStatus )
+ else {
+ ui_Temp = 0;
+ } //if(devpriv->b_OutputMemoryStatus )
+ if (data[3] == 0) {
+ if (data[1] == 0) {
+ data[0] = (data[0] << ui_NoOfChannel) | ui_Temp;
+ outw(data[0], devpriv->iobase + APCI1516_DIGITAL_OP);
+
+ printk("EL311003 : d=%d @=%x\n", data[0],
+ devpriv->iobase + APCI1516_DIGITAL_OP);
+
+ } //if(data[1]==0)
+ else {
+ if (data[1] == 1) {
+ switch (ui_NoOfChannel) {
+
+ case 2:
+ data[0] =
+ (data[0] << (2 *
+ data[2])) | ui_Temp;
+ break;
+
+ case 4:
+ data[0] =
+ (data[0] << (4 *
+ data[2])) | ui_Temp;
+ break;
+
+ case 7:
+ data[0] = data[0] | ui_Temp;
+ break;
+
+ default:
+ comedi_error(dev, " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+
+ } //switch(ui_NoOfChannels)
+
+ outw(data[0],
+ devpriv->iobase + APCI1516_DIGITAL_OP);
+
+ printk("EL311003 : d=%d @=%x\n", data[0],
+ devpriv->iobase + APCI1516_DIGITAL_OP);
+ } // if(data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } //else if(data[1]==1)
+ } //elseif(data[1]==0)
+ } //if(data[3]==0)
+ else {
+ if (data[3] == 1) {
+ if (data[1] == 0) {
+ data[0] = ~data[0] & 0x1;
+ ui_Temp1 = 1;
+ ui_Temp1 = ui_Temp1 << ui_NoOfChannel;
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] = (data[0] << ui_NoOfChannel) ^ 0xff;
+ data[0] = data[0] & ui_Temp;
+ outw(data[0],
+ devpriv->iobase + APCI1516_DIGITAL_OP);
+
+ printk("EL311003 : d=%d @=%x\n", data[0],
+ devpriv->iobase + APCI1516_DIGITAL_OP);
+
+ } //if(data[1]==0)
+ else {
+ if (data[1] == 1) {
+ switch (ui_NoOfChannel) {
+
+ case 2:
+ data[0] = ~data[0] & 0x3;
+ ui_Temp1 = 3;
+ ui_Temp1 =
+ ui_Temp1 << 2 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (2 *
+ data
+ [2])) ^
+ 0xff) & ui_Temp;
+ break;
+
+ case 4:
+ data[0] = ~data[0] & 0xf;
+ ui_Temp1 = 15;
+ ui_Temp1 =
+ ui_Temp1 << 4 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (4 *
+ data
+ [2])) ^
+ 0xff) & ui_Temp;
+ break;
+
+ case 7:
+ break;
+
+ default:
+ comedi_error(dev,
+ " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+
+ } //switch(ui_NoOfChannels)
+
+ outw(data[0],
+ devpriv->iobase +
+ APCI1516_DIGITAL_OP);
+
+ printk("EL311003 : d=%d @=%x\n",
+ data[0],
+ devpriv->iobase +
+ APCI1516_DIGITAL_OP);
+ } // if(data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } //else if(data[1]==1)
+ } //elseif(data[1]==0)
+ } //if(data[3]==1);
+ else {
+ printk("\nSpecified functionality does not exist\n");
+ return -EINVAL;
+ } //if else data[3]==1)
+ } //if else data[3]==0)
+ return (insn->n);;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1516_ReadDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,
+ unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read value of the selected channel or port |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1516_ReadDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ UINT ui_Temp;
+ UINT ui_NoOfChannel = CR_CHAN(insn->chanspec); // get the channel
+ ui_Temp = data[0];
+ *data = inw(devpriv->iobase + APCI1516_DIGITAL_OP_RW);
+ if (ui_Temp == 0) {
+ *data = (*data >> ui_NoOfChannel) & 0x1;
+ } //if(ui_Temp==0)
+ else {
+ if (ui_Temp == 1) {
+ switch (ui_NoOfChannel) {
+
+ case 2:
+ *data = (*data >> (2 * data[1])) & 3;
+ break;
+
+ case 4:
+ *data = (*data >> (4 * data[1])) & 15;
+ break;
+
+ case 7:
+ break;
+
+ default:
+ comedi_error(dev, " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+
+ } //switch(ui_NoOfChannels)
+ } //if(ui_Temp==1)
+ else {
+ printk("\nSpecified channel not supported \n");
+ } //elseif(ui_Temp==1)
+ } //elseif(ui_Temp==0)
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1516_ConfigWatchdog(struct comedi_device *dev,
+ struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) |
+| |
++----------------------------------------------------------------------------+
+| Task : Configures The Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI1516_ConfigWatchdog(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (data[0] == 0) {
+ //Disable the watchdog
+ outw(0x0,
+ devpriv->i_IobaseAddon +
+ APCI1516_WATCHDOG_ENABLEDISABLE);
+ //Loading the Reload value
+ outw(data[1],
+ devpriv->i_IobaseAddon +
+ APCI1516_WATCHDOG_RELOAD_VALUE);
+ data[1] = data[1] >> 16;
+ outw(data[1],
+ devpriv->i_IobaseAddon +
+ APCI1516_WATCHDOG_RELOAD_VALUE + 2);
+ } //if(data[0]==0)
+ else {
+ printk("\nThe input parameters are wrong\n");
+ return -EINVAL;
+ } //elseif(data[0]==0)
+
+ return insn->n;
+}
+
+ /*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI1516_StartStopWriteWatchdog |
+ | (struct comedi_device *dev,struct comedi_subdevice *s,
+ struct comedi_insn *insn,unsigned int *data); |
+ +----------------------------------------------------------------------------+
+ | Task : Start / Stop The Watchdog |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+ | unsigned int *data : Data Pointer to read status |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+ */
+
+int i_APCI1516_StartStopWriteWatchdog(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ switch (data[0]) {
+ case 0: //stop the watchdog
+ outw(0x0, devpriv->i_IobaseAddon + APCI1516_WATCHDOG_ENABLEDISABLE); //disable the watchdog
+ break;
+ case 1: //start the watchdog
+ outw(0x0001,
+ devpriv->i_IobaseAddon +
+ APCI1516_WATCHDOG_ENABLEDISABLE);
+ break;
+ case 2: //Software trigger
+ outw(0x0201,
+ devpriv->i_IobaseAddon +
+ APCI1516_WATCHDOG_ENABLEDISABLE);
+ break;
+ default:
+ printk("\nSpecified functionality does not exist\n");
+ return -EINVAL;
+ } // switch(data[0])
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1516_ReadWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,
+ unsigned int *data); |
++----------------------------------------------------------------------------+
+| Task : Read The Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI1516_ReadWatchdog(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = inw(devpriv->i_IobaseAddon + APCI1516_WATCHDOG_STATUS) & 0x1;
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1516_Reset(struct comedi_device *dev) | |
++----------------------------------------------------------------------------+
+| Task :resets all the registers |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1516_Reset(struct comedi_device * dev)
+{
+ outw(0x0, devpriv->iobase + APCI1516_DIGITAL_OP); //RESETS THE DIGITAL OUTPUTS
+ outw(0x0, devpriv->i_IobaseAddon + APCI1516_WATCHDOG_ENABLEDISABLE);
+ outw(0x0, devpriv->i_IobaseAddon + APCI1516_WATCHDOG_RELOAD_VALUE);
+ outw(0x0, devpriv->i_IobaseAddon + APCI1516_WATCHDOG_RELOAD_VALUE + 2);
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1516.h b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1516.h
new file mode 100644
index 000000000000..398baa04a163
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1516.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+/********* Definitions for APCI-1516 card *****/
+
+// Card Specific information
+#define APCI1516_BOARD_VENDOR_ID 0x15B8
+#define APCI1516_ADDRESS_RANGE 8
+
+//DIGITAL INPUT-OUTPUT DEFINE
+
+#define APCI1516_DIGITAL_OP 4
+#define APCI1516_DIGITAL_OP_RW 4
+#define APCI1516_DIGITAL_IP 0
+
+// TIMER COUNTER WATCHDOG DEFINES
+
+#define ADDIDATA_WATCHDOG 2
+#define APCI1516_DIGITAL_OP_WATCHDOG 0
+#define APCI1516_WATCHDOG_ENABLEDISABLE 12
+#define APCI1516_WATCHDOG_RELOAD_VALUE 4
+#define APCI1516_WATCHDOG_STATUS 16
+
+// Hardware Layer functions for Apci1516
+
+//Digital Input
+INT i_APCI1516_ReadMoreDigitalInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI1516_Read1DigitalInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+//Digital Output
+int i_APCI1516_ConfigDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI1516_WriteDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI1516_ReadDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+// TIMER
+// timer value is passed as u seconds
+int i_APCI1516_ConfigWatchdog(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI1516_StartStopWriteWatchdog(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI1516_ReadWatchdog(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+//reset
+INT i_APCI1516_Reset(struct comedi_device *dev);
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1564.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1564.c
new file mode 100644
index 000000000000..a6b504c3d391
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1564.c
@@ -0,0 +1,1105 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-------------------------------+---------------------------------------+
+ | Project : APCI-1564 | Compiler : GCC |
+ | Module name : hwdrv_apci1564.c| Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-------------------------------+---------------------------------------+
+ | Description : Hardware Layer Acces For APCI-1564 |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +----------+-----------+------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | | | |
+ | | | |
+ | | | |
+ +----------+-----------+------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+
+#include <linux/delay.h>
+#include "hwdrv_apci1564.h"
+
+//Global variables
+UINT ui_InterruptStatus_1564 = 0;
+UINT ui_InterruptData, ui_Type;
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1564_ConfigDigitalInput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Configures the digital input Subdevice |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| unsigned int *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| data[0] : 1 Enable Digital Input Interrupt |
+| 0 Disable Digital Input Interrupt |
+| data[1] : 0 ADDIDATA Interrupt OR LOGIC |
+| : 1 ADDIDATA Interrupt AND LOGIC |
+| data[2] : Interrupt mask for the mode 1 |
+| data[3] : Interrupt mask for the mode 2 |
+| |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI1564_ConfigDigitalInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ devpriv->tsk_Current = current;
+ /*******************************/
+ /* Set the digital input logic */
+ /*******************************/
+ if (data[0] == ADDIDATA_ENABLE) {
+ data[2] = data[2] << 4;
+ data[3] = data[3] << 4;
+ outl(data[2],
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP +
+ APCI1564_DIGITAL_IP_INTERRUPT_MODE1);
+ outl(data[3],
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP +
+ APCI1564_DIGITAL_IP_INTERRUPT_MODE2);
+ if (data[1] == ADDIDATA_OR) {
+ outl(0x4,
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP +
+ APCI1564_DIGITAL_IP_IRQ);
+ } // if (data[1] == ADDIDATA_OR)
+ else {
+ outl(0x6,
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP +
+ APCI1564_DIGITAL_IP_IRQ);
+ } // else if (data[1] == ADDIDATA_OR)
+ } // if (data[0] == ADDIDATA_ENABLE)
+ else {
+ outl(0x0,
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP +
+ APCI1564_DIGITAL_IP_INTERRUPT_MODE1);
+ outl(0x0,
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP +
+ APCI1564_DIGITAL_IP_INTERRUPT_MODE2);
+ outl(0x0,
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP +
+ APCI1564_DIGITAL_IP_IRQ);
+ } // else if (data[0] == ADDIDATA_ENABLE)
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1564_Read1DigitalInput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Return the status of the digital input |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_Channel : Channel number to read |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI1564_Read1DigitalInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_TmpValue = 0;
+ UINT ui_Channel;
+
+ ui_Channel = CR_CHAN(insn->chanspec);
+ if (ui_Channel >= 0 && ui_Channel <= 31) {
+ ui_TmpValue =
+ (UINT) inl(devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP);
+ // since only 1 channel reqd to bring it to last bit it is rotated
+ // 8 +(chan - 1) times then ANDed with 1 for last bit.
+ *data = (ui_TmpValue >> ui_Channel) & 0x1;
+ } // if (ui_Channel >= 0 && ui_Channel <=31)
+ else {
+ comedi_error(dev, "Not a valid channel number !!! \n");
+ return -EINVAL; // "sorry channel spec wrong "
+ } //else if (ui_Channel >= 0 && ui_Channel <=31)
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1564_ReadMoreDigitalInput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Return the status of the Requested digital inputs |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_NoOfChannels : No Of Channels To be Read |
+| UINT *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI1564_ReadMoreDigitalInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_PortValue = data[0];
+ UINT ui_Mask = 0;
+ UINT ui_NoOfChannels;
+
+ ui_NoOfChannels = CR_CHAN(insn->chanspec);
+ if (data[1] == 0) {
+ *data = (UINT) inl(devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP);
+ switch (ui_NoOfChannels) {
+ case 2:
+ ui_Mask = 3;
+ *data = (*data >> (2 * ui_PortValue)) & ui_Mask;
+ break;
+ case 4:
+ ui_Mask = 15;
+ *data = (*data >> (4 * ui_PortValue)) & ui_Mask;
+ break;
+ case 8:
+ ui_Mask = 255;
+ *data = (*data >> (8 * ui_PortValue)) & ui_Mask;
+ break;
+ case 16:
+ ui_Mask = 65535;
+ *data = (*data >> (16 * ui_PortValue)) & ui_Mask;
+ break;
+ case 31:
+ break;
+ default:
+ comedi_error(dev, "Not a valid Channel number !!!\n");
+ return -EINVAL; // "sorry channel spec wrong "
+ break;
+ } // switch (ui_NoOfChannels)
+ } // if (data[1]==0)
+ else {
+ if (data[1] == 1) {
+ *data = ui_InterruptStatus_1564;
+ } // if (data[1]==1)
+ } // else if (data[1]==0)
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1564_ConfigDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Configures The Digital Output Subdevice. |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| data[1] : 1 Enable VCC Interrupt |
+| 0 Disable VCC Interrupt |
+| data[2] : 1 Enable CC Interrupt |
+| 0 Disable CC Interrupt |
+| |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI1564_ConfigDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ ULONG ul_Command = 0;
+
+ if ((data[0] != 0) && (data[0] != 1)) {
+ comedi_error(dev,
+ "Not a valid Data !!! ,Data should be 1 or 0\n");
+ return -EINVAL;
+ } // if ((data[0]!=0) && (data[0]!=1))
+ if (data[0]) {
+ devpriv->b_OutputMemoryStatus = ADDIDATA_ENABLE;
+ } // if (data[0])
+ else {
+ devpriv->b_OutputMemoryStatus = ADDIDATA_DISABLE;
+ } // else if (data[0])
+ if (data[1] == ADDIDATA_ENABLE) {
+ ul_Command = ul_Command | 0x1;
+ } // if (data[1] == ADDIDATA_ENABLE)
+ else {
+ ul_Command = ul_Command & 0xFFFFFFFE;
+ } // else if (data[1] == ADDIDATA_ENABLE)
+ if (data[2] == ADDIDATA_ENABLE) {
+ ul_Command = ul_Command | 0x2;
+ } // if (data[2] == ADDIDATA_ENABLE)
+ else {
+ ul_Command = ul_Command & 0xFFFFFFFD;
+ } // else if (data[2] == ADDIDATA_ENABLE)
+ outl(ul_Command,
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_OP +
+ APCI1564_DIGITAL_OP_INTERRUPT);
+ ui_InterruptData =
+ inl(devpriv->i_IobaseAmcc + APCI1564_DIGITAL_OP +
+ APCI1564_DIGITAL_OP_INTERRUPT);
+ devpriv->tsk_Current = current;
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1564_WriteDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Writes port value To the selected port |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_NoOfChannels : No Of Channels To Write |
+| UINT *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI1564_WriteDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Temp, ui_Temp1;
+ UINT ui_NoOfChannel;
+
+ ui_NoOfChannel = CR_CHAN(insn->chanspec);
+ if (devpriv->b_OutputMemoryStatus) {
+ ui_Temp =
+ inl(devpriv->i_IobaseAmcc + APCI1564_DIGITAL_OP +
+ APCI1564_DIGITAL_OP_RW);
+ } // if (devpriv->b_OutputMemoryStatus )
+ else {
+ ui_Temp = 0;
+ } // else if (devpriv->b_OutputMemoryStatus )
+ if (data[3] == 0) {
+ if (data[1] == 0) {
+ data[0] = (data[0] << ui_NoOfChannel) | ui_Temp;
+ outl(data[0],
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_OP +
+ APCI1564_DIGITAL_OP_RW);
+ } // if (data[1]==0)
+ else {
+ if (data[1] == 1) {
+ switch (ui_NoOfChannel) {
+ case 2:
+ data[0] =
+ (data[0] << (2 *
+ data[2])) | ui_Temp;
+ break;
+ case 4:
+ data[0] =
+ (data[0] << (4 *
+ data[2])) | ui_Temp;
+ break;
+ case 8:
+ data[0] =
+ (data[0] << (8 *
+ data[2])) | ui_Temp;
+ break;
+ case 16:
+ data[0] =
+ (data[0] << (16 *
+ data[2])) | ui_Temp;
+ break;
+ case 31:
+ data[0] = data[0] | ui_Temp;
+ break;
+ default:
+ comedi_error(dev, " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+ } // switch (ui_NoOfChannels)
+ outl(data[0],
+ devpriv->i_IobaseAmcc +
+ APCI1564_DIGITAL_OP +
+ APCI1564_DIGITAL_OP_RW);
+ } // if (data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } // else if (data[1]==1)
+ } // else if (data[1]==0)
+ } //if(data[3]==0)
+ else {
+ if (data[3] == 1) {
+ if (data[1] == 0) {
+ data[0] = ~data[0] & 0x1;
+ ui_Temp1 = 1;
+ ui_Temp1 = ui_Temp1 << ui_NoOfChannel;
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ (data[0] << ui_NoOfChannel) ^
+ 0xffffffff;
+ data[0] = data[0] & ui_Temp;
+ outl(data[0],
+ devpriv->i_IobaseAmcc +
+ APCI1564_DIGITAL_OP +
+ APCI1564_DIGITAL_OP_RW);
+ } // if (data[1]==0)
+ else {
+ if (data[1] == 1) {
+ switch (ui_NoOfChannel) {
+ case 2:
+ data[0] = ~data[0] & 0x3;
+ ui_Temp1 = 3;
+ ui_Temp1 =
+ ui_Temp1 << 2 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (2 *
+ data
+ [2])) ^
+ 0xffffffff) & ui_Temp;
+ break;
+ case 4:
+ data[0] = ~data[0] & 0xf;
+ ui_Temp1 = 15;
+ ui_Temp1 =
+ ui_Temp1 << 4 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (4 *
+ data
+ [2])) ^
+ 0xffffffff) & ui_Temp;
+ break;
+ case 8:
+ data[0] = ~data[0] & 0xff;
+ ui_Temp1 = 255;
+ ui_Temp1 =
+ ui_Temp1 << 8 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (8 *
+ data
+ [2])) ^
+ 0xffffffff) & ui_Temp;
+ break;
+ case 16:
+ data[0] = ~data[0] & 0xffff;
+ ui_Temp1 = 65535;
+ ui_Temp1 =
+ ui_Temp1 << 16 *
+ data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (16 *
+ data
+ [2])) ^
+ 0xffffffff) & ui_Temp;
+ break;
+ case 31:
+ break;
+ default:
+ comedi_error(dev,
+ " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+ } //switch(ui_NoOfChannels)
+ outl(data[0],
+ devpriv->i_IobaseAmcc +
+ APCI1564_DIGITAL_OP +
+ APCI1564_DIGITAL_OP_RW);
+ } // if (data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } // else if (data[1]==1)
+ } // else if (data[1]==0)
+ } // if (data[3]==1);
+ else {
+ printk("\nSpecified functionality does not exist\n");
+ return -EINVAL;
+ } // else if (data[3]==1)
+ } // else if (data[3]==0)
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1564_ReadDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read value of the selected channel or port |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_NoOfChannels : No Of Channels To read |
+| UINT *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI1564_ReadDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Temp;
+ UINT ui_NoOfChannel;
+
+ ui_NoOfChannel = CR_CHAN(insn->chanspec);
+ ui_Temp = data[0];
+ *data = inl(devpriv->i_IobaseAmcc + APCI1564_DIGITAL_OP +
+ APCI1564_DIGITAL_OP_RW);
+ if (ui_Temp == 0) {
+ *data = (*data >> ui_NoOfChannel) & 0x1;
+ } // if (ui_Temp==0)
+ else {
+ if (ui_Temp == 1) {
+ switch (ui_NoOfChannel) {
+ case 2:
+ *data = (*data >> (2 * data[1])) & 3;
+ break;
+
+ case 4:
+ *data = (*data >> (4 * data[1])) & 15;
+ break;
+
+ case 8:
+ *data = (*data >> (8 * data[1])) & 255;
+ break;
+
+ case 16:
+ *data = (*data >> (16 * data[1])) & 65535;
+ break;
+
+ case 31:
+ break;
+
+ default:
+ comedi_error(dev, " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+ break;
+ } // switch(ui_NoOfChannels)
+ } // if (ui_Temp==1)
+ else {
+ printk("\nSpecified channel not supported \n");
+ } // else if (ui_Temp==1)
+ } // else if (ui_Temp==0)
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1564_ConfigTimerCounterWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Configures The Timer , Counter or Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| data[0] : 0 Configure As Timer |
+| 1 Configure As Counter |
+| 2 Configure As Watchdog |
+| data[1] : 1 Enable Interrupt |
+| 0 Disable Interrupt |
+| data[2] : Time Unit |
+| data[3] : Reload Value |
+| data[4] : Timer Mode |
+| data[5] : Timer Counter Watchdog Number|
+ data[6] : Counter Direction
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI1564_ConfigTimerCounterWatchdog(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ ULONG ul_Command1 = 0;
+ devpriv->tsk_Current = current;
+ if (data[0] == ADDIDATA_WATCHDOG) {
+ devpriv->b_TimerSelectMode = ADDIDATA_WATCHDOG;
+
+ //Disable the watchdog
+ outl(0x0,
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_OP_WATCHDOG +
+ APCI1564_TCW_PROG);
+ //Loading the Reload value
+ outl(data[3],
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_OP_WATCHDOG +
+ APCI1564_TCW_RELOAD_VALUE);
+ } // if (data[0]==ADDIDATA_WATCHDOG)
+ else if (data[0] == ADDIDATA_TIMER) {
+ //First Stop The Timer
+ ul_Command1 =
+ inl(devpriv->i_IobaseAmcc + APCI1564_TIMER +
+ APCI1564_TCW_PROG);
+ ul_Command1 = ul_Command1 & 0xFFFFF9FEUL;
+ outl(ul_Command1, devpriv->i_IobaseAmcc + APCI1564_TIMER + APCI1564_TCW_PROG); //Stop The Timer
+
+ devpriv->b_TimerSelectMode = ADDIDATA_TIMER;
+ if (data[1] == 1) {
+ outl(0x02, devpriv->i_IobaseAmcc + APCI1564_TIMER + APCI1564_TCW_PROG); //Enable TIMER int & DISABLE ALL THE OTHER int SOURCES
+ outl(0x0,
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP +
+ APCI1564_DIGITAL_IP_IRQ);
+ outl(0x0,
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_OP +
+ APCI1564_DIGITAL_OP_IRQ);
+ outl(0x0,
+ devpriv->i_IobaseAmcc +
+ APCI1564_DIGITAL_OP_WATCHDOG +
+ APCI1564_TCW_IRQ);
+ outl(0x0,
+ devpriv->iobase + APCI1564_COUNTER1 +
+ APCI1564_TCW_IRQ);
+ outl(0x0,
+ devpriv->iobase + APCI1564_COUNTER2 +
+ APCI1564_TCW_IRQ);
+ outl(0x0,
+ devpriv->iobase + APCI1564_COUNTER3 +
+ APCI1564_TCW_IRQ);
+ outl(0x0,
+ devpriv->iobase + APCI1564_COUNTER4 +
+ APCI1564_TCW_IRQ);
+ } // if (data[1]==1)
+ else {
+ outl(0x0, devpriv->i_IobaseAmcc + APCI1564_TIMER + APCI1564_TCW_PROG); //disable Timer interrupt
+ } // else if (data[1]==1)
+
+ // Loading Timebase
+
+ outl(data[2],
+ devpriv->i_IobaseAmcc + APCI1564_TIMER +
+ APCI1564_TCW_TIMEBASE);
+
+ //Loading the Reload value
+ outl(data[3],
+ devpriv->i_IobaseAmcc + APCI1564_TIMER +
+ APCI1564_TCW_RELOAD_VALUE);
+
+ ul_Command1 =
+ inl(devpriv->i_IobaseAmcc + APCI1564_TIMER +
+ APCI1564_TCW_PROG);
+ ul_Command1 =
+ (ul_Command1 & 0xFFF719E2UL) | 2UL << 13UL | 0x10UL;
+ outl(ul_Command1, devpriv->i_IobaseAmcc + APCI1564_TIMER + APCI1564_TCW_PROG); //mode 2
+ } // else if (data[0]==ADDIDATA_TIMER)
+ else if (data[0] == ADDIDATA_COUNTER) {
+ devpriv->b_TimerSelectMode = ADDIDATA_COUNTER;
+ devpriv->b_ModeSelectRegister = data[5];
+
+ //First Stop The Counter
+ ul_Command1 =
+ inl(devpriv->iobase + ((data[5] - 1) * 0x20) +
+ APCI1564_TCW_PROG);
+ ul_Command1 = ul_Command1 & 0xFFFFF9FEUL;
+ outl(ul_Command1, devpriv->iobase + ((data[5] - 1) * 0x20) + APCI1564_TCW_PROG); //Stop The Timer
+
+ /************************/
+ /* Set the reload value */
+ /************************/
+ outl(data[3],
+ devpriv->iobase + ((data[5] - 1) * 0x20) +
+ APCI1564_TCW_RELOAD_VALUE);
+
+ /******************************/
+ /* Set the mode : */
+ /* - Disable the hardware */
+ /* - Disable the counter mode */
+ /* - Disable the warning */
+ /* - Disable the reset */
+ /* - Disable the timer mode */
+ /* - Enable the counter mode */
+ /******************************/
+ ul_Command1 =
+ (ul_Command1 & 0xFFFC19E2UL) | 0x80000UL |
+ (ULONG) ((ULONG) data[4] << 16UL);
+ outl(ul_Command1,
+ devpriv->iobase + ((data[5] - 1) * 0x20) +
+ APCI1564_TCW_PROG);
+
+ // Enable or Disable Interrupt
+ ul_Command1 = (ul_Command1 & 0xFFFFF9FD) | (data[1] << 1);
+ outl(ul_Command1,
+ devpriv->iobase + ((data[5] - 1) * 0x20) +
+ APCI1564_TCW_PROG);
+
+ /*****************************/
+ /* Set the Up/Down selection */
+ /*****************************/
+ ul_Command1 = (ul_Command1 & 0xFFFBF9FFUL) | (data[6] << 18);
+ outl(ul_Command1,
+ devpriv->iobase + ((data[5] - 1) * 0x20) +
+ APCI1564_TCW_PROG);
+ } // else if (data[0]==ADDIDATA_COUNTER)
+ else {
+ printk(" Invalid subdevice.");
+ } // else if (data[0]==ADDIDATA_WATCHDOG)
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1564_StartStopWriteTimerCounterWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Start / Stop The Selected Timer , Counter or Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| data[0] : 0 Timer |
+| 1 Counter |
+| 2 Watchdog | | data[1] : 1 Start |
+| 0 Stop |
+| 2 Trigger |
+| Clear (Only Counter) |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI1564_StartStopWriteTimerCounterWatchdog(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ ULONG ul_Command1 = 0;
+ if (devpriv->b_TimerSelectMode == ADDIDATA_WATCHDOG) {
+ switch (data[1]) {
+ case 0: //stop the watchdog
+ outl(0x0, devpriv->i_IobaseAmcc + APCI1564_DIGITAL_OP_WATCHDOG + APCI1564_TCW_PROG); //disable the watchdog
+ break;
+ case 1: //start the watchdog
+ outl(0x0001,
+ devpriv->i_IobaseAmcc +
+ APCI1564_DIGITAL_OP_WATCHDOG +
+ APCI1564_TCW_PROG);
+ break;
+ case 2: //Software trigger
+ outl(0x0201,
+ devpriv->i_IobaseAmcc +
+ APCI1564_DIGITAL_OP_WATCHDOG +
+ APCI1564_TCW_PROG);
+ break;
+ default:
+ printk("\nSpecified functionality does not exist\n");
+ return -EINVAL;
+ } // switch (data[1])
+ } // if (devpriv->b_TimerSelectMode==ADDIDATA_WATCHDOG)
+ if (devpriv->b_TimerSelectMode == ADDIDATA_TIMER) {
+ if (data[1] == 1) {
+ ul_Command1 =
+ inl(devpriv->i_IobaseAmcc + APCI1564_TIMER +
+ APCI1564_TCW_PROG);
+ ul_Command1 = (ul_Command1 & 0xFFFFF9FFUL) | 0x1UL;
+
+ //Enable the Timer
+ outl(ul_Command1,
+ devpriv->i_IobaseAmcc + APCI1564_TIMER +
+ APCI1564_TCW_PROG);
+ } // if (data[1]==1)
+ else if (data[1] == 0) {
+ //Stop The Timer
+
+ ul_Command1 =
+ inl(devpriv->i_IobaseAmcc + APCI1564_TIMER +
+ APCI1564_TCW_PROG);
+ ul_Command1 = ul_Command1 & 0xFFFFF9FEUL;
+ outl(ul_Command1,
+ devpriv->i_IobaseAmcc + APCI1564_TIMER +
+ APCI1564_TCW_PROG);
+ } // else if(data[1]==0)
+ } // if (devpriv->b_TimerSelectMode==ADDIDATA_TIMER)
+ if (devpriv->b_TimerSelectMode == ADDIDATA_COUNTER) {
+ ul_Command1 =
+ inl(devpriv->iobase + ((devpriv->b_ModeSelectRegister -
+ 1) * 0x20) + APCI1564_TCW_PROG);
+ if (data[1] == 1) {
+ //Start the Counter subdevice
+ ul_Command1 = (ul_Command1 & 0xFFFFF9FFUL) | 0x1UL;
+ } // if (data[1] == 1)
+ else if (data[1] == 0) {
+ // Stops the Counter subdevice
+ ul_Command1 = 0;
+
+ } // else if (data[1] == 0)
+ else if (data[1] == 2) {
+ // Clears the Counter subdevice
+ ul_Command1 = (ul_Command1 & 0xFFFFF9FFUL) | 0x400;
+ } // else if (data[1] == 3)
+ outl(ul_Command1,
+ devpriv->iobase + ((devpriv->b_ModeSelectRegister -
+ 1) * 0x20) + APCI1564_TCW_PROG);
+ } // if (devpriv->b_TimerSelectMode==ADDIDATA_COUNTER)
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1564_ReadTimerCounterWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read The Selected Timer , Counter or Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI1564_ReadTimerCounterWatchdog(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ ULONG ul_Command1 = 0;
+
+ if (devpriv->b_TimerSelectMode == ADDIDATA_WATCHDOG) {
+ // Stores the status of the Watchdog
+ data[0] =
+ inl(devpriv->i_IobaseAmcc +
+ APCI1564_DIGITAL_OP_WATCHDOG +
+ APCI1564_TCW_TRIG_STATUS) & 0x1;
+ data[1] =
+ inl(devpriv->i_IobaseAmcc +
+ APCI1564_DIGITAL_OP_WATCHDOG);
+ } // if (devpriv->b_TimerSelectMode==ADDIDATA_WATCHDOG)
+ else if (devpriv->b_TimerSelectMode == ADDIDATA_TIMER) {
+ // Stores the status of the Timer
+ data[0] =
+ inl(devpriv->i_IobaseAmcc + APCI1564_TIMER +
+ APCI1564_TCW_TRIG_STATUS) & 0x1;
+
+ // Stores the Actual value of the Timer
+ data[1] = inl(devpriv->i_IobaseAmcc + APCI1564_TIMER);
+ } // else if (devpriv->b_TimerSelectMode==ADDIDATA_TIMER)
+ else if (devpriv->b_TimerSelectMode == ADDIDATA_COUNTER) {
+ // Read the Counter Actual Value.
+ data[0] =
+ inl(devpriv->iobase + ((devpriv->b_ModeSelectRegister -
+ 1) * 0x20) +
+ APCI1564_TCW_SYNC_ENABLEDISABLE);
+ ul_Command1 =
+ inl(devpriv->iobase + ((devpriv->b_ModeSelectRegister -
+ 1) * 0x20) + APCI1564_TCW_TRIG_STATUS);
+
+ /***********************************/
+ /* Get the software trigger status */
+ /***********************************/
+ data[1] = (BYTE) ((ul_Command1 >> 1) & 1);
+
+ /***********************************/
+ /* Get the hardware trigger status */
+ /***********************************/
+ data[2] = (BYTE) ((ul_Command1 >> 2) & 1);
+
+ /*********************************/
+ /* Get the software clear status */
+ /*********************************/
+ data[3] = (BYTE) ((ul_Command1 >> 3) & 1);
+
+ /***************************/
+ /* Get the overflow status */
+ /***************************/
+ data[4] = (BYTE) ((ul_Command1 >> 0) & 1);
+ } // else if (devpriv->b_TimerSelectMode==ADDIDATA_COUNTER)
+ else if ((devpriv->b_TimerSelectMode != ADDIDATA_TIMER)
+ && (devpriv->b_TimerSelectMode != ADDIDATA_WATCHDOG)
+ && (devpriv->b_TimerSelectMode != ADDIDATA_COUNTER)) {
+ printk("\n Invalid Subdevice !!!\n");
+ } // else if ((devpriv->b_TimerSelectMode!=ADDIDATA_TIMER) && (devpriv->b_TimerSelectMode!=ADDIDATA_WATCHDOG)&& (devpriv->b_TimerSelectMode!=ADDIDATA_COUNTER))
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1564_ReadInterruptStatus |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task :Reads the interrupt status register |
++----------------------------------------------------------------------------+
+| Input Parameters : |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI1564_ReadInterruptStatus(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ *data = ui_Type;
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : static void v_APCI1564_Interrupt |
+| (int irq , void *d) |
++----------------------------------------------------------------------------+
+| Task : Interrupt handler for the interruptible digital inputs |
++----------------------------------------------------------------------------+
+| Input Parameters : int irq : irq number |
+| void *d : void pointer |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+static void v_APCI1564_Interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ UINT ui_DO, ui_DI;
+ UINT ui_Timer;
+ UINT ui_C1, ui_C2, ui_C3, ui_C4;
+ ULONG ul_Command2 = 0;
+ ui_DI = inl(devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP +
+ APCI1564_DIGITAL_IP_IRQ) & 0x01;
+ ui_DO = inl(devpriv->i_IobaseAmcc + APCI1564_DIGITAL_OP +
+ APCI1564_DIGITAL_OP_IRQ) & 0x01;
+ ui_Timer =
+ inl(devpriv->i_IobaseAmcc + APCI1564_TIMER +
+ APCI1564_TCW_IRQ) & 0x01;
+ ui_C1 = inl(devpriv->iobase + APCI1564_COUNTER1 +
+ APCI1564_TCW_IRQ) & 0x1;
+ ui_C2 = inl(devpriv->iobase + APCI1564_COUNTER2 +
+ APCI1564_TCW_IRQ) & 0x1;
+ ui_C3 = inl(devpriv->iobase + APCI1564_COUNTER3 +
+ APCI1564_TCW_IRQ) & 0x1;
+ ui_C4 = inl(devpriv->iobase + APCI1564_COUNTER4 +
+ APCI1564_TCW_IRQ) & 0x1;
+ if (ui_DI == 0 && ui_DO == 0 && ui_Timer == 0 && ui_C1 == 0
+ && ui_C2 == 0 && ui_C3 == 0 && ui_C4 == 0) {
+ printk("\nInterrupt from unknown source\n");
+ } // if(ui_DI==0 && ui_DO==0 && ui_Timer==0 && ui_C1==0 && ui_C2==0 && ui_C3==0 && ui_C4==0)
+
+ if (ui_DI == 1) {
+ ui_DI = inl(devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP +
+ APCI1564_DIGITAL_IP_IRQ);
+ outl(0x0,
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP +
+ APCI1564_DIGITAL_IP_IRQ);
+ ui_InterruptStatus_1564 =
+ inl(devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP +
+ APCI1564_DIGITAL_IP_INTERRUPT_STATUS);
+ ui_InterruptStatus_1564 = ui_InterruptStatus_1564 & 0X000FFFF0;
+ send_sig(SIGIO, devpriv->tsk_Current, 0); // send signal to the sample
+ outl(ui_DI, devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP + APCI1564_DIGITAL_IP_IRQ); //enable the interrupt
+ return;
+ }
+
+ if (ui_DO == 1) {
+ // Check for Digital Output interrupt Type - 1: Vcc interrupt 2: CC interrupt.
+ ui_Type =
+ inl(devpriv->i_IobaseAmcc + APCI1564_DIGITAL_OP +
+ APCI1564_DIGITAL_OP_INTERRUPT_STATUS) & 0x3;
+ //Disable the Interrupt
+ outl(0x0,
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_OP +
+ APCI1564_DIGITAL_OP_INTERRUPT);
+
+ //Sends signal to user space
+ send_sig(SIGIO, devpriv->tsk_Current, 0);
+
+ } // if (ui_DO)
+
+ if ((ui_Timer == 1) && (devpriv->b_TimerSelectMode = ADDIDATA_TIMER)) {
+ // Disable Timer Interrupt
+ ul_Command2 =
+ inl(devpriv->i_IobaseAmcc + APCI1564_TIMER +
+ APCI1564_TCW_PROG);
+ outl(0x0,
+ devpriv->i_IobaseAmcc + APCI1564_TIMER +
+ APCI1564_TCW_PROG);
+
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current, 0);
+
+ // Enable Timer Interrupt
+
+ outl(ul_Command2,
+ devpriv->i_IobaseAmcc + APCI1564_TIMER +
+ APCI1564_TCW_PROG);
+ } // if ((ui_Timer == 1) && (devpriv->b_TimerSelectMode =ADDIDATA_TIMER))
+
+ if ((ui_C1 == 1) && (devpriv->b_TimerSelectMode = ADDIDATA_COUNTER)) {
+ // Disable Counter Interrupt
+ ul_Command2 =
+ inl(devpriv->iobase + APCI1564_COUNTER1 +
+ APCI1564_TCW_PROG);
+ outl(0x0,
+ devpriv->iobase + APCI1564_COUNTER1 +
+ APCI1564_TCW_PROG);
+
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current, 0);
+
+ // Enable Counter Interrupt
+ outl(ul_Command2,
+ devpriv->iobase + APCI1564_COUNTER1 +
+ APCI1564_TCW_PROG);
+ } // if ((ui_C1 == 1) && (devpriv->b_TimerSelectMode = ADDIDATA_COUNTER))
+
+ if ((ui_C2 == 1) && (devpriv->b_TimerSelectMode = ADDIDATA_COUNTER)) {
+ // Disable Counter Interrupt
+ ul_Command2 =
+ inl(devpriv->iobase + APCI1564_COUNTER2 +
+ APCI1564_TCW_PROG);
+ outl(0x0,
+ devpriv->iobase + APCI1564_COUNTER2 +
+ APCI1564_TCW_PROG);
+
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current, 0);
+
+ // Enable Counter Interrupt
+ outl(ul_Command2,
+ devpriv->iobase + APCI1564_COUNTER2 +
+ APCI1564_TCW_PROG);
+ } // if ((ui_C2 == 1) && (devpriv->b_TimerSelectMode =ADDIDATA_COUNTER))
+
+ if ((ui_C3 == 1) && (devpriv->b_TimerSelectMode = ADDIDATA_COUNTER)) {
+ // Disable Counter Interrupt
+ ul_Command2 =
+ inl(devpriv->iobase + APCI1564_COUNTER3 +
+ APCI1564_TCW_PROG);
+ outl(0x0,
+ devpriv->iobase + APCI1564_COUNTER3 +
+ APCI1564_TCW_PROG);
+
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current, 0);
+
+ // Enable Counter Interrupt
+ outl(ul_Command2,
+ devpriv->iobase + APCI1564_COUNTER3 +
+ APCI1564_TCW_PROG);
+ } // if ((ui_C3 == 1) && (devpriv->b_TimerSelectMode =ADDIDATA_COUNTER))
+
+ if ((ui_C4 == 1) && (devpriv->b_TimerSelectMode = ADDIDATA_COUNTER)) {
+ // Disable Counter Interrupt
+ ul_Command2 =
+ inl(devpriv->iobase + APCI1564_COUNTER4 +
+ APCI1564_TCW_PROG);
+ outl(0x0,
+ devpriv->iobase + APCI1564_COUNTER4 +
+ APCI1564_TCW_PROG);
+
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current, 0);
+
+ // Enable Counter Interrupt
+ outl(ul_Command2,
+ devpriv->iobase + APCI1564_COUNTER4 +
+ APCI1564_TCW_PROG);
+ } // if ((ui_C4 == 1) && (devpriv->b_TimerSelectMode =ADDIDATA_COUNTER))
+ return;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI1564_Reset(struct comedi_device *dev) | |
++----------------------------------------------------------------------------+
+| Task :resets all the registers |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI1564_Reset(struct comedi_device * dev)
+{
+ outl(0x0, devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP_IRQ); //disable the interrupts
+ inl(devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP_INTERRUPT_STATUS); //Reset the interrupt status register
+ outl(0x0, devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP_INTERRUPT_MODE1); //Disable the and/or interrupt
+ outl(0x0, devpriv->i_IobaseAmcc + APCI1564_DIGITAL_IP_INTERRUPT_MODE2);
+ devpriv->b_DigitalOutputRegister = 0;
+ ui_Type = 0;
+ outl(0x0, devpriv->i_IobaseAmcc + APCI1564_DIGITAL_OP); //Resets the output channels
+ outl(0x0, devpriv->i_IobaseAmcc + APCI1564_DIGITAL_OP_INTERRUPT); //Disables the interrupt.
+ outl(0x0,
+ devpriv->i_IobaseAmcc + APCI1564_DIGITAL_OP_WATCHDOG +
+ APCI1564_TCW_RELOAD_VALUE);
+ outl(0x0, devpriv->i_IobaseAmcc + APCI1564_TIMER);
+ outl(0x0, devpriv->i_IobaseAmcc + APCI1564_TIMER + APCI1564_TCW_PROG);
+
+ outl(0x0, devpriv->iobase + APCI1564_COUNTER1 + APCI1564_TCW_PROG);
+ outl(0x0, devpriv->iobase + APCI1564_COUNTER2 + APCI1564_TCW_PROG);
+ outl(0x0, devpriv->iobase + APCI1564_COUNTER3 + APCI1564_TCW_PROG);
+ outl(0x0, devpriv->iobase + APCI1564_COUNTER4 + APCI1564_TCW_PROG);
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1564.h b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1564.h
new file mode 100644
index 000000000000..f0c461ca5396
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1564.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+/********* Definitions for APCI-1564 card *****/
+
+#define APCI1564_BOARD_VENDOR_ID 0x15B8
+#define APCI1564_ADDRESS_RANGE 128
+
+//DIGITAL INPUT-OUTPUT DEFINE
+// Input defines
+#define APCI1564_DIGITAL_IP 0x04
+#define APCI1564_DIGITAL_IP_INTERRUPT_MODE1 4
+#define APCI1564_DIGITAL_IP_INTERRUPT_MODE2 8
+#define APCI1564_DIGITAL_IP_IRQ 16
+
+// Output defines
+#define APCI1564_DIGITAL_OP 0x18
+#define APCI1564_DIGITAL_OP_RW 0
+#define APCI1564_DIGITAL_OP_INTERRUPT 4
+#define APCI1564_DIGITAL_OP_IRQ 12
+
+//Digital Input IRQ Function Selection
+#define ADDIDATA_OR 0
+#define ADDIDATA_AND 1
+
+//Digital Input Interrupt Status
+#define APCI1564_DIGITAL_IP_INTERRUPT_STATUS 12
+
+//Digital Output Interrupt Status
+#define APCI1564_DIGITAL_OP_INTERRUPT_STATUS 8
+
+//Digital Input Interrupt Enable Disable.
+#define APCI1564_DIGITAL_IP_INTERRUPT_ENABLE 0x4
+#define APCI1564_DIGITAL_IP_INTERRUPT_DISABLE 0xFFFFFFFB
+
+//Digital Output Interrupt Enable Disable.
+#define APCI1564_DIGITAL_OP_VCC_INTERRUPT_ENABLE 0x1
+#define APCI1564_DIGITAL_OP_VCC_INTERRUPT_DISABLE 0xFFFFFFFE
+#define APCI1564_DIGITAL_OP_CC_INTERRUPT_ENABLE 0x2
+#define APCI1564_DIGITAL_OP_CC_INTERRUPT_DISABLE 0xFFFFFFFD
+
+//ADDIDATA Enable Disable
+
+#define ADDIDATA_ENABLE 1
+#define ADDIDATA_DISABLE 0
+
+// TIMER COUNTER WATCHDOG DEFINES
+
+#define ADDIDATA_TIMER 0
+#define ADDIDATA_COUNTER 1
+#define ADDIDATA_WATCHDOG 2
+#define APCI1564_DIGITAL_OP_WATCHDOG 0x28
+#define APCI1564_TIMER 0x48
+#define APCI1564_COUNTER1 0x0
+#define APCI1564_COUNTER2 0x20
+#define APCI1564_COUNTER3 0x40
+#define APCI1564_COUNTER4 0x60
+#define APCI1564_TCW_SYNC_ENABLEDISABLE 0
+#define APCI1564_TCW_RELOAD_VALUE 4
+#define APCI1564_TCW_TIMEBASE 8
+#define APCI1564_TCW_PROG 12
+#define APCI1564_TCW_TRIG_STATUS 16
+#define APCI1564_TCW_IRQ 20
+#define APCI1564_TCW_WARN_TIMEVAL 24
+#define APCI1564_TCW_WARN_TIMEBASE 28
+
+// Hardware Layer functions for Apci1564
+
+//DI
+// for di read
+INT i_APCI1564_ConfigDigitalInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI1564_Read1DigitalInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI1564_ReadMoreDigitalInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+//DO
+int i_APCI1564_ConfigDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI1564_WriteDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI1564_ReadDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI1564_ReadInterruptStatus(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+// TIMER
+// timer value is passed as u seconds
+INT i_APCI1564_ConfigTimerCounterWatchdog(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI1564_StartStopWriteTimerCounterWatchdog(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+int i_APCI1564_ReadTimerCounterWatchdog(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+// INTERRUPT
+static void v_APCI1564_Interrupt(int irq, void *d);
+
+// RESET
+INT i_APCI1564_Reset(struct comedi_device *dev);
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci16xx.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci16xx.c
new file mode 100644
index 000000000000..906d635da6b2
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci16xx.c
@@ -0,0 +1,780 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-----------------------------------------------------------------------+
+ | Project : API APCI1648 | Compiler : gcc |
+ | Module name : TTL.C | Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: S. Weber | Date : 25/05/2005 |
+ +-----------------------------------------------------------------------+
+ | Description : APCI-16XX TTL I/O module |
+ | |
+ | |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +-----------------------------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ |25.05.2005| S.Weber | Creation |
+ | | | |
+ +-----------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+
+#include "hwdrv_apci16xx.h"
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI16XX_InsnConfigInitTTLIO |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task APCI16XX_TTL_INIT (using defaults) : |
+| Configure the TTL I/O operating mode from all ports |
+| You must calling this function be |
+| for you call any other function witch access of TTL. |
+| APCI16XX_TTL_INITDIRECTION(user inputs for direction) |
++----------------------------------------------------------------------------+
+| Input Parameters : b_InitType = (BYTE) data[0]; |
+| b_Port0Mode = (BYTE) data[1]; |
+| b_Port1Mode = (BYTE) data[2]; |
+| b_Port2Mode = (BYTE) data[3]; |
+| b_Port3Mode = (BYTE) data[4]; |
+| ........ |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value :>0: No error |
+| -1: Port 0 mode selection is wrong |
+| -2: Port 1 mode selection is wrong |
+| -3: Port 2 mode selection is wrong |
+| -4: Port 3 mode selection is wrong |
+| -X: Port X-1 mode selection is wrong |
+| .... |
+| -100 : Config command error |
+| -101 : Data size error |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI16XX_InsnConfigInitTTLIO(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = insn->n;
+ BYTE b_Command = 0;
+ BYTE b_Cpt = 0;
+ BYTE b_NumberOfPort =
+ (BYTE) (devpriv->ps_BoardInfo->i_NbrTTLChannel / 8);
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n >= 1) {
+ /*******************/
+ /* Get the command */
+ /* **************** */
+
+ b_Command = (BYTE) data[0];
+
+ /********************/
+ /* Test the command */
+ /********************/
+
+ if ((b_Command == APCI16XX_TTL_INIT) ||
+ (b_Command == APCI16XX_TTL_INITDIRECTION) ||
+ (b_Command == APCI16XX_TTL_OUTPUTMEMORY)) {
+ /***************************************/
+ /* Test the initialisation buffer size */
+ /***************************************/
+
+ if ((b_Command == APCI16XX_TTL_INITDIRECTION)
+ && ((BYTE) (insn->n - 1) != b_NumberOfPort)) {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("\nBuffer size error");
+ i_ReturnValue = -101;
+ }
+
+ if ((b_Command == APCI16XX_TTL_OUTPUTMEMORY)
+ && ((BYTE) (insn->n) != 2)) {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("\nBuffer size error");
+ i_ReturnValue = -101;
+ }
+ } else {
+ /************************/
+ /* Config command error */
+ /************************/
+
+ printk("\nCommand selection error");
+ i_ReturnValue = -100;
+ }
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("\nBuffer size error");
+ i_ReturnValue = -101;
+ }
+
+ /**************************************************************************/
+ /* Test if no error occur and APCI16XX_TTL_INITDIRECTION command selected */
+ /**************************************************************************/
+
+ if ((i_ReturnValue >= 0) && (b_Command == APCI16XX_TTL_INITDIRECTION)) {
+ memset(devpriv->ul_TTLPortConfiguration, 0,
+ sizeof(devpriv->ul_TTLPortConfiguration));
+
+ /*************************************/
+ /* Test the port direction selection */
+ /*************************************/
+
+ for (b_Cpt = 1;
+ (b_Cpt <= b_NumberOfPort) && (i_ReturnValue >= 0);
+ b_Cpt++) {
+ /**********************/
+ /* Test the direction */
+ /**********************/
+
+ if ((data[b_Cpt] != 0) && (data[b_Cpt] != 0xFF)) {
+ /************************/
+ /* Port direction error */
+ /************************/
+
+ printk("\nPort %d direction selection error",
+ (INT) b_Cpt);
+ i_ReturnValue = -(INT) b_Cpt;
+ }
+
+ /**************************/
+ /* Save the configuration */
+ /**************************/
+
+ devpriv->ul_TTLPortConfiguration[(b_Cpt - 1) / 4] =
+ devpriv->ul_TTLPortConfiguration[(b_Cpt -
+ 1) / 4] | (data[b_Cpt] << (8 * ((b_Cpt -
+ 1) % 4)));
+ }
+ }
+
+ /**************************/
+ /* Test if no error occur */
+ /**************************/
+
+ if (i_ReturnValue >= 0) {
+ /***********************************/
+ /* Test if TTL port initilaisation */
+ /***********************************/
+
+ if ((b_Command == APCI16XX_TTL_INIT)
+ || (b_Command == APCI16XX_TTL_INITDIRECTION)) {
+ /******************************/
+ /* Set all port configuration */
+ /******************************/
+
+ for (b_Cpt = 0; b_Cpt <= b_NumberOfPort; b_Cpt++) {
+ if ((b_Cpt % 4) == 0) {
+ /*************************/
+ /* Set the configuration */
+ /*************************/
+
+ outl(devpriv->
+ ul_TTLPortConfiguration[b_Cpt /
+ 4],
+ devpriv->iobase + 32 + b_Cpt);
+ }
+ }
+ }
+ }
+
+ /************************************************/
+ /* Test if output memory initialisation command */
+ /************************************************/
+
+ if (b_Command == APCI16XX_TTL_OUTPUTMEMORY) {
+ if (data[1]) {
+ devpriv->b_OutputMemoryStatus = ADDIDATA_ENABLE;
+ } else {
+ devpriv->b_OutputMemoryStatus = ADDIDATA_DISABLE;
+ }
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| INPUT FUNCTIONS |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI16XX_InsnBitsReadTTLIO |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read the status from selected TTL digital input |
+| (b_InputChannel) |
++----------------------------------------------------------------------------+
+| Task : Read the status from digital input port |
+| (b_SelectedPort) |
++----------------------------------------------------------------------------+
+| Input Parameters : |
+| APCI16XX_TTL_READCHANNEL |
+| b_SelectedPort= CR_RANGE(insn->chanspec); |
+| b_InputChannel= CR_CHAN(insn->chanspec); |
+| b_ReadType = (BYTE) data[0]; |
+| |
+| APCI16XX_TTL_READPORT |
+| b_SelectedPort= CR_RANGE(insn->chanspec); |
+| b_ReadType = (BYTE) data[0]; |
++----------------------------------------------------------------------------+
+| Output Parameters : data[0] 0 : Channle is not active |
+| 1 : Channle is active |
++----------------------------------------------------------------------------+
+| Return Value : >0 : No error |
+| -100 : Config command error |
+| -101 : Data size error |
+| -102 : The selected TTL input port is wrong |
+| -103 : The selected TTL digital input is wrong |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI16XX_InsnBitsReadTTLIO(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = insn->n;
+ BYTE b_Command = 0;
+ BYTE b_NumberOfPort =
+ (BYTE) (devpriv->ps_BoardInfo->i_NbrTTLChannel / 8);
+ BYTE b_SelectedPort = CR_RANGE(insn->chanspec);
+ BYTE b_InputChannel = CR_CHAN(insn->chanspec);
+ BYTE *pb_Status;
+ DWORD dw_Status;
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n >= 1) {
+ /*******************/
+ /* Get the command */
+ /* **************** */
+
+ b_Command = (BYTE) data[0];
+
+ /********************/
+ /* Test the command */
+ /********************/
+
+ if ((b_Command == APCI16XX_TTL_READCHANNEL)
+ || (b_Command == APCI16XX_TTL_READPORT)) {
+ /**************************/
+ /* Test the selected port */
+ /**************************/
+
+ if (b_SelectedPort < b_NumberOfPort) {
+ /**********************/
+ /* Test if input port */
+ /**********************/
+
+ if (((devpriv->ul_TTLPortConfiguration
+ [b_SelectedPort /
+ 4] >> (8 *
+ (b_SelectedPort
+ %
+ 4))) &
+ 0xFF) == 0) {
+ /***************************/
+ /* Test the channel number */
+ /***************************/
+
+ if ((b_Command ==
+ APCI16XX_TTL_READCHANNEL)
+ && (b_InputChannel > 7)) {
+ /*******************************************/
+ /* The selected TTL digital input is wrong */
+ /*******************************************/
+
+ printk("\nChannel selection error");
+ i_ReturnValue = -103;
+ }
+ } else {
+ /****************************************/
+ /* The selected TTL input port is wrong */
+ /****************************************/
+
+ printk("\nPort selection error");
+ i_ReturnValue = -102;
+ }
+ } else {
+ /****************************************/
+ /* The selected TTL input port is wrong */
+ /****************************************/
+
+ printk("\nPort selection error");
+ i_ReturnValue = -102;
+ }
+ } else {
+ /************************/
+ /* Config command error */
+ /************************/
+
+ printk("\nCommand selection error");
+ i_ReturnValue = -100;
+ }
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("\nBuffer size error");
+ i_ReturnValue = -101;
+ }
+
+ /**************************/
+ /* Test if no error occur */
+ /**************************/
+
+ if (i_ReturnValue >= 0) {
+ pb_Status = (PBYTE) & data[0];
+
+ /*******************************/
+ /* Get the digital inpu status */
+ /*******************************/
+
+ dw_Status =
+ inl(devpriv->iobase + 8 + ((b_SelectedPort / 4) * 4));
+ dw_Status = (dw_Status >> (8 * (b_SelectedPort % 4))) & 0xFF;
+
+ /***********************/
+ /* Save the port value */
+ /***********************/
+
+ *pb_Status = (BYTE) dw_Status;
+
+ /***************************************/
+ /* Test if read channel status command */
+ /***************************************/
+
+ if (b_Command == APCI16XX_TTL_READCHANNEL) {
+ *pb_Status = (*pb_Status >> b_InputChannel) & 1;
+ }
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI16XX_InsnReadTTLIOAllPortValue |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read the status from all digital input ports |
++----------------------------------------------------------------------------+
+| Input Parameters : - |
++----------------------------------------------------------------------------+
+| Output Parameters : data[0] : Port 0 to 3 data |
+| data[1] : Port 4 to 7 data |
+| .... |
++----------------------------------------------------------------------------+
+| Return Value : 0: No error |
+| -100 : Read command error |
+| -101 : Data size error |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI16XX_InsnReadTTLIOAllPortValue(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ BYTE b_Command = (BYTE) CR_AREF(insn->chanspec);
+ INT i_ReturnValue = insn->n;
+ BYTE b_Cpt = 0;
+ BYTE b_NumberOfPort = 0;
+ unsigned int *pls_ReadData = data;
+
+ /********************/
+ /* Test the command */
+ /********************/
+
+ if ((b_Command == APCI16XX_TTL_READ_ALL_INPUTS)
+ || (b_Command == APCI16XX_TTL_READ_ALL_OUTPUTS)) {
+ /**********************************/
+ /* Get the number of 32-Bit ports */
+ /**********************************/
+
+ b_NumberOfPort =
+ (BYTE) (devpriv->ps_BoardInfo->i_NbrTTLChannel / 32);
+ if ((b_NumberOfPort * 32) <
+ devpriv->ps_BoardInfo->i_NbrTTLChannel) {
+ b_NumberOfPort = b_NumberOfPort + 1;
+ }
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n >= b_NumberOfPort) {
+ if (b_Command == APCI16XX_TTL_READ_ALL_INPUTS) {
+ /**************************/
+ /* Read all digital input */
+ /**************************/
+
+ for (b_Cpt = 0; b_Cpt < b_NumberOfPort; b_Cpt++) {
+ /************************/
+ /* Read the 32-Bit port */
+ /************************/
+
+ pls_ReadData[b_Cpt] =
+ inl(devpriv->iobase + 8 +
+ (b_Cpt * 4));
+
+ /**************************************/
+ /* Mask all channels used als outputs */
+ /**************************************/
+
+ pls_ReadData[b_Cpt] =
+ pls_ReadData[b_Cpt] &
+ (~devpriv->
+ ul_TTLPortConfiguration[b_Cpt]);
+ }
+ } else {
+ /****************************/
+ /* Read all digital outputs */
+ /****************************/
+
+ for (b_Cpt = 0; b_Cpt < b_NumberOfPort; b_Cpt++) {
+ /************************/
+ /* Read the 32-Bit port */
+ /************************/
+
+ pls_ReadData[b_Cpt] =
+ inl(devpriv->iobase + 20 +
+ (b_Cpt * 4));
+
+ /**************************************/
+ /* Mask all channels used als outputs */
+ /**************************************/
+
+ pls_ReadData[b_Cpt] =
+ pls_ReadData[b_Cpt] & devpriv->
+ ul_TTLPortConfiguration[b_Cpt];
+ }
+ }
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("\nBuffer size error");
+ i_ReturnValue = -101;
+ }
+ } else {
+ /*****************/
+ /* Command error */
+ /*****************/
+
+ printk("\nCommand selection error");
+ i_ReturnValue = -100;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| OUTPUT FUNCTIONS |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI16XX_InsnBitsWriteTTLIO |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Set the state from selected TTL digital output |
+| (b_OutputChannel) |
++----------------------------------------------------------------------------+
+| Task : Set the state from digital output port |
+| (b_SelectedPort) |
++----------------------------------------------------------------------------+
+| Input Parameters : |
+| APCI16XX_TTL_WRITECHANNEL_ON | APCI16XX_TTL_WRITECHANNEL_OFF |
+| b_SelectedPort = CR_RANGE(insn->chanspec); |
+| b_OutputChannel= CR_CHAN(insn->chanspec); |
+| b_Command = (BYTE) data[0]; |
+| |
+| APCI16XX_TTL_WRITEPORT_ON | APCI16XX_TTL_WRITEPORT_OFF |
+| b_SelectedPort = CR_RANGE(insn->chanspec); |
+| b_Command = (BYTE) data[0]; |
++----------------------------------------------------------------------------+
+| Output Parameters : data[0] : TTL output port 0 to 3 data |
+| data[1] : TTL output port 4 to 7 data |
+| .... |
++----------------------------------------------------------------------------+
+| Return Value : >0 : No error |
+| -100 : Command error |
+| -101 : Data size error |
+| -102 : The selected TTL output port is wrong |
+| -103 : The selected TTL digital output is wrong |
+| -104 : Output memory disabled |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI16XX_InsnBitsWriteTTLIO(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = insn->n;
+ BYTE b_Command = 0;
+ BYTE b_NumberOfPort =
+ (BYTE) (devpriv->ps_BoardInfo->i_NbrTTLChannel / 8);
+ BYTE b_SelectedPort = CR_RANGE(insn->chanspec);
+ BYTE b_OutputChannel = CR_CHAN(insn->chanspec);
+ DWORD dw_Status = 0;
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n >= 1) {
+ /*******************/
+ /* Get the command */
+ /* **************** */
+
+ b_Command = (BYTE) data[0];
+
+ /********************/
+ /* Test the command */
+ /********************/
+
+ if ((b_Command == APCI16XX_TTL_WRITECHANNEL_ON) ||
+ (b_Command == APCI16XX_TTL_WRITEPORT_ON) ||
+ (b_Command == APCI16XX_TTL_WRITECHANNEL_OFF) ||
+ (b_Command == APCI16XX_TTL_WRITEPORT_OFF)) {
+ /**************************/
+ /* Test the selected port */
+ /**************************/
+
+ if (b_SelectedPort < b_NumberOfPort) {
+ /***********************/
+ /* Test if output port */
+ /***********************/
+
+ if (((devpriv->ul_TTLPortConfiguration
+ [b_SelectedPort /
+ 4] >> (8 *
+ (b_SelectedPort
+ %
+ 4))) &
+ 0xFF) == 0xFF) {
+ /***************************/
+ /* Test the channel number */
+ /***************************/
+
+ if (((b_Command == APCI16XX_TTL_WRITECHANNEL_ON) || (b_Command == APCI16XX_TTL_WRITECHANNEL_OFF)) && (b_OutputChannel > 7)) {
+ /********************************************/
+ /* The selected TTL digital output is wrong */
+ /********************************************/
+
+ printk("\nChannel selection error");
+ i_ReturnValue = -103;
+ }
+
+ if (((b_Command == APCI16XX_TTL_WRITECHANNEL_OFF) || (b_Command == APCI16XX_TTL_WRITEPORT_OFF)) && (devpriv->b_OutputMemoryStatus == ADDIDATA_DISABLE)) {
+ /********************************************/
+ /* The selected TTL digital output is wrong */
+ /********************************************/
+
+ printk("\nOutput memory disabled");
+ i_ReturnValue = -104;
+ }
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (((b_Command == APCI16XX_TTL_WRITEPORT_ON) || (b_Command == APCI16XX_TTL_WRITEPORT_OFF)) && (insn->n < 2)) {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("\nBuffer size error");
+ i_ReturnValue = -101;
+ }
+ } else {
+ /*****************************************/
+ /* The selected TTL output port is wrong */
+ /*****************************************/
+
+ printk("\nPort selection error %lX",
+ (unsigned long)devpriv->
+ ul_TTLPortConfiguration[0]);
+ i_ReturnValue = -102;
+ }
+ } else {
+ /****************************************/
+ /* The selected TTL output port is wrong */
+ /****************************************/
+
+ printk("\nPort selection error %d %d",
+ b_SelectedPort, b_NumberOfPort);
+ i_ReturnValue = -102;
+ }
+ } else {
+ /************************/
+ /* Config command error */
+ /************************/
+
+ printk("\nCommand selection error");
+ i_ReturnValue = -100;
+ }
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("\nBuffer size error");
+ i_ReturnValue = -101;
+ }
+
+ /**************************/
+ /* Test if no error occur */
+ /**************************/
+
+ if (i_ReturnValue >= 0) {
+ /********************************/
+ /* Get the digital output state */
+ /********************************/
+
+ dw_Status =
+ inl(devpriv->iobase + 20 + ((b_SelectedPort / 4) * 4));
+
+ /**********************************/
+ /* Test if output memory not used */
+ /**********************************/
+
+ if (devpriv->b_OutputMemoryStatus == ADDIDATA_DISABLE) {
+ /*********************************/
+ /* Clear the selected port value */
+ /*********************************/
+
+ dw_Status =
+ dw_Status & (0xFFFFFFFFUL -
+ (0xFFUL << (8 * (b_SelectedPort % 4))));
+ }
+
+ /******************************/
+ /* Test if setting channel ON */
+ /******************************/
+
+ if (b_Command == APCI16XX_TTL_WRITECHANNEL_ON) {
+ dw_Status =
+ dw_Status | (1UL << ((8 * (b_SelectedPort %
+ 4)) + b_OutputChannel));
+ }
+
+ /***************************/
+ /* Test if setting port ON */
+ /***************************/
+
+ if (b_Command == APCI16XX_TTL_WRITEPORT_ON) {
+ dw_Status =
+ dw_Status | ((data[1] & 0xFF) << (8 *
+ (b_SelectedPort % 4)));
+ }
+
+ /*******************************/
+ /* Test if setting channel OFF */
+ /*******************************/
+
+ if (b_Command == APCI16XX_TTL_WRITECHANNEL_OFF) {
+ dw_Status =
+ dw_Status & (0xFFFFFFFFUL -
+ (1UL << ((8 * (b_SelectedPort % 4)) +
+ b_OutputChannel)));
+ }
+
+ /****************************/
+ /* Test if setting port OFF */
+ /****************************/
+
+ if (b_Command == APCI16XX_TTL_WRITEPORT_OFF) {
+ dw_Status =
+ dw_Status & (0xFFFFFFFFUL -
+ ((data[1] & 0xFF) << (8 * (b_SelectedPort %
+ 4))));
+ }
+
+ outl(dw_Status,
+ devpriv->iobase + 20 + ((b_SelectedPort / 4) * 4));
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2200_Reset(struct comedi_device *dev) | +----------------------------------------------------------------------------+
+| Task :resets all the registers |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : - |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI16XX_Reset(struct comedi_device * dev)
+{
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci16xx.h b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci16xx.h
new file mode 100644
index 000000000000..5bf91e13a035
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci16xx.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+#ifndef COMEDI_SUBD_TTLIO
+#define COMEDI_SUBD_TTLIO 11 /* Digital Input Output But TTL */
+#endif
+
+#ifndef ADDIDATA_ENABLE
+#define ADDIDATA_ENABLE 1
+#define ADDIDATA_DISABLE 0
+#endif
+
+#define APCI16XX_TTL_INIT 0
+#define APCI16XX_TTL_INITDIRECTION 1
+#define APCI16XX_TTL_OUTPUTMEMORY 2
+
+#define APCI16XX_TTL_READCHANNEL 0
+#define APCI16XX_TTL_READPORT 1
+
+#define APCI16XX_TTL_WRITECHANNEL_ON 0
+#define APCI16XX_TTL_WRITECHANNEL_OFF 1
+#define APCI16XX_TTL_WRITEPORT_ON 2
+#define APCI16XX_TTL_WRITEPORT_OFF 3
+
+#define APCI16XX_TTL_READ_ALL_INPUTS 0
+#define APCI16XX_TTL_READ_ALL_OUTPUTS 1
+
+#ifdef __KERNEL__
+
+static const struct comedi_lrange range_apci16xx_ttl = { 12,
+ {BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1)}
+};
+
+/*
++----------------------------------------------------------------------------+
+| TTL INISIALISATION FUNCTION |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI16XX_InsnConfigInitTTLIO(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn,
+ unsigned int *data);
+
+/*
++----------------------------------------------------------------------------+
+| TTL INPUT FUNCTION |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI16XX_InsnBitsReadTTLIO(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn,
+ unsigned int *data);
+
+int i_APCI16XX_InsnReadTTLIOAllPortValue(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+/*
++----------------------------------------------------------------------------+
+| TTL OUTPUT FUNCTIONS |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI16XX_InsnBitsWriteTTLIO(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn,
+ unsigned int *data);
+
+int i_APCI16XX_Reset(struct comedi_device *dev);
+#endif
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2016.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2016.c
new file mode 100644
index 000000000000..31f55da39ea8
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2016.c
@@ -0,0 +1,460 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-------------------------------+---------------------------------------+
+ | Project : APCI-2016 | Compiler : GCC |
+ | Module name : hwdrv_apci2016.c| Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-------------------------------+---------------------------------------+
+ | Description : Hardware Layer Acces For APCI-2016 |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +----------+-----------+------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | | | |
+ | | | |
+ | | | |
+ +----------+-----------+------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+#include "hwdrv_apci2016.h"
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2016_ConfigDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Configures The Digital Output Subdevice. |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| data[0] : 1 Digital Memory On |
+| 0 Digital Memory Off |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI2016_ConfigDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if ((data[0] != 0) && (data[0] != 1)) {
+ comedi_error(dev,
+ "Not a valid Data !!! ,Data should be 1 or 0\n");
+ return -EINVAL;
+ } // if ((data[0]!=0) && (data[0]!=1))
+ if (data[0]) {
+ devpriv->b_OutputMemoryStatus = ADDIDATA_ENABLE;
+ } // if (data[0]
+ else {
+ devpriv->b_OutputMemoryStatus = ADDIDATA_DISABLE;
+ } // else if (data[0]
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2016_WriteDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Writes port value To the selected port |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_NoOfChannels : No Of Channels To Write |
+| UINT *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI2016_WriteDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_NoOfChannel;
+ UINT ui_Temp, ui_Temp1;
+ ui_NoOfChannel = CR_CHAN(insn->chanspec);
+ if ((ui_NoOfChannel < 0) || (ui_NoOfChannel > 15)) {
+ comedi_error(dev,
+ "Invalid Channel Numbers !!!, Channel Numbers must be between 0 and 15\n");
+ return -EINVAL;
+ } // if ((ui_NoOfChannel<0) || (ui_NoOfChannel>15))
+ if (devpriv->b_OutputMemoryStatus) {
+ ui_Temp = inw(devpriv->iobase + APCI2016_DIGITAL_OP);
+ } // if (devpriv->b_OutputMemoryStatus )
+ else {
+ ui_Temp = 0;
+ } // else if (devpriv->b_OutputMemoryStatus )
+ if ((data[1] != 0) && (data[1] != 1)) {
+ comedi_error(dev,
+ "Invalid Data[1] value !!!, Data[1] should be 0 or 1\n");
+ return -EINVAL;
+ } // if ((data[1]!=0) && (data[1]!=1))
+
+ if (data[3] == 0) {
+ if (data[1] == 0) {
+ data[0] = (data[0] << ui_NoOfChannel) | ui_Temp;
+ outw(data[0], devpriv->iobase + APCI2016_DIGITAL_OP);
+ } // if (data[1]==0)
+ else {
+ if (data[1] == 1) {
+ switch (ui_NoOfChannel) {
+ case 2:
+ data[0] =
+ (data[0] << (2 *
+ data[2])) | ui_Temp;
+ break;
+ case 4:
+ data[0] =
+ (data[0] << (4 *
+ data[2])) | ui_Temp;
+ break;
+ case 8:
+ data[0] =
+ (data[0] << (8 *
+ data[2])) | ui_Temp;
+ break;
+ case 15:
+ data[0] = data[0] | ui_Temp;
+ break;
+ default:
+ comedi_error(dev, " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+ } //switch(ui_NoOfChannels)
+ outw(data[0],
+ devpriv->iobase + APCI2016_DIGITAL_OP);
+ } // if (data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } // else if (data[1]==1)
+ } // else if (data[1]==0)
+ } // if (data[3]==0)
+ else {
+ if (data[3] == 1) {
+ if (data[1] == 0) {
+ data[0] = ~data[0] & 0x1;
+ ui_Temp1 = 1;
+ ui_Temp1 = ui_Temp1 << ui_NoOfChannel;
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] = (data[0] << ui_NoOfChannel) ^ 0xffff;
+ data[0] = data[0] & ui_Temp;
+ outw(data[0],
+ devpriv->iobase + APCI2016_DIGITAL_OP);
+ } // if (data[1]==0)
+ else {
+ if (data[1] == 1) {
+ switch (ui_NoOfChannel) {
+ case 2:
+ data[0] = ~data[0] & 0x3;
+ ui_Temp1 = 3;
+ ui_Temp1 =
+ ui_Temp1 << 2 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (2 *
+ data
+ [2])) ^
+ 0xffff) & ui_Temp;
+ break;
+ case 4:
+ data[0] = ~data[0] & 0xf;
+ ui_Temp1 = 15;
+ ui_Temp1 =
+ ui_Temp1 << 4 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (4 *
+ data
+ [2])) ^
+ 0xffff) & ui_Temp;
+ break;
+ case 8:
+ data[0] = ~data[0] & 0xff;
+ ui_Temp1 = 255;
+ ui_Temp1 =
+ ui_Temp1 << 8 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (8 *
+ data
+ [2])) ^
+ 0xffff) & ui_Temp;
+ break;
+ case 15:
+ break;
+ default:
+ comedi_error(dev,
+ " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+ } //switch(ui_NoOfChannels)
+ outw(data[0],
+ devpriv->iobase +
+ APCI2016_DIGITAL_OP);
+ } // if(data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } //else if(data[1]==1)
+ } //elseif(data[1]==0)
+ } //if(data[3]==1);
+ else {
+ printk("\nSpecified functionality does not exist\n");
+ return -EINVAL;
+ } //if else data[3]==1)
+ } //if else data[3]==0)
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2016_BitsDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read value of the selected channel or port |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_NoOfChannels : No Of Channels To read |
+| UINT *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI2016_BitsDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Temp;
+ UINT ui_NoOfChannel;
+ ui_NoOfChannel = CR_CHAN(insn->chanspec);
+ if ((ui_NoOfChannel < 0) || (ui_NoOfChannel > 15)) {
+ comedi_error(dev,
+ "Invalid Channel Numbers !!!, Channel Numbers must be between 0 and 15\n");
+ return -EINVAL;
+ } // if ((ui_NoOfChannel<0) || (ui_NoOfChannel>15))
+ if ((data[0] != 0) && (data[0] != 1)) {
+ comedi_error(dev,
+ "Invalid Data[0] value !!!, Data[0] should be 0 or 1\n");
+ return -EINVAL;
+ } // if ((data[0]!=0) && (data[0]!=1))
+ ui_Temp = data[0];
+ *data = inw(devpriv->iobase + APCI2016_DIGITAL_OP_RW);
+ if (ui_Temp == 0) {
+ *data = (*data >> ui_NoOfChannel) & 0x1;
+ } // if (ui_Temp==0)
+ else {
+ if (ui_Temp == 1) {
+ switch (ui_NoOfChannel) {
+ case 2:
+ *data = (*data >> (2 * data[1])) & 3;
+ break;
+
+ case 4:
+ *data = (*data >> (4 * data[1])) & 15;
+ break;
+
+ case 8:
+ *data = (*data >> (8 * data[1])) & 255;
+ break;
+
+ case 15:
+ break;
+
+ default:
+ comedi_error(dev, " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+ } //switch(ui_NoOfChannel)
+ } // if (ui_Temp==1)
+ else {
+ printk("\nSpecified channel not supported \n");
+ } // else if (ui_Temp==1)
+ } // if (ui_Temp==0)
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2016_ConfigWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Configures The Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure |
+| struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI2016_ConfigWatchdog(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ if (data[0] == 0) {
+ //Disable the watchdog
+ outw(0x0,
+ devpriv->i_IobaseAddon +
+ APCI2016_WATCHDOG_ENABLEDISABLE);
+ //Loading the Reload value
+ outw(data[1],
+ devpriv->i_IobaseAddon +
+ APCI2016_WATCHDOG_RELOAD_VALUE);
+ data[1] = data[1] >> 16;
+ outw(data[1],
+ devpriv->i_IobaseAddon +
+ APCI2016_WATCHDOG_RELOAD_VALUE + 2);
+ } else {
+ printk("\nThe input parameters are wrong\n");
+ }
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2016_StartStopWriteWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Start / Stop The Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure |
+| struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI2016_StartStopWriteWatchdog(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ switch (data[0]) {
+ case 0: //stop the watchdog
+ outw(0x0, devpriv->i_IobaseAddon + APCI2016_WATCHDOG_ENABLEDISABLE); //disable the watchdog
+ break;
+ case 1: //start the watchdog
+ outw(0x0001,
+ devpriv->i_IobaseAddon +
+ APCI2016_WATCHDOG_ENABLEDISABLE);
+ break;
+ case 2: //Software trigger
+ outw(0x0201,
+ devpriv->i_IobaseAddon +
+ APCI2016_WATCHDOG_ENABLEDISABLE);
+ break;
+ default:
+ printk("\nSpecified functionality does not exist\n");
+ return -EINVAL;
+ } // switch(data[0])
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2016_ReadWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read The Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure |
+| struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI2016_ReadWatchdog(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ udelay(5);
+ data[0] = inw(devpriv->i_IobaseAddon + APCI2016_WATCHDOG_STATUS) & 0x1;
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2016_Reset(struct comedi_device *dev) | |
++----------------------------------------------------------------------------+
+| Task :resets all the registers |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI2016_Reset(struct comedi_device * dev)
+{
+ outw(0x0, devpriv->iobase + APCI2016_DIGITAL_OP); // Resets the digital output channels
+ outw(0x0, devpriv->i_IobaseAddon + APCI2016_WATCHDOG_ENABLEDISABLE);
+ outw(0x0, devpriv->i_IobaseAddon + APCI2016_WATCHDOG_RELOAD_VALUE);
+ outw(0x0, devpriv->i_IobaseAddon + APCI2016_WATCHDOG_RELOAD_VALUE + 2);
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2016.h b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2016.h
new file mode 100644
index 000000000000..9261aacd25f1
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2016.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+/********* Definitions for APCI-2016 card *****/
+
+#define APCI2016_BOARD_VENDOR_ID 0x15B8
+#define APCI2016_ADDRESS_RANGE 8
+
+//DIGITAL INPUT-OUTPUT DEFINE
+
+#define APCI2016_DIGITAL_OP 0x04
+#define APCI2016_DIGITAL_OP_RW 4
+
+//ADDIDATA Enable Disable
+
+#define ADDIDATA_ENABLE 1
+#define ADDIDATA_DISABLE 0
+
+// TIMER COUNTER WATCHDOG DEFINES
+
+#define ADDIDATA_WATCHDOG 2
+#define APCI2016_DIGITAL_OP_WATCHDOG 0
+#define APCI2016_WATCHDOG_ENABLEDISABLE 12
+#define APCI2016_WATCHDOG_RELOAD_VALUE 4
+#define APCI2016_WATCHDOG_STATUS 16
+
+// Hardware Layer functions for Apci2016
+
+//DO
+int i_APCI2016_ConfigDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+int i_APCI2016_WriteDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+int i_APCI2016_BitsDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+// TIMER
+// timer value is passed as u seconds
+
+int i_APCI2016_ConfigWatchdog(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+int i_APCI2016_StartStopWriteWatchdog(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+int i_APCI2016_ReadWatchdog(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+// Interrupt functions.....
+
+// void v_APCI2016_Interrupt(int irq, void *d) ;
+
+ //void v_APCI2016_Interrupt(int irq, void *d);
+// RESET
+INT i_APCI2016_Reset(struct comedi_device *dev);
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2032.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2032.c
new file mode 100644
index 000000000000..1347bc388f3e
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2032.c
@@ -0,0 +1,579 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-------------------------------+---------------------------------------+
+ | Project : APCI-2032 | Compiler : GCC |
+ | Module name : hwdrv_apci2032.c| Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-------------------------------+---------------------------------------+
+ | Description : Hardware Layer Acces For APCI-2032 |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +----------+-----------+------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | | | |
+ | | | |
+ | | | |
+ +----------+-----------+------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+
+#include "hwdrv_apci2032.h"
+UINT ui_InterruptData, ui_Type;
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2032_ConfigDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Configures The Digital Output Subdevice. |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| data[1] : 1 Enable VCC Interrupt |
+| 0 Disable VCC Interrupt |
+| data[2] : 1 Enable CC Interrupt |
+| 0 Disable CC Interrupt |
+| |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI2032_ConfigDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ ULONG ul_Command = 0;
+ devpriv->tsk_Current = current;
+
+ if ((data[0] != 0) && (data[0] != 1)) {
+ comedi_error(dev,
+ "Not a valid Data !!! ,Data should be 1 or 0\n");
+ return -EINVAL;
+ } //if ( (data[0]!=0) && (data[0]!=1) )
+ if (data[0]) {
+ devpriv->b_OutputMemoryStatus = ADDIDATA_ENABLE;
+ } // if (data[0])
+ else {
+ devpriv->b_OutputMemoryStatus = ADDIDATA_DISABLE;
+ } //else if (data[0])
+
+ if (data[1] == ADDIDATA_ENABLE) {
+ ul_Command = ul_Command | 0x1;
+ } //if (data[1] == ADDIDATA_ENABLE)
+ else {
+ ul_Command = ul_Command & 0xFFFFFFFE;
+ } //elseif (data[1] == ADDIDATA_ENABLE)
+ if (data[2] == ADDIDATA_ENABLE) {
+ ul_Command = ul_Command | 0x2;
+ } //if (data[2] == ADDIDATA_ENABLE)
+ else {
+ ul_Command = ul_Command & 0xFFFFFFFD;
+ } //elseif (data[2] == ADDIDATA_ENABLE)
+ outl(ul_Command, devpriv->iobase + APCI2032_DIGITAL_OP_INTERRUPT);
+ ui_InterruptData = inl(devpriv->iobase + APCI2032_DIGITAL_OP_INTERRUPT);
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2032_WriteDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Writes port value To the selected port |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_NoOfChannels : No Of Channels To Write |
+| UINT *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI2032_WriteDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Temp, ui_Temp1;
+ UINT ui_NoOfChannel = CR_CHAN(insn->chanspec); // get the channel
+ if (devpriv->b_OutputMemoryStatus) {
+ ui_Temp = inl(devpriv->iobase + APCI2032_DIGITAL_OP);
+
+ } //if(devpriv->b_OutputMemoryStatus )
+ else {
+ ui_Temp = 0;
+ } //if(devpriv->b_OutputMemoryStatus )
+ if (data[3] == 0) {
+ if (data[1] == 0) {
+ data[0] = (data[0] << ui_NoOfChannel) | ui_Temp;
+ outl(data[0], devpriv->iobase + APCI2032_DIGITAL_OP);
+ } //if(data[1]==0)
+ else {
+ if (data[1] == 1) {
+ switch (ui_NoOfChannel) {
+
+ case 2:
+ data[0] =
+ (data[0] << (2 *
+ data[2])) | ui_Temp;
+ break;
+
+ case 4:
+ data[0] =
+ (data[0] << (4 *
+ data[2])) | ui_Temp;
+ break;
+
+ case 8:
+ data[0] =
+ (data[0] << (8 *
+ data[2])) | ui_Temp;
+ break;
+
+ case 16:
+ data[0] =
+ (data[0] << (16 *
+ data[2])) | ui_Temp;
+ break;
+ case 31:
+ data[0] = data[0] | ui_Temp;
+ break;
+
+ default:
+ comedi_error(dev, " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+
+ } //switch(ui_NoOfChannels)
+
+ outl(data[0],
+ devpriv->iobase + APCI2032_DIGITAL_OP);
+ } // if(data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } //else if(data[1]==1)
+ } //elseif(data[1]==0)
+ } //if(data[3]==0)
+ else {
+ if (data[3] == 1) {
+ if (data[1] == 0) {
+ data[0] = ~data[0] & 0x1;
+ ui_Temp1 = 1;
+ ui_Temp1 = ui_Temp1 << ui_NoOfChannel;
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ (data[0] << ui_NoOfChannel) ^
+ 0xffffffff;
+ data[0] = data[0] & ui_Temp;
+ outl(data[0],
+ devpriv->iobase + APCI2032_DIGITAL_OP);
+ } //if(data[1]==0)
+ else {
+ if (data[1] == 1) {
+ switch (ui_NoOfChannel) {
+
+ case 2:
+ data[0] = ~data[0] & 0x3;
+ ui_Temp1 = 3;
+ ui_Temp1 =
+ ui_Temp1 << 2 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (2 *
+ data
+ [2])) ^
+ 0xffffffff) & ui_Temp;
+ break;
+
+ case 4:
+ data[0] = ~data[0] & 0xf;
+ ui_Temp1 = 15;
+ ui_Temp1 =
+ ui_Temp1 << 4 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (4 *
+ data
+ [2])) ^
+ 0xffffffff) & ui_Temp;
+ break;
+
+ case 8:
+ data[0] = ~data[0] & 0xff;
+ ui_Temp1 = 255;
+ ui_Temp1 =
+ ui_Temp1 << 8 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (8 *
+ data
+ [2])) ^
+ 0xffffffff) & ui_Temp;
+ break;
+
+ case 16:
+ data[0] = ~data[0] & 0xffff;
+ ui_Temp1 = 65535;
+ ui_Temp1 =
+ ui_Temp1 << 16 *
+ data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (16 *
+ data
+ [2])) ^
+ 0xffffffff) & ui_Temp;
+ break;
+
+ case 31:
+ break;
+ default:
+ comedi_error(dev,
+ " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+
+ } //switch(ui_NoOfChannels)
+
+ outl(data[0],
+ devpriv->iobase +
+ APCI2032_DIGITAL_OP);
+ } // if(data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } //else if(data[1]==1)
+ } //elseif(data[1]==0)
+ } //if(data[3]==1);
+ else {
+ printk("\nSpecified functionality does not exist\n");
+ return -EINVAL;
+ } //if else data[3]==1)
+ } //if else data[3]==0)
+ return (insn->n);;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2032_ReadDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read value of the selected channel or port |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_NoOfChannels : No Of Channels To read |
+| UINT *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI2032_ReadDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Temp;
+ UINT ui_NoOfChannel;
+ ui_NoOfChannel = CR_CHAN(insn->chanspec);
+ ui_Temp = data[0];
+ *data = inl(devpriv->iobase + APCI2032_DIGITAL_OP_RW);
+ if (ui_Temp == 0) {
+ *data = (*data >> ui_NoOfChannel) & 0x1;
+ } //if (ui_Temp==0)
+ else {
+ if (ui_Temp == 1) {
+ switch (ui_NoOfChannel) {
+
+ case 2:
+ *data = (*data >> (2 * data[1])) & 3;
+ break;
+
+ case 4:
+ *data = (*data >> (4 * data[1])) & 15;
+ break;
+
+ case 8:
+ *data = (*data >> (8 * data[1])) & 255;
+ break;
+
+ case 16:
+ *data = (*data >> (16 * data[1])) & 65535;
+ break;
+
+ case 31:
+ break;
+
+ default:
+ comedi_error(dev, " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+
+ } //switch(ui_NoOfChannels)
+ } //if (ui_Temp==1)
+ else {
+ printk("\nSpecified channel not supported \n");
+ } //elseif (ui_Temp==1)
+ }
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI2032_ConfigWatchdog(comedi_device
+ *dev,struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data)|
+| |
++----------------------------------------------------------------------------+
+| Task : Configures The Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI2032_ConfigWatchdog(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (data[0] == 0) {
+ //Disable the watchdog
+ outl(0x0,
+ devpriv->iobase + APCI2032_DIGITAL_OP_WATCHDOG +
+ APCI2032_TCW_PROG);
+ //Loading the Reload value
+ outl(data[1],
+ devpriv->iobase + APCI2032_DIGITAL_OP_WATCHDOG +
+ APCI2032_TCW_RELOAD_VALUE);
+ } else {
+ printk("\nThe input parameters are wrong\n");
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+ /*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI2032_StartStopWriteWatchdog |
+ | (struct comedi_device *dev,struct comedi_subdevice *s,
+ struct comedi_insn *insn,unsigned int *data); |
+ +----------------------------------------------------------------------------+
+ | Task : Start / Stop The Watchdog |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+ | unsigned int *data : Data Pointer to read status |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+ */
+
+int i_APCI2032_StartStopWriteWatchdog(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ switch (data[0]) {
+ case 0: //stop the watchdog
+ outl(0x0, devpriv->iobase + APCI2032_DIGITAL_OP_WATCHDOG + APCI2032_TCW_PROG); //disable the watchdog
+ break;
+ case 1: //start the watchdog
+ outl(0x0001,
+ devpriv->iobase + APCI2032_DIGITAL_OP_WATCHDOG +
+ APCI2032_TCW_PROG);
+ break;
+ case 2: //Software trigger
+ outl(0x0201,
+ devpriv->iobase + APCI2032_DIGITAL_OP_WATCHDOG +
+ APCI2032_TCW_PROG);
+ break;
+ default:
+ printk("\nSpecified functionality does not exist\n");
+ return -EINVAL;
+ }
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2032_ReadWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,
+ unsigned int *data); |
++----------------------------------------------------------------------------+
+| Task : Read The Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI2032_ReadWatchdog(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ data[0] =
+ inl(devpriv->iobase + APCI2032_DIGITAL_OP_WATCHDOG +
+ APCI2032_TCW_TRIG_STATUS) & 0x1;
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : void v_APCI2032_Interrupt |
+| (int irq , void *d) |
++----------------------------------------------------------------------------+
+| Task : Writes port value To the selected port |
++----------------------------------------------------------------------------+
+| Input Parameters : int irq : irq number |
+| void *d : void pointer |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+void v_APCI2032_Interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ unsigned int ui_DO;
+
+ ui_DO = inl(devpriv->iobase + APCI2032_DIGITAL_OP_IRQ) & 0x1; //Check if VCC OR CC interrupt has occured.
+
+ if (ui_DO == 0) {
+ printk("\nInterrupt from unKnown source\n");
+ } // if(ui_DO==0)
+ if (ui_DO) {
+ // Check for Digital Output interrupt Type - 1: Vcc interrupt 2: CC interrupt.
+ ui_Type =
+ inl(devpriv->iobase +
+ APCI2032_DIGITAL_OP_INTERRUPT_STATUS) & 0x3;
+ outl(0x0,
+ devpriv->iobase + APCI2032_DIGITAL_OP +
+ APCI2032_DIGITAL_OP_INTERRUPT);
+ if (ui_Type == 1) {
+ //Sends signal to user space
+ send_sig(SIGIO, devpriv->tsk_Current, 0);
+ } // if (ui_Type==1)
+ else {
+ if (ui_Type == 2) {
+ // Sends signal to user space
+ send_sig(SIGIO, devpriv->tsk_Current, 0);
+ } //if (ui_Type==2)
+ } //else if (ui_Type==1)
+ } //if(ui_DO)
+
+ return;
+
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2032_ReadInterruptStatus |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task :Reads the interrupt status register |
++----------------------------------------------------------------------------+
+| Input Parameters : |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI2032_ReadInterruptStatus(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ *data = ui_Type;
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2032_Reset(struct comedi_device *dev) |
+| |
++----------------------------------------------------------------------------+
+| Task :Resets the registers of the card |
++----------------------------------------------------------------------------+
+| Input Parameters : |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI2032_Reset(struct comedi_device * dev)
+{
+ devpriv->b_DigitalOutputRegister = 0;
+ ui_Type = 0;
+ outl(0x0, devpriv->iobase + APCI2032_DIGITAL_OP); //Resets the output channels
+ outl(0x0, devpriv->iobase + APCI2032_DIGITAL_OP_INTERRUPT); //Disables the interrupt.
+ outl(0x0, devpriv->iobase + APCI2032_DIGITAL_OP_WATCHDOG + APCI2032_TCW_PROG); //disable the watchdog
+ outl(0x0, devpriv->iobase + APCI2032_DIGITAL_OP_WATCHDOG + APCI2032_TCW_RELOAD_VALUE); //reload=0
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2032.h b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2032.h
new file mode 100644
index 000000000000..55002e0288b5
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2032.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+/********* Definitions for APCI-2032 card *****/
+
+// Card Specific information
+#define APCI2032_BOARD_VENDOR_ID 0x15B8
+#define APCI2032_ADDRESS_RANGE 63
+
+//DIGITAL INPUT-OUTPUT DEFINE
+
+#define APCI2032_DIGITAL_OP 0
+#define APCI2032_DIGITAL_OP_RW 0
+#define APCI2032_DIGITAL_OP_INTERRUPT 4
+#define APCI2032_DIGITAL_OP_IRQ 12
+
+//Digital Output Interrupt Status
+#define APCI2032_DIGITAL_OP_INTERRUPT_STATUS 8
+
+//Digital Output Interrupt Enable Disable.
+#define APCI2032_DIGITAL_OP_VCC_INTERRUPT_ENABLE 0x1
+#define APCI2032_DIGITAL_OP_VCC_INTERRUPT_DISABLE 0xFFFFFFFE
+#define APCI2032_DIGITAL_OP_CC_INTERRUPT_ENABLE 0x2
+#define APCI2032_DIGITAL_OP_CC_INTERRUPT_DISABLE 0xFFFFFFFD
+
+//ADDIDATA Enable Disable
+
+#define ADDIDATA_ENABLE 1
+#define ADDIDATA_DISABLE 0
+
+// TIMER COUNTER WATCHDOG DEFINES
+
+#define ADDIDATA_WATCHDOG 2
+#define APCI2032_DIGITAL_OP_WATCHDOG 16
+#define APCI2032_TCW_RELOAD_VALUE 4
+#define APCI2032_TCW_TIMEBASE 8
+#define APCI2032_TCW_PROG 12
+#define APCI2032_TCW_TRIG_STATUS 16
+#define APCI2032_TCW_IRQ 20
+
+// Hardware Layer functions for Apci2032
+
+//DO
+int i_APCI2032_ConfigDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI2032_WriteDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI2032_ReadDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI2032_ReadInterruptStatus(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+// TIMER
+// timer value is passed as u seconds
+INT i_APCI2032_ConfigWatchdog(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI2032_StartStopWriteWatchdog(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI2032_ReadWatchdog(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+// Interrupt functions.....
+
+void v_APCI2032_Interrupt(int irq, void *d);
+
+//Reset functions
+int i_APCI2032_Reset(struct comedi_device *dev);
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2200.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2200.c
new file mode 100644
index 000000000000..947e18ef3ab3
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2200.c
@@ -0,0 +1,549 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-------------------------------+---------------------------------------+
+ | Project : APCI-2200 | Compiler : GCC |
+ | Module name : hwdrv_apci2200.c| Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-------------------------------+---------------------------------------+
+ | Description : Hardware Layer Acces For APCI-2200 |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +----------+-----------+------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | | | |
+ | | | |
+ | | | |
+ +----------+-----------+------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+#include "hwdrv_apci2200.h"
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2200_Read1DigitalInput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Return the status of the digital input |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI2200_Read1DigitalInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_TmpValue = 0;
+ UINT ui_Channel;
+ ui_Channel = CR_CHAN(insn->chanspec);
+ if (ui_Channel >= 0 && ui_Channel <= 7) {
+ ui_TmpValue = (UINT) inw(devpriv->iobase + APCI2200_DIGITAL_IP);
+ *data = (ui_TmpValue >> ui_Channel) & 0x1;
+ } //if(ui_Channel >= 0 && ui_Channel <=7)
+ else {
+ printk("\nThe specified channel does not exist\n");
+ return -EINVAL; // "sorry channel spec wrong "
+ } //else if(ui_Channel >= 0 && ui_Channel <=7)
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2200_ReadMoreDigitalInput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Return the status of the Requested digital inputs |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI2200_ReadMoreDigitalInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ UINT ui_PortValue = data[0];
+ UINT ui_Mask = 0;
+ UINT ui_NoOfChannels;
+
+ ui_NoOfChannels = CR_CHAN(insn->chanspec);
+
+ *data = (UINT) inw(devpriv->iobase + APCI2200_DIGITAL_IP);
+ switch (ui_NoOfChannels) {
+ case 2:
+ ui_Mask = 3;
+ *data = (*data >> (2 * ui_PortValue)) & ui_Mask;
+ break;
+ case 4:
+ ui_Mask = 15;
+ *data = (*data >> (4 * ui_PortValue)) & ui_Mask;
+ break;
+ case 7:
+ break;
+
+ default:
+ printk("\nWrong parameters\n");
+ return -EINVAL; // "sorry channel spec wrong "
+ break;
+ } //switch(ui_NoOfChannels)
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2200_ConfigDigitalOutput (struct comedi_device *dev,
+ struct comedi_subdevice *s struct comedi_insn *insn,unsigned int *data) |
+| |
++----------------------------------------------------------------------------+
+| Task : Configures The Digital Output Subdevice. |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| unsigned int *data : Data Pointer contains |
+| configuration parameters as below |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| data[0] :1:Memory on |
+| 0:Memory off |
+| |
+| |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI2200_ConfigDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ devpriv->b_OutputMemoryStatus = data[0];
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2200_WriteDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,
+ unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Writes port value To the selected port |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI2200_WriteDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Temp, ui_Temp1;
+ UINT ui_NoOfChannel = CR_CHAN(insn->chanspec); // get the channel
+ if (devpriv->b_OutputMemoryStatus) {
+ ui_Temp = inw(devpriv->iobase + APCI2200_DIGITAL_OP);
+
+ } //if(devpriv->b_OutputMemoryStatus )
+ else {
+ ui_Temp = 0;
+ } //if(devpriv->b_OutputMemoryStatus )
+ if (data[3] == 0) {
+ if (data[1] == 0) {
+ data[0] = (data[0] << ui_NoOfChannel) | ui_Temp;
+ outw(data[0], devpriv->iobase + APCI2200_DIGITAL_OP);
+ } //if(data[1]==0)
+ else {
+ if (data[1] == 1) {
+ switch (ui_NoOfChannel) {
+
+ case 2:
+ data[0] =
+ (data[0] << (2 *
+ data[2])) | ui_Temp;
+ break;
+
+ case 4:
+ data[0] =
+ (data[0] << (4 *
+ data[2])) | ui_Temp;
+ break;
+
+ case 8:
+ data[0] =
+ (data[0] << (8 *
+ data[2])) | ui_Temp;
+ break;
+ case 15:
+ data[0] = data[0] | ui_Temp;
+ break;
+ default:
+ comedi_error(dev, " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+
+ } //switch(ui_NoOfChannels)
+
+ outw(data[0],
+ devpriv->iobase + APCI2200_DIGITAL_OP);
+ } // if(data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } //else if(data[1]==1)
+ } //elseif(data[1]==0)
+ } //if(data[3]==0)
+ else {
+ if (data[3] == 1) {
+ if (data[1] == 0) {
+ data[0] = ~data[0] & 0x1;
+ ui_Temp1 = 1;
+ ui_Temp1 = ui_Temp1 << ui_NoOfChannel;
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] = (data[0] << ui_NoOfChannel) ^ 0xffff;
+ data[0] = data[0] & ui_Temp;
+ outw(data[0],
+ devpriv->iobase + APCI2200_DIGITAL_OP);
+ } //if(data[1]==0)
+ else {
+ if (data[1] == 1) {
+ switch (ui_NoOfChannel) {
+
+ case 2:
+ data[0] = ~data[0] & 0x3;
+ ui_Temp1 = 3;
+ ui_Temp1 =
+ ui_Temp1 << 2 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (2 *
+ data
+ [2])) ^
+ 0xffff) & ui_Temp;
+ break;
+
+ case 4:
+ data[0] = ~data[0] & 0xf;
+ ui_Temp1 = 15;
+ ui_Temp1 =
+ ui_Temp1 << 4 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (4 *
+ data
+ [2])) ^
+ 0xffff) & ui_Temp;
+ break;
+
+ case 8:
+ data[0] = ~data[0] & 0xff;
+ ui_Temp1 = 255;
+ ui_Temp1 =
+ ui_Temp1 << 8 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (8 *
+ data
+ [2])) ^
+ 0xffff) & ui_Temp;
+ break;
+ case 15:
+ break;
+
+ default:
+ comedi_error(dev,
+ " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+
+ } //switch(ui_NoOfChannels)
+
+ outw(data[0],
+ devpriv->iobase +
+ APCI2200_DIGITAL_OP);
+ } // if(data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } //else if(data[1]==1)
+ } //elseif(data[1]==0)
+ } //if(data[3]==1);
+ else {
+ printk("\nSpecified functionality does not exist\n");
+ return -EINVAL;
+ } //if else data[3]==1)
+ } //if else data[3]==0)
+ return (insn->n);;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2200_ReadDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,
+ unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read value of the selected channel or port |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI2200_ReadDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ UINT ui_Temp;
+ UINT ui_NoOfChannel = CR_CHAN(insn->chanspec); // get the channel
+ ui_Temp = data[0];
+ *data = inw(devpriv->iobase + APCI2200_DIGITAL_OP);
+ if (ui_Temp == 0) {
+ *data = (*data >> ui_NoOfChannel) & 0x1;
+ } //if(ui_Temp==0)
+ else {
+ if (ui_Temp == 1) {
+ switch (ui_NoOfChannel) {
+
+ case 2:
+ *data = (*data >> (2 * data[1])) & 3;
+ break;
+
+ case 4:
+ *data = (*data >> (4 * data[1])) & 15;
+ break;
+
+ case 8:
+ *data = (*data >> (8 * data[1])) & 255;
+ break;
+
+ case 15:
+ break;
+
+ default:
+ comedi_error(dev, " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+
+ } //switch(ui_NoOfChannels)
+ } //if(ui_Temp==1)
+ else {
+ printk("\nSpecified channel not supported \n");
+ } //elseif(ui_Temp==1)
+ } //elseif(ui_Temp==0)
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2200_ConfigWatchdog(struct comedi_device *dev,
+ struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) |
+| |
++----------------------------------------------------------------------------+
+| Task : Configures The Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI2200_ConfigWatchdog(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (data[0] == 0) {
+ //Disable the watchdog
+ outw(0x0,
+ devpriv->iobase + APCI2200_WATCHDOG +
+ APCI2200_WATCHDOG_ENABLEDISABLE);
+ //Loading the Reload value
+ outw(data[1],
+ devpriv->iobase + APCI2200_WATCHDOG +
+ APCI2200_WATCHDOG_RELOAD_VALUE);
+ data[1] = data[1] >> 16;
+ outw(data[1],
+ devpriv->iobase + APCI2200_WATCHDOG +
+ APCI2200_WATCHDOG_RELOAD_VALUE + 2);
+ } //if(data[0]==0)
+ else {
+ printk("\nThe input parameters are wrong\n");
+ return -EINVAL;
+ } //elseif(data[0]==0)
+
+ return insn->n;
+}
+
+ /*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI2200_StartStopWriteWatchdog |
+ | (struct comedi_device *dev,struct comedi_subdevice *s,
+ struct comedi_insn *insn,unsigned int *data); |
+ +----------------------------------------------------------------------------+
+ | Task : Start / Stop The Watchdog |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+ | unsigned int *data : Data Pointer to read status |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+ */
+
+int i_APCI2200_StartStopWriteWatchdog(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ switch (data[0]) {
+ case 0: //stop the watchdog
+ outw(0x0, devpriv->iobase + APCI2200_WATCHDOG + APCI2200_WATCHDOG_ENABLEDISABLE); //disable the watchdog
+ break;
+ case 1: //start the watchdog
+ outw(0x0001,
+ devpriv->iobase + APCI2200_WATCHDOG +
+ APCI2200_WATCHDOG_ENABLEDISABLE);
+ break;
+ case 2: //Software trigger
+ outw(0x0201,
+ devpriv->iobase + APCI2200_WATCHDOG +
+ APCI2200_WATCHDOG_ENABLEDISABLE);
+ break;
+ default:
+ printk("\nSpecified functionality does not exist\n");
+ return -EINVAL;
+ } // switch(data[0])
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2200_ReadWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,
+ unsigned int *data); |
++----------------------------------------------------------------------------+
+| Task : Read The Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s, :pointer to subdevice structure
+ struct comedi_insn *insn :pointer to insn structure |
+| unsigned int *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI2200_ReadWatchdog(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] =
+ inw(devpriv->iobase + APCI2200_WATCHDOG +
+ APCI2200_WATCHDOG_STATUS) & 0x1;
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI2200_Reset(struct comedi_device *dev) | |
++----------------------------------------------------------------------------+
+| Task :resets all the registers |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI2200_Reset(struct comedi_device * dev)
+{
+ outw(0x0, devpriv->iobase + APCI2200_DIGITAL_OP); //RESETS THE DIGITAL OUTPUTS
+ outw(0x0,
+ devpriv->iobase + APCI2200_WATCHDOG +
+ APCI2200_WATCHDOG_ENABLEDISABLE);
+ outw(0x0,
+ devpriv->iobase + APCI2200_WATCHDOG +
+ APCI2200_WATCHDOG_RELOAD_VALUE);
+ outw(0x0,
+ devpriv->iobase + APCI2200_WATCHDOG +
+ APCI2200_WATCHDOG_RELOAD_VALUE + 2);
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2200.h b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2200.h
new file mode 100644
index 000000000000..0a115b4c44c9
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci2200.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+/********* Definitions for APCI-2200 card *****/
+
+// Card Specific information
+#define APCI2200_BOARD_VENDOR_ID 0x15b8
+#define APCI2200_ADDRESS_RANGE 64
+
+//DIGITAL INPUT-OUTPUT DEFINE
+
+#define APCI2200_DIGITAL_OP 4
+#define APCI2200_DIGITAL_IP 0
+
+// TIMER COUNTER WATCHDOG DEFINES
+
+#define APCI2200_WATCHDOG 0x08
+#define APCI2200_WATCHDOG_ENABLEDISABLE 12
+#define APCI2200_WATCHDOG_RELOAD_VALUE 4
+#define APCI2200_WATCHDOG_STATUS 16
+
+// Hardware Layer functions for Apci2200
+
+//Digital Input
+INT i_APCI2200_ReadMoreDigitalInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI2200_Read1DigitalInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+//Digital Output
+int i_APCI2200_ConfigDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI2200_WriteDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI2200_ReadDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+// TIMER
+int i_APCI2200_ConfigWatchdog(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI2200_StartStopWriteWatchdog(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI2200_ReadWatchdog(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+//reset
+INT i_APCI2200_Reset(struct comedi_device *dev);
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c
new file mode 100644
index 000000000000..68eb8dfd5139
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c
@@ -0,0 +1,2697 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstrasse 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-----------------------------------------------------------------------+
+ | Project : APCI-3120 | Compiler : GCC |
+ | Module name : hwdrv_apci3120.c| Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-----------------------------------------------------------------------+
+ | Description :APCI3120 Module. Hardware abstraction Layer for APCI3120|
+ +-----------------------------------------------------------------------+
+ | UPDATE'S |
+ +-----------------------------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | | | |
+ | | | |
+ +----------+-----------+------------------------------------------------+
+*/
+
+#include "hwdrv_apci3120.h"
+static UINT ui_Temp = 0;
+
+// FUNCTION DEFINITIONS
+
+/*
++----------------------------------------------------------------------------+
+| ANALOG INPUT SUBDEVICE |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3120_InsnConfigAnalogInput(struct comedi_device *dev,|
+| struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) |
+| |
++----------------------------------------------------------------------------+
+| Task : Calls card specific function |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| struct comedi_subdevice *s |
+| struct comedi_insn *insn |
+| unsigned int *data |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_InsnConfigAnalogInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT i;
+
+ if ((data[0] != APCI3120_EOC_MODE) && (data[0] != APCI3120_EOS_MODE))
+ return -1;
+
+ // Check for Conversion time to be added ??
+ devpriv->ui_EocEosConversionTime = data[2];
+
+ if (data[0] == APCI3120_EOS_MODE) {
+
+ //Test the number of the channel
+ for (i = 0; i < data[3]; i++) {
+
+ if (CR_CHAN(data[4 + i]) >= this_board->i_NbrAiChannel) {
+ printk("bad channel list\n");
+ return -2;
+ }
+ }
+
+ devpriv->b_InterruptMode = APCI3120_EOS_MODE;
+
+ if (data[1]) {
+ devpriv->b_EocEosInterrupt = APCI3120_ENABLE;
+ } else
+ devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
+ // Copy channel list and Range List to devpriv
+
+ devpriv->ui_AiNbrofChannels = data[3];
+ for (i = 0; i < devpriv->ui_AiNbrofChannels; i++) {
+ devpriv->ui_AiChannelList[i] = data[4 + i];
+ }
+
+ } else // EOC
+ {
+ devpriv->b_InterruptMode = APCI3120_EOC_MODE;
+ if (data[1]) {
+ devpriv->b_EocEosInterrupt = APCI3120_ENABLE;
+ } else {
+ devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
+ }
+ }
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3120_InsnReadAnalogInput(struct comedi_device *dev, |
+| struct comedi_subdevice *s,struct comedi_insn *insn, unsigned int *data) |
+| |
++----------------------------------------------------------------------------+
+| Task : card specific function |
+| Reads analog input in synchronous mode |
+| EOC and EOS is selected as per configured |
+| if no conversion time is set uses default conversion |
+| time 10 microsec. |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| struct comedi_subdevice *s |
+| struct comedi_insn *insn |
+| unsigned int *data |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_InsnReadAnalogInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ USHORT us_ConvertTiming, us_TmpValue, i;
+ BYTE b_Tmp;
+
+ // fix convertion time to 10 us
+ if (!devpriv->ui_EocEosConversionTime) {
+ printk("No timer0 Value using 10 us\n");
+ us_ConvertTiming = 10;
+ } else
+ us_ConvertTiming = (USHORT) (devpriv->ui_EocEosConversionTime / 1000); // nano to useconds
+
+ // this_board->i_hwdrv_InsnReadAnalogInput(dev,us_ConvertTiming,insn->n,&insn->chanspec,data,insn->unused[0]);
+
+ // Clear software registers
+ devpriv->b_TimerSelectMode = 0;
+ devpriv->b_ModeSelectRegister = 0;
+ devpriv->us_OutputRegister = 0;
+// devpriv->b_DigitalOutputRegister=0;
+
+ if (insn->unused[0] == 222) // second insn read
+ {
+
+ for (i = 0; i < insn->n; i++) {
+ data[i] = devpriv->ui_AiReadData[i];
+ }
+
+ } else {
+ devpriv->tsk_Current = current; // Save the current process task structure
+ //Testing if board have the new Quartz and calculate the time value
+ //to set in the timer
+
+ us_TmpValue =
+ (USHORT) inw(devpriv->iobase + APCI3120_RD_STATUS);
+
+ //EL250804: Testing if board APCI3120 have the new Quartz or if it is an APCI3001
+ if ((us_TmpValue & 0x00B0) == 0x00B0
+ || !strcmp(this_board->pc_DriverName, "apci3001")) {
+ us_ConvertTiming = (us_ConvertTiming * 2) - 2;
+ } else {
+ us_ConvertTiming =
+ ((us_ConvertTiming * 12926) / 10000) - 1;
+ }
+
+ us_TmpValue = (USHORT) devpriv->b_InterruptMode;
+
+ switch (us_TmpValue) {
+
+ case APCI3120_EOC_MODE:
+
+ // Testing the interrupt flag and set the EOC bit
+ // Clears the FIFO
+ inw(devpriv->iobase + APCI3120_RESET_FIFO);
+
+ // Initialize the sequence array
+
+ //if (!i_APCI3120_SetupChannelList(dev,s,1,chanlist,0)) return -EINVAL;
+
+ if (!i_APCI3120_SetupChannelList(dev, s, 1,
+ &insn->chanspec, 0))
+ return -EINVAL;
+
+ //Initialize Timer 0 mode 4
+ devpriv->b_TimerSelectMode =
+ (devpriv->
+ b_TimerSelectMode & 0xFC) |
+ APCI3120_TIMER_0_MODE_4;
+ outb(devpriv->b_TimerSelectMode,
+ devpriv->iobase + APCI3120_TIMER_CRT1);
+
+ // Reset the scan bit and Disables the EOS, DMA, EOC interrupt
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister & APCI3120_DISABLE_SCAN;
+
+ if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) {
+
+ //Disables the EOS,DMA and enables the EOC interrupt
+ devpriv->b_ModeSelectRegister =
+ (devpriv->
+ b_ModeSelectRegister &
+ APCI3120_DISABLE_EOS_INT) |
+ APCI3120_ENABLE_EOC_INT;
+ inw(devpriv->iobase);
+
+ } else {
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister &
+ APCI3120_DISABLE_ALL_INTERRUPT_WITHOUT_TIMER;
+ }
+
+ outb(devpriv->b_ModeSelectRegister,
+ devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
+
+ // Sets gate 0
+ devpriv->us_OutputRegister =
+ (devpriv->
+ us_OutputRegister & APCI3120_CLEAR_PA_PR) |
+ APCI3120_ENABLE_TIMER0;
+ outw(devpriv->us_OutputRegister,
+ devpriv->iobase + APCI3120_WR_ADDRESS);
+
+ // Select Timer 0
+ b_Tmp = ((devpriv->
+ b_DigitalOutputRegister) & 0xF0) |
+ APCI3120_SELECT_TIMER_0_WORD;
+ outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
+
+ //Set the convertion time
+ outw(us_ConvertTiming,
+ devpriv->iobase + APCI3120_TIMER_VALUE);
+
+ us_TmpValue =
+ (USHORT) inw(dev->iobase + APCI3120_RD_STATUS);
+
+ if (devpriv->b_EocEosInterrupt == APCI3120_DISABLE) {
+
+ do {
+ // Waiting for the end of conversion
+ us_TmpValue =
+ inw(devpriv->iobase +
+ APCI3120_RD_STATUS);
+ } while ((us_TmpValue & APCI3120_EOC) ==
+ APCI3120_EOC);
+
+ //Read the result in FIFO and put it in insn data pointer
+ us_TmpValue = inw(devpriv->iobase + 0);
+ *data = us_TmpValue;
+
+ inw(devpriv->iobase + APCI3120_RESET_FIFO);
+ }
+
+ break;
+
+ case APCI3120_EOS_MODE:
+
+ inw(devpriv->iobase);
+ // Clears the FIFO
+ inw(devpriv->iobase + APCI3120_RESET_FIFO);
+ // clear PA PR and disable timer 0
+
+ devpriv->us_OutputRegister =
+ (devpriv->
+ us_OutputRegister & APCI3120_CLEAR_PA_PR) |
+ APCI3120_DISABLE_TIMER0;
+
+ outw(devpriv->us_OutputRegister,
+ devpriv->iobase + APCI3120_WR_ADDRESS);
+
+ if (!i_APCI3120_SetupChannelList(dev, s,
+ devpriv->ui_AiNbrofChannels,
+ devpriv->ui_AiChannelList, 0))
+ return -EINVAL;
+
+ //Initialize Timer 0 mode 2
+ devpriv->b_TimerSelectMode =
+ (devpriv->
+ b_TimerSelectMode & 0xFC) |
+ APCI3120_TIMER_0_MODE_2;
+ outb(devpriv->b_TimerSelectMode,
+ devpriv->iobase + APCI3120_TIMER_CRT1);
+
+ //Select Timer 0
+ b_Tmp = ((devpriv->
+ b_DigitalOutputRegister) & 0xF0) |
+ APCI3120_SELECT_TIMER_0_WORD;
+ outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
+
+ //Set the convertion time
+ outw(us_ConvertTiming,
+ devpriv->iobase + APCI3120_TIMER_VALUE);
+
+ //Set the scan bit
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister | APCI3120_ENABLE_SCAN;
+ outb(devpriv->b_ModeSelectRegister,
+ devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
+
+ //If Interrupt function is loaded
+ if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) {
+ //Disables the EOC,DMA and enables the EOS interrupt
+ devpriv->b_ModeSelectRegister =
+ (devpriv->
+ b_ModeSelectRegister &
+ APCI3120_DISABLE_EOC_INT) |
+ APCI3120_ENABLE_EOS_INT;
+ inw(devpriv->iobase);
+
+ } else
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister &
+ APCI3120_DISABLE_ALL_INTERRUPT_WITHOUT_TIMER;
+
+ outb(devpriv->b_ModeSelectRegister,
+ devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
+
+ inw(devpriv->iobase + APCI3120_RD_STATUS);
+
+ //Sets gate 0
+
+ devpriv->us_OutputRegister =
+ devpriv->
+ us_OutputRegister | APCI3120_ENABLE_TIMER0;
+ outw(devpriv->us_OutputRegister,
+ devpriv->iobase + APCI3120_WR_ADDRESS);
+
+ //Start conversion
+ outw(0, devpriv->iobase + APCI3120_START_CONVERSION);
+
+ //Waiting of end of convertion if interrupt is not installed
+ if (devpriv->b_EocEosInterrupt == APCI3120_DISABLE) {
+ //Waiting the end of convertion
+ do {
+ us_TmpValue =
+ inw(devpriv->iobase +
+ APCI3120_RD_STATUS);
+ }
+ while ((us_TmpValue & APCI3120_EOS) !=
+ APCI3120_EOS);
+
+ for (i = 0; i < devpriv->ui_AiNbrofChannels;
+ i++) {
+ //Read the result in FIFO and write them in shared memory
+ us_TmpValue = inw(devpriv->iobase);
+ data[i] = (UINT) us_TmpValue;
+ }
+
+ devpriv->b_InterruptMode = APCI3120_EOC_MODE; // Restore defaults.
+ }
+ break;
+
+ default:
+ printk("inputs wrong\n");
+
+ }
+ devpriv->ui_EocEosConversionTime = 0; // re initializing the variable;
+ }
+
+ return insn->n;
+
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3120_StopCyclicAcquisition(struct comedi_device *dev,|
+| struct comedi_subdevice *s)|
+| |
++----------------------------------------------------------------------------+
+| Task : Stops Cyclic acquisition |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| struct comedi_subdevice *s |
+| |
++----------------------------------------------------------------------------+
+| Return Value :0 |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_StopCyclicAcquisition(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ // Disable A2P Fifo write and AMWEN signal
+ outw(0, devpriv->i_IobaseAddon + 4);
+
+ //Disable Bus Master ADD ON
+ outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0);
+ outw(0, devpriv->i_IobaseAddon + 2);
+ outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0);
+ outw(0, devpriv->i_IobaseAddon + 2);
+
+ //Disable BUS Master PCI
+ outl(0, devpriv->i_IobaseAmcc + AMCC_OP_REG_MCSR);
+
+ //outl(inl(devpriv->i_IobaseAmcc+AMCC_OP_REG_INTCSR)&(~AINT_WRITE_COMPL), devpriv->i_IobaseAmcc+AMCC_OP_REG_INTCSR); // stop amcc irqs
+ //outl(inl(devpriv->i_IobaseAmcc+AMCC_OP_REG_MCSR)&(~EN_A2P_TRANSFERS), devpriv->i_IobaseAmcc+AMCC_OP_REG_MCSR); // stop DMA
+
+ //Disable ext trigger
+ i_APCI3120_ExttrigDisable(dev);
+
+ devpriv->us_OutputRegister = 0;
+ //stop counters
+ outw(devpriv->
+ us_OutputRegister & APCI3120_DISABLE_TIMER0 &
+ APCI3120_DISABLE_TIMER1, dev->iobase + APCI3120_WR_ADDRESS);
+
+ outw(APCI3120_DISABLE_ALL_TIMER, dev->iobase + APCI3120_WR_ADDRESS);
+
+ //DISABLE_ALL_INTERRUPT
+ outb(APCI3120_DISABLE_ALL_INTERRUPT,
+ dev->iobase + APCI3120_WRITE_MODE_SELECT);
+ //Flush FIFO
+ inb(dev->iobase + APCI3120_RESET_FIFO);
+ inw(dev->iobase + APCI3120_RD_STATUS);
+ devpriv->ui_AiActualScan = 0;
+ devpriv->ui_AiActualScanPosition = 0;
+ s->async->cur_chan = 0;
+ devpriv->ui_AiBufferPtr = 0;
+ devpriv->b_AiContinuous = 0;
+ devpriv->ui_DmaActualBuffer = 0;
+
+ devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE;
+ devpriv->b_InterruptMode = APCI3120_EOC_MODE;
+ devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
+ i_APCI3120_Reset(dev);
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3120_CommandTestAnalogInput(struct comedi_device *dev|
+| ,struct comedi_subdevice *s,struct comedi_cmd *cmd) |
+| |
++----------------------------------------------------------------------------+
+| Task : Test validity for a command for cyclic anlog input |
+| acquisition |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| struct comedi_subdevice *s |
+| struct comedi_cmd *cmd |
++----------------------------------------------------------------------------+
+| Return Value :0 |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_CommandTestAnalogInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp; // divisor1,divisor2;
+
+ // step 1: make sure trigger sources are trivially valid
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_EXT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ //step 2: make sure trigger sources are unique and mutually compatible
+
+ if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT) {
+ err++;
+ }
+
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_FOLLOW)
+ err++;
+
+ if (cmd->convert_src != TRIG_TIMER)
+ err++;
+
+ if (cmd->scan_end_src != TRIG_COUNT) {
+ cmd->scan_end_src = TRIG_COUNT;
+ err++;
+ }
+
+ if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
+ err++;
+
+ if (err)
+ return 2;
+
+ // step 3: make sure arguments are trivially compatible
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) // Test Delay timing
+ {
+ if (cmd->scan_begin_arg < this_board->ui_MinDelaytimeNs) {
+ cmd->scan_begin_arg = this_board->ui_MinDelaytimeNs;
+ err++;
+ }
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) // Test Acquisition timing
+ {
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if ((cmd->convert_arg)
+ && (cmd->convert_arg <
+ this_board->ui_MinAcquisitiontimeNs)) {
+ cmd->convert_arg =
+ this_board->ui_MinAcquisitiontimeNs;
+ err++;
+ }
+ } else {
+ if (cmd->convert_arg <
+ this_board->ui_MinAcquisitiontimeNs) {
+ cmd->convert_arg =
+ this_board->ui_MinAcquisitiontimeNs;
+ err++;
+
+ }
+ }
+ }
+
+ if (!cmd->chanlist_len) {
+ cmd->chanlist_len = 1;
+ err++;
+ }
+ if (cmd->chanlist_len > this_board->i_AiChannelList) {
+ cmd->chanlist_len = this_board->i_AiChannelList;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (!cmd->stop_arg) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ } else { // TRIG_NONE
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ // step 4: fix up any arguments
+
+ if (cmd->convert_src == TRIG_TIMER) {
+
+ if (cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->scan_begin_arg <
+ cmd->convert_arg * cmd->scan_end_arg) {
+ cmd->scan_begin_arg =
+ cmd->convert_arg * cmd->scan_end_arg;
+ err++;
+ }
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name : int i_APCI3120_CommandAnalogInput(struct comedi_device *dev, |
+| struct comedi_subdevice *s) |
+| |
++----------------------------------------------------------------------------+
+| Task : Does asynchronous acquisition |
+| Determines the mode 1 or 2. |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| struct comedi_subdevice *s |
+| |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_CommandAnalogInput(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ //loading private structure with cmd structure inputs
+ devpriv->ui_AiFlags = cmd->flags;
+ devpriv->ui_AiNbrofChannels = cmd->chanlist_len;
+ devpriv->ui_AiScanLength = cmd->scan_end_arg;
+ devpriv->pui_AiChannelList = cmd->chanlist;
+
+ //UPDATE-0.7.57->0.7.68devpriv->AiData=s->async->data;
+ devpriv->AiData = s->async->prealloc_buf;
+ //UPDATE-0.7.57->0.7.68devpriv->ui_AiDataLength=s->async->data_len;
+ devpriv->ui_AiDataLength = s->async->prealloc_bufsz;
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ devpriv->ui_AiNbrofScans = cmd->stop_arg;
+ } else {
+ devpriv->ui_AiNbrofScans = 0;
+ }
+
+ devpriv->ui_AiTimer0 = 0; // variables changed to timer0,timer1
+ devpriv->ui_AiTimer1 = 0;
+ if ((devpriv->ui_AiNbrofScans == 0) || (devpriv->ui_AiNbrofScans == -1))
+ devpriv->b_AiContinuous = 1; // user want neverending analog acquisition
+ // stopped using cancel
+
+ if (cmd->start_src == TRIG_EXT)
+ devpriv->b_ExttrigEnable = APCI3120_ENABLE;
+ else
+ devpriv->b_ExttrigEnable = APCI3120_DISABLE;
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ // mode 1 or 3
+ if (cmd->convert_src == TRIG_TIMER) {
+ // mode 1
+
+ devpriv->ui_AiTimer0 = cmd->convert_arg; // timer constant in nano seconds
+ //return this_board->i_hwdrv_CommandAnalogInput(1,dev,s);
+ return i_APCI3120_CyclicAnalogInput(1, dev, s);
+ }
+
+ }
+ if ((cmd->scan_begin_src == TRIG_TIMER)
+ && (cmd->convert_src == TRIG_TIMER)) {
+ // mode 2
+ devpriv->ui_AiTimer1 = cmd->scan_begin_arg;
+ devpriv->ui_AiTimer0 = cmd->convert_arg; // variable changed timer2 to timer0
+ //return this_board->i_hwdrv_CommandAnalogInput(2,dev,s);
+ return i_APCI3120_CyclicAnalogInput(2, dev, s);
+ }
+ return -1;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name : int i_APCI3120_CyclicAnalogInput(int mode, |
+| struct comedi_device * dev,struct comedi_subdevice * s) |
++----------------------------------------------------------------------------+
+| Task : This is used for analog input cyclic acquisition |
+| Performs the command operations. |
+| If DMA is configured does DMA initialization |
+| otherwise does the acquisition with EOS interrupt. |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : |
+| |
+| |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_CyclicAnalogInput(int mode, struct comedi_device * dev,
+ struct comedi_subdevice * s)
+{
+ BYTE b_Tmp;
+ UINT ui_Tmp, ui_DelayTiming = 0, ui_TimerValue1 = 0, dmalen0 =
+ 0, dmalen1 = 0, ui_TimerValue2 =
+ 0, ui_TimerValue0, ui_ConvertTiming;
+ USHORT us_TmpValue;
+
+ //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver
+ //devpriv->b_AiCyclicAcquisition=APCI3120_ENABLE;
+ //END JK 07.05.04: Comparison between WIN32 and Linux driver
+
+ /*******************/
+ /* Resets the FIFO */
+ /*******************/
+ inb(dev->iobase + APCI3120_RESET_FIFO);
+
+ //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver
+ //inw(dev->iobase+APCI3120_RD_STATUS);
+ //END JK 07.05.04: Comparison between WIN32 and Linux driver
+
+ /***************************/
+ /* Acquisition initialized */
+ /***************************/
+ //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver
+ devpriv->b_AiCyclicAcquisition = APCI3120_ENABLE;
+ //END JK 07.05.04: Comparison between WIN32 and Linux driver
+
+ // clear software registers
+ devpriv->b_TimerSelectMode = 0;
+ devpriv->us_OutputRegister = 0;
+ devpriv->b_ModeSelectRegister = 0;
+ //devpriv->b_DigitalOutputRegister=0;
+
+ //COMMENT JK 07.05.04: Followings calls are in i_APCI3120_StartAnalogInputAcquisition
+
+ /****************************/
+ /* Clear Timer Write TC INT */
+ /****************************/
+ outl(APCI3120_CLEAR_WRITE_TC_INT,
+ devpriv->i_IobaseAmcc + APCI3120_AMCC_OP_REG_INTCSR);
+
+ /************************************/
+ /* Clears the timer status register */
+ /************************************/
+ //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver
+ //inw(dev->iobase+APCI3120_TIMER_STATUS_REGISTER);
+ inb(dev->iobase + APCI3120_TIMER_STATUS_REGISTER);
+ //END JK 07.05.04: Comparison between WIN32 and Linux driver
+
+ /**************************/
+ /* Disables All Timer */
+ /* Sets PR and PA to 0 */
+ /**************************/
+ devpriv->us_OutputRegister = devpriv->us_OutputRegister &
+ APCI3120_DISABLE_TIMER0 &
+ APCI3120_DISABLE_TIMER1 & APCI3120_CLEAR_PA_PR;
+
+ outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS);
+
+ /*******************/
+ /* Resets the FIFO */
+ /*******************/
+ //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver
+ inb(devpriv->iobase + APCI3120_RESET_FIFO);
+ //END JK 07.05.04: Comparison between WIN32 and Linux driver
+
+ devpriv->ui_AiActualScan = 0;
+ devpriv->ui_AiActualScanPosition = 0;
+ s->async->cur_chan = 0;
+ devpriv->ui_AiBufferPtr = 0;
+ devpriv->ui_DmaActualBuffer = 0;
+
+ // value for timer2 minus -2 has to be done .....dunno y??
+ ui_TimerValue2 = devpriv->ui_AiNbrofScans - 2;
+ ui_ConvertTiming = devpriv->ui_AiTimer0;
+
+ if (mode == 2)
+ ui_DelayTiming = devpriv->ui_AiTimer1;
+
+ /**********************************/
+ /* Initializes the sequence array */
+ /**********************************/
+ if (!i_APCI3120_SetupChannelList(dev, s, devpriv->ui_AiNbrofChannels,
+ devpriv->pui_AiChannelList, 0))
+ return -EINVAL;
+
+ us_TmpValue = (USHORT) inw(dev->iobase + APCI3120_RD_STATUS);
+/*** EL241003 : add this section in comment because floats must not be used
+ if((us_TmpValue & 0x00B0)==0x00B0)
+ {
+ f_ConvertValue=(((float)ui_ConvertTiming * 0.002) - 2);
+ ui_TimerValue0=(UINT)f_ConvertValue;
+ if (mode==2)
+ {
+ f_DelayValue = (((float)ui_DelayTiming * 0.00002) - 2);
+ ui_TimerValue1 = (UINT) f_DelayValue;
+ }
+ }
+ else
+ {
+ f_ConvertValue=(((float)ui_ConvertTiming * 0.0012926) - 1);
+ ui_TimerValue0=(UINT)f_ConvertValue;
+ if (mode == 2)
+ {
+ f_DelayValue = (((float)ui_DelayTiming * 0.000012926) - 1);
+ ui_TimerValue1 = (UINT) f_DelayValue;
+ }
+ }
+***********************************************************************************************/
+/*** EL241003 Begin : add this section to replace floats calculation by integer calculations **/
+ //EL250804: Testing if board APCI3120 have the new Quartz or if it is an APCI3001
+ if ((us_TmpValue & 0x00B0) == 0x00B0
+ || !strcmp(this_board->pc_DriverName, "apci3001")) {
+ ui_TimerValue0 = ui_ConvertTiming * 2 - 2000;
+ ui_TimerValue0 = ui_TimerValue0 / 1000;
+
+ if (mode == 2) {
+ ui_DelayTiming = ui_DelayTiming / 1000;
+ ui_TimerValue1 = ui_DelayTiming * 2 - 200;
+ ui_TimerValue1 = ui_TimerValue1 / 100;
+ }
+ } else {
+ ui_ConvertTiming = ui_ConvertTiming / 1000;
+ ui_TimerValue0 = ui_ConvertTiming * 12926 - 10000;
+ ui_TimerValue0 = ui_TimerValue0 / 10000;
+
+ if (mode == 2) {
+ ui_DelayTiming = ui_DelayTiming / 1000;
+ ui_TimerValue1 = ui_DelayTiming * 12926 - 1;
+ ui_TimerValue1 = ui_TimerValue1 / 1000000;
+ }
+ }
+/*** EL241003 End ******************************************************************************/
+
+ if (devpriv->b_ExttrigEnable == APCI3120_ENABLE) {
+ i_APCI3120_ExttrigEnable(dev); // activate EXT trigger
+ }
+ switch (mode) {
+ case 1:
+ // init timer0 in mode 2
+ devpriv->b_TimerSelectMode =
+ (devpriv->
+ b_TimerSelectMode & 0xFC) | APCI3120_TIMER_0_MODE_2;
+ outb(devpriv->b_TimerSelectMode,
+ dev->iobase + APCI3120_TIMER_CRT1);
+
+ //Select Timer 0
+ b_Tmp = ((devpriv->
+ b_DigitalOutputRegister) & 0xF0) |
+ APCI3120_SELECT_TIMER_0_WORD;
+ outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
+ //Set the convertion time
+ outw(((USHORT) ui_TimerValue0),
+ dev->iobase + APCI3120_TIMER_VALUE);
+ break;
+
+ case 2:
+ // init timer1 in mode 2
+ devpriv->b_TimerSelectMode =
+ (devpriv->
+ b_TimerSelectMode & 0xF3) | APCI3120_TIMER_1_MODE_2;
+ outb(devpriv->b_TimerSelectMode,
+ dev->iobase + APCI3120_TIMER_CRT1);
+
+ //Select Timer 1
+ b_Tmp = ((devpriv->
+ b_DigitalOutputRegister) & 0xF0) |
+ APCI3120_SELECT_TIMER_1_WORD;
+ outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
+ //Set the convertion time
+ outw(((USHORT) ui_TimerValue1),
+ dev->iobase + APCI3120_TIMER_VALUE);
+
+ // init timer0 in mode 2
+ devpriv->b_TimerSelectMode =
+ (devpriv->
+ b_TimerSelectMode & 0xFC) | APCI3120_TIMER_0_MODE_2;
+ outb(devpriv->b_TimerSelectMode,
+ dev->iobase + APCI3120_TIMER_CRT1);
+
+ //Select Timer 0
+ b_Tmp = ((devpriv->
+ b_DigitalOutputRegister) & 0xF0) |
+ APCI3120_SELECT_TIMER_0_WORD;
+ outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
+
+ //Set the convertion time
+ outw(((USHORT) ui_TimerValue0),
+ dev->iobase + APCI3120_TIMER_VALUE);
+ break;
+
+ }
+ // ##########common for all modes#################
+
+ /***********************/
+ /* Clears the SCAN bit */
+ /***********************/
+ //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver
+ //devpriv->b_ModeSelectRegister=devpriv->b_ModeSelectRegister | APCI3120_DISABLE_SCAN;
+ devpriv->b_ModeSelectRegister = devpriv->b_ModeSelectRegister &
+ APCI3120_DISABLE_SCAN;
+ //END JK 07.05.04: Comparison between WIN32 and Linux driver
+ outb(devpriv->b_ModeSelectRegister,
+ dev->iobase + APCI3120_WRITE_MODE_SELECT);
+
+ // If DMA is disabled
+ if (devpriv->us_UseDma == APCI3120_DISABLE) {
+ // disable EOC and enable EOS
+ devpriv->b_InterruptMode = APCI3120_EOS_MODE;
+ devpriv->b_EocEosInterrupt = APCI3120_ENABLE;
+
+ devpriv->b_ModeSelectRegister =
+ (devpriv->
+ b_ModeSelectRegister & APCI3120_DISABLE_EOC_INT) |
+ APCI3120_ENABLE_EOS_INT;
+ outb(devpriv->b_ModeSelectRegister,
+ dev->iobase + APCI3120_WRITE_MODE_SELECT);
+
+ if (!devpriv->b_AiContinuous) {
+ // configure Timer2 For counting EOS
+ //Reset gate 2 of Timer 2 to disable it (Set Bit D14 to 0)
+ devpriv->us_OutputRegister =
+ devpriv->
+ us_OutputRegister & APCI3120_DISABLE_TIMER2;
+ outw(devpriv->us_OutputRegister,
+ dev->iobase + APCI3120_WR_ADDRESS);
+
+ // DISABLE TIMER INTERRUPT
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister &
+ APCI3120_DISABLE_TIMER_INT & 0xEF;
+ outb(devpriv->b_ModeSelectRegister,
+ dev->iobase + APCI3120_WRITE_MODE_SELECT);
+
+ //(1) Init timer 2 in mode 0 and write timer value
+ devpriv->b_TimerSelectMode =
+ (devpriv->
+ b_TimerSelectMode & 0x0F) |
+ APCI3120_TIMER_2_MODE_0;
+ outb(devpriv->b_TimerSelectMode,
+ dev->iobase + APCI3120_TIMER_CRT1);
+
+ //Writing LOW WORD
+ b_Tmp = ((devpriv->
+ b_DigitalOutputRegister) & 0xF0) |
+ APCI3120_SELECT_TIMER_2_LOW_WORD;
+ outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
+ outw(LOWORD(ui_TimerValue2),
+ dev->iobase + APCI3120_TIMER_VALUE);
+
+ //Writing HIGH WORD
+ b_Tmp = ((devpriv->
+ b_DigitalOutputRegister) & 0xF0) |
+ APCI3120_SELECT_TIMER_2_HIGH_WORD;
+ outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
+ outw(HIWORD(ui_TimerValue2),
+ dev->iobase + APCI3120_TIMER_VALUE);
+
+ //(2) Reset FC_TIMER BIT Clearing timer status register
+ inb(dev->iobase + APCI3120_TIMER_STATUS_REGISTER);
+ // enable timer counter and disable watch dog
+ devpriv->b_ModeSelectRegister =
+ (devpriv->
+ b_ModeSelectRegister |
+ APCI3120_ENABLE_TIMER_COUNTER) &
+ APCI3120_DISABLE_WATCHDOG;
+ // select EOS clock input for timer 2
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister |
+ APCI3120_TIMER2_SELECT_EOS;
+ // Enable timer2 interrupt
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister |
+ APCI3120_ENABLE_TIMER_INT;
+ outb(devpriv->b_ModeSelectRegister,
+ dev->iobase + APCI3120_WRITE_MODE_SELECT);
+ devpriv->b_Timer2Mode = APCI3120_COUNTER;
+ devpriv->b_Timer2Interrupt = APCI3120_ENABLE;
+ }
+ } else {
+ // If DMA Enabled
+ //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver
+ //inw(dev->iobase+0);// reset EOC bit
+ //END JK 07.05.04: Comparison between WIN32 and Linux driver
+ devpriv->b_InterruptMode = APCI3120_DMA_MODE;
+
+ /************************************/
+ /* Disables the EOC, EOS interrupt */
+ /************************************/
+ devpriv->b_ModeSelectRegister = devpriv->b_ModeSelectRegister &
+ APCI3120_DISABLE_EOC_INT & APCI3120_DISABLE_EOS_INT;
+
+ outb(devpriv->b_ModeSelectRegister,
+ dev->iobase + APCI3120_WRITE_MODE_SELECT);
+
+ dmalen0 = devpriv->ui_DmaBufferSize[0];
+ dmalen1 = devpriv->ui_DmaBufferSize[1];
+
+ if (!devpriv->b_AiContinuous) {
+
+ if (dmalen0 > (devpriv->ui_AiNbrofScans * devpriv->ui_AiScanLength * 2)) { // must we fill full first buffer?
+ dmalen0 =
+ devpriv->ui_AiNbrofScans *
+ devpriv->ui_AiScanLength * 2;
+ } else if (dmalen1 > (devpriv->ui_AiNbrofScans * devpriv->ui_AiScanLength * 2 - dmalen0)) // and must we fill full second buffer when first is once filled?
+ dmalen1 =
+ devpriv->ui_AiNbrofScans *
+ devpriv->ui_AiScanLength * 2 - dmalen0;
+ }
+
+ if (devpriv->ui_AiFlags & TRIG_WAKE_EOS) {
+ // don't we want wake up every scan?
+ if (dmalen0 > (devpriv->ui_AiScanLength * 2)) {
+ dmalen0 = devpriv->ui_AiScanLength * 2;
+ if (devpriv->ui_AiScanLength & 1)
+ dmalen0 += 2;
+ }
+ if (dmalen1 > (devpriv->ui_AiScanLength * 2)) {
+ dmalen1 = devpriv->ui_AiScanLength * 2;
+ if (devpriv->ui_AiScanLength & 1)
+ dmalen1 -= 2;
+ if (dmalen1 < 4)
+ dmalen1 = 4;
+ }
+ } else { // isn't output buff smaller that our DMA buff?
+ if (dmalen0 > (devpriv->ui_AiDataLength)) {
+ dmalen0 = devpriv->ui_AiDataLength;
+ }
+ if (dmalen1 > (devpriv->ui_AiDataLength)) {
+ dmalen1 = devpriv->ui_AiDataLength;
+ }
+ }
+ devpriv->ui_DmaBufferUsesize[0] = dmalen0;
+ devpriv->ui_DmaBufferUsesize[1] = dmalen1;
+
+ //Initialize DMA
+
+ // Set Transfer count enable bit and A2P_fifo reset bit in AGCSTS register
+ //1
+ ui_Tmp = AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO;
+ outl(ui_Tmp, devpriv->i_IobaseAmcc + AMCC_OP_REG_AGCSTS);
+
+ // changed since 16 bit interface for add on
+ /*********************/
+ /* ENABLE BUS MASTER */
+ /*********************/
+ outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0);
+ outw(APCI3120_ENABLE_TRANSFER_ADD_ON_LOW,
+ devpriv->i_IobaseAddon + 2);
+
+ outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0);
+ outw(APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH,
+ devpriv->i_IobaseAddon + 2);
+
+ // TO VERIFIED
+ //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver
+ outw(0x1000, devpriv->i_IobaseAddon + 2);
+ //END JK 07.05.04: Comparison between WIN32 and Linux driver
+
+ //2 No change
+ // A2P FIFO MANAGEMENT
+ // A2P fifo reset & transfer control enable
+ /***********************/
+ /* A2P FIFO MANAGEMENT */
+ /***********************/
+ outl(APCI3120_A2P_FIFO_MANAGEMENT, devpriv->i_IobaseAmcc +
+ APCI3120_AMCC_OP_MCSR);
+
+ //3
+ //beginning address of dma buf
+ //The 32 bit address of dma buffer is converted into two 16 bit addresses
+ // Can done by using _attach and put into into an array
+ // array used may be for differnet pages
+
+ // DMA Start Adress Low
+ outw(APCI3120_ADD_ON_MWAR_LOW, devpriv->i_IobaseAddon + 0);
+ outw((devpriv->ul_DmaBufferHw[0] & 0xFFFF),
+ devpriv->i_IobaseAddon + 2);
+
+ /*************************/
+ /* DMA Start Adress High */
+ /*************************/
+ outw(APCI3120_ADD_ON_MWAR_HIGH, devpriv->i_IobaseAddon + 0);
+ outw((devpriv->ul_DmaBufferHw[0] / 65536),
+ devpriv->i_IobaseAddon + 2);
+
+ //4
+ // amount of bytes to be transfered set transfer count
+ // used ADDON MWTC register
+ //commented testing outl(devpriv->ui_DmaBufferUsesize[0], devpriv->i_IobaseAddon+AMCC_OP_REG_AMWTC);
+
+ /**************************/
+ /* Nbr of acquisition LOW */
+ /**************************/
+ outw(APCI3120_ADD_ON_MWTC_LOW, devpriv->i_IobaseAddon + 0);
+ outw((devpriv->ui_DmaBufferUsesize[0] & 0xFFFF),
+ devpriv->i_IobaseAddon + 2);
+
+ /***************************/
+ /* Nbr of acquisition HIGH */
+ /***************************/
+ outw(APCI3120_ADD_ON_MWTC_HIGH, devpriv->i_IobaseAddon + 0);
+ outw((devpriv->ui_DmaBufferUsesize[0] / 65536),
+ devpriv->i_IobaseAddon + 2);
+
+ //5
+ // To configure A2P FIFO
+ // testing outl( FIFO_ADVANCE_ON_BYTE_2,devpriv->i_IobaseAmcc+AMCC_OP_REG_INTCSR);
+
+ /******************/
+ /* A2P FIFO RESET */
+ /******************/
+ // TO VERIFY
+ //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver
+ outl(0x04000000UL, devpriv->i_IobaseAmcc + AMCC_OP_REG_MCSR);
+ //END JK 07.05.04: Comparison between WIN32 and Linux driver
+
+ //6
+ //ENABLE A2P FIFO WRITE AND ENABLE AMWEN
+ // AMWEN_ENABLE | A2P_FIFO_WRITE_ENABLE (0x01|0x02)=0x03
+ //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver
+ //outw(3,devpriv->i_IobaseAddon + 4);
+ //END JK 07.05.04: Comparison between WIN32 and Linux driver
+
+ //7
+ //initialise end of dma interrupt AINT_WRITE_COMPL = ENABLE_WRITE_TC_INT(ADDI)
+ /***************************************************/
+ /* A2P FIFO CONFIGURATE, END OF DMA INTERRUPT INIT */
+ /***************************************************/
+ outl((APCI3120_FIFO_ADVANCE_ON_BYTE_2 |
+ APCI3120_ENABLE_WRITE_TC_INT),
+ devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR);
+
+ //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver
+ /******************************************/
+ /* ENABLE A2P FIFO WRITE AND ENABLE AMWEN */
+ /******************************************/
+ outw(3, devpriv->i_IobaseAddon + 4);
+ //END JK 07.05.04: Comparison between WIN32 and Linux driver
+
+ /******************/
+ /* A2P FIFO RESET */
+ /******************/
+ //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver
+ outl(0x04000000UL,
+ devpriv->i_IobaseAmcc + APCI3120_AMCC_OP_MCSR);
+ //END JK 07.05.04: Comparison between WIN32 and Linux driver
+ }
+
+ if ((devpriv->us_UseDma == APCI3120_DISABLE)
+ && !devpriv->b_AiContinuous) {
+ // set gate 2 to start conversion
+ devpriv->us_OutputRegister =
+ devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER2;
+ outw(devpriv->us_OutputRegister,
+ dev->iobase + APCI3120_WR_ADDRESS);
+ }
+
+ switch (mode) {
+ case 1:
+ // set gate 0 to start conversion
+ devpriv->us_OutputRegister =
+ devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER0;
+ outw(devpriv->us_OutputRegister,
+ dev->iobase + APCI3120_WR_ADDRESS);
+ break;
+ case 2:
+ // set gate 0 and gate 1
+ devpriv->us_OutputRegister =
+ devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER1;
+ devpriv->us_OutputRegister =
+ devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER0;
+ outw(devpriv->us_OutputRegister,
+ dev->iobase + APCI3120_WR_ADDRESS);
+ break;
+
+ }
+
+ return 0;
+
+}
+
+/*
++----------------------------------------------------------------------------+
+| INTERNAL FUNCTIONS |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function name : int i_APCI3120_Reset(struct comedi_device *dev) |
+| |
+| |
++----------------------------------------------------------------------------+
+| Task : Hardware reset function |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| |
+| |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_Reset(struct comedi_device * dev)
+{
+ unsigned int i;
+ unsigned short us_TmpValue;
+
+ devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE;
+ devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
+ devpriv->b_InterruptMode = APCI3120_EOC_MODE;
+ devpriv->ui_EocEosConversionTime = 0; // set eoc eos conv time to 0
+ devpriv->b_OutputMemoryStatus = 0;
+
+ // variables used in timer subdevice
+ devpriv->b_Timer2Mode = 0;
+ devpriv->b_Timer2Interrupt = 0;
+ devpriv->b_ExttrigEnable = 0; // Disable ext trigger
+
+ /* Disable all interrupts, watchdog for the anolog output */
+ devpriv->b_ModeSelectRegister = 0;
+ outb(devpriv->b_ModeSelectRegister,
+ dev->iobase + APCI3120_WRITE_MODE_SELECT);
+
+ // Disables all counters, ext trigger and clears PA, PR
+ devpriv->us_OutputRegister = 0;
+ outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS);
+
+ //Code to set the all anolog o/p channel to 0v
+ //8191 is decimal value for zero(0 v)volt in bipolar mode(default)
+ outw(8191 | APCI3120_ANALOG_OP_CHANNEL_1, dev->iobase + APCI3120_ANALOG_OUTPUT_1); //channel 1
+ outw(8191 | APCI3120_ANALOG_OP_CHANNEL_2, dev->iobase + APCI3120_ANALOG_OUTPUT_1); //channel 2
+ outw(8191 | APCI3120_ANALOG_OP_CHANNEL_3, dev->iobase + APCI3120_ANALOG_OUTPUT_1); //channel 3
+ outw(8191 | APCI3120_ANALOG_OP_CHANNEL_4, dev->iobase + APCI3120_ANALOG_OUTPUT_1); //channel 4
+
+ outw(8191 | APCI3120_ANALOG_OP_CHANNEL_5, dev->iobase + APCI3120_ANALOG_OUTPUT_2); //channel 5
+ outw(8191 | APCI3120_ANALOG_OP_CHANNEL_6, dev->iobase + APCI3120_ANALOG_OUTPUT_2); //channel 6
+ outw(8191 | APCI3120_ANALOG_OP_CHANNEL_7, dev->iobase + APCI3120_ANALOG_OUTPUT_2); //channel 7
+ outw(8191 | APCI3120_ANALOG_OP_CHANNEL_8, dev->iobase + APCI3120_ANALOG_OUTPUT_2); //channel 8
+
+ // Reset digital output to L0W
+
+//ES05 outb(0x0,dev->iobase+APCI3120_DIGITAL_OUTPUT);
+ udelay(10);
+
+ inw(dev->iobase + 0); //make a dummy read
+ inb(dev->iobase + APCI3120_RESET_FIFO); // flush FIFO
+ inw(dev->iobase + APCI3120_RD_STATUS); // flush A/D status register
+
+ //code to reset the RAM sequence
+ for (i = 0; i < 16; i++) {
+ us_TmpValue = i << 8; //select the location
+ outw(us_TmpValue, dev->iobase + APCI3120_SEQ_RAM_ADDRESS);
+ }
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name : int i_APCI3120_SetupChannelList(struct comedi_device * dev, |
+| struct comedi_subdevice * s, int n_chan,unsigned int *chanlist|
+| ,char check) |
+| |
++----------------------------------------------------------------------------+
+| Task :This function will first check channel list is ok or not|
+|and then initialize the sequence RAM with the polarity, Gain,Channel number |
+|If the last argument of function "check"is 1 then it only checks the channel|
+|list is ok or not. |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device * dev |
+| struct comedi_subdevice * s |
+| int n_chan |
+ unsigned int *chanlist
+ char check
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_SetupChannelList(struct comedi_device * dev, struct comedi_subdevice * s,
+ int n_chan, unsigned int *chanlist, char check)
+{
+ unsigned int i; //, differencial=0, bipolar=0;
+ unsigned int gain;
+ unsigned short us_TmpValue;
+
+ /* correct channel and range number check itself comedi/range.c */
+ if (n_chan < 1) {
+ if (!check)
+ comedi_error(dev, "range/channel list is empty!");
+ return 0;
+ }
+ // All is ok, so we can setup channel/range list
+ if (check)
+ return 1;
+
+ //Code to set the PA and PR...Here it set PA to 0..
+ devpriv->us_OutputRegister =
+ devpriv->us_OutputRegister & APCI3120_CLEAR_PA_PR;
+ devpriv->us_OutputRegister = ((n_chan - 1) & 0xf) << 8;
+ outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS);
+
+ for (i = 0; i < n_chan; i++) {
+ // store range list to card
+ us_TmpValue = CR_CHAN(chanlist[i]); // get channel number;
+
+ if (CR_RANGE(chanlist[i]) < APCI3120_BIPOLAR_RANGES) {
+ us_TmpValue &= ((~APCI3120_UNIPOLAR) & 0xff); // set bipolar
+ } else {
+ us_TmpValue |= APCI3120_UNIPOLAR; // enable unipolar......
+ }
+
+ gain = CR_RANGE(chanlist[i]); // get gain number
+ us_TmpValue |= ((gain & 0x03) << 4); //<<4 for G0 and G1 bit in RAM
+ us_TmpValue |= i << 8; //To select the RAM LOCATION....
+ outw(us_TmpValue, dev->iobase + APCI3120_SEQ_RAM_ADDRESS);
+
+ printk("\n Gain = %i",
+ (((unsigned char)CR_RANGE(chanlist[i]) & 0x03) << 2));
+ printk("\n Channel = %i", CR_CHAN(chanlist[i]));
+ printk("\n Polarity = %i", us_TmpValue & APCI3120_UNIPOLAR);
+ }
+ return 1; // we can serve this with scan logic
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name : int i_APCI3120_ExttrigEnable(struct comedi_device * dev) |
+| |
+| |
++----------------------------------------------------------------------------+
+| Task : Enable the external trigger |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device * dev |
+| |
+| |
++----------------------------------------------------------------------------+
+| Return Value : 0 |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_ExttrigEnable(struct comedi_device * dev)
+{
+
+ devpriv->us_OutputRegister |= APCI3120_ENABLE_EXT_TRIGGER;
+ outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS);
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name : int i_APCI3120_ExttrigDisable(struct comedi_device * dev) |
+| |
++----------------------------------------------------------------------------+
+| Task : Disables the external trigger |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device * dev |
+| |
+| |
++----------------------------------------------------------------------------+
+| Return Value : 0 |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_ExttrigDisable(struct comedi_device * dev)
+{
+ devpriv->us_OutputRegister &= ~APCI3120_ENABLE_EXT_TRIGGER;
+ outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS);
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| INTERRUPT FUNCTIONS |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function name : void v_APCI3120_Interrupt(int irq, void *d) |
+| |
+| |
++----------------------------------------------------------------------------+
+| Task :Interrupt handler for APCI3120 |
+| When interrupt occurs this gets called. |
+| First it finds which interrupt has been generated and |
+| handles corresponding interrupt |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : int irq |
+| void *d |
+| |
++----------------------------------------------------------------------------+
+| Return Value : void |
+| |
++----------------------------------------------------------------------------+
+*/
+
+void v_APCI3120_Interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ USHORT int_daq;
+
+ unsigned int int_amcc, ui_Check, i;
+ USHORT us_TmpValue;
+ BYTE b_DummyRead;
+
+ struct comedi_subdevice *s = dev->subdevices + 0;
+ ui_Check = 1;
+
+ int_daq = inw(dev->iobase + APCI3120_RD_STATUS) & 0xf000; // get IRQ reasons
+ int_amcc = inl(devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR); // get AMCC INT register
+
+ if ((!int_daq) && (!(int_amcc & ANY_S593X_INT))) {
+ comedi_error(dev, "IRQ from unknow source");
+ return;
+ }
+
+ outl(int_amcc | 0x00ff0000, devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR); // shutdown IRQ reasons in AMCC
+
+ int_daq = (int_daq >> 12) & 0xF;
+
+ if (devpriv->b_ExttrigEnable == APCI3120_ENABLE) {
+ //Disable ext trigger
+ i_APCI3120_ExttrigDisable(dev);
+ devpriv->b_ExttrigEnable = APCI3120_DISABLE;
+ }
+ //clear the timer 2 interrupt
+ inb(devpriv->i_IobaseAmcc + APCI3120_TIMER_STATUS_REGISTER);
+
+ if (int_amcc & MASTER_ABORT_INT)
+ comedi_error(dev, "AMCC IRQ - MASTER DMA ABORT!");
+ if (int_amcc & TARGET_ABORT_INT)
+ comedi_error(dev, "AMCC IRQ - TARGET DMA ABORT!");
+
+ // Ckeck if EOC interrupt
+ if (((int_daq & 0x8) == 0)
+ && (devpriv->b_InterruptMode == APCI3120_EOC_MODE)) {
+ if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) {
+
+ // Read the AI Value
+
+ devpriv->ui_AiReadData[0] =
+ (UINT) inw(devpriv->iobase + 0);
+ devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
+ send_sig(SIGIO, devpriv->tsk_Current, 0); // send signal to the sample
+ } else {
+ //Disable EOC Interrupt
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister & APCI3120_DISABLE_EOC_INT;
+ outb(devpriv->b_ModeSelectRegister,
+ devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
+
+ }
+ }
+
+ // Check If EOS interrupt
+ if ((int_daq & 0x2) && (devpriv->b_InterruptMode == APCI3120_EOS_MODE)) {
+
+ if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) // enable this in without DMA ???
+ {
+
+ if (devpriv->b_AiCyclicAcquisition == APCI3120_ENABLE) {
+ ui_Check = 0;
+ i_APCI3120_InterruptHandleEos(dev);
+ devpriv->ui_AiActualScan++;
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister |
+ APCI3120_ENABLE_EOS_INT;
+ outb(devpriv->b_ModeSelectRegister,
+ dev->iobase +
+ APCI3120_WRITE_MODE_SELECT);
+ } else {
+ ui_Check = 0;
+ for (i = 0; i < devpriv->ui_AiNbrofChannels;
+ i++) {
+ us_TmpValue = inw(devpriv->iobase + 0);
+ devpriv->ui_AiReadData[i] =
+ (UINT) us_TmpValue;
+ }
+ devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
+ devpriv->b_InterruptMode = APCI3120_EOC_MODE;
+
+ send_sig(SIGIO, devpriv->tsk_Current, 0); // send signal to the sample
+
+ }
+
+ } else {
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister & APCI3120_DISABLE_EOS_INT;
+ outb(devpriv->b_ModeSelectRegister,
+ dev->iobase + APCI3120_WRITE_MODE_SELECT);
+ devpriv->b_EocEosInterrupt = APCI3120_DISABLE; //Default settings
+ devpriv->b_InterruptMode = APCI3120_EOC_MODE;
+ }
+
+ }
+ //Timer2 interrupt
+ if (int_daq & 0x1) {
+
+ switch (devpriv->b_Timer2Mode) {
+ case APCI3120_COUNTER:
+
+ devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE;
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister & APCI3120_DISABLE_EOS_INT;
+ outb(devpriv->b_ModeSelectRegister,
+ dev->iobase + APCI3120_WRITE_MODE_SELECT);
+
+ // stop timer 2
+ devpriv->us_OutputRegister =
+ devpriv->
+ us_OutputRegister & APCI3120_DISABLE_ALL_TIMER;
+ outw(devpriv->us_OutputRegister,
+ dev->iobase + APCI3120_WR_ADDRESS);
+
+ //stop timer 0 and timer 1
+ i_APCI3120_StopCyclicAcquisition(dev, s);
+ devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE;
+
+ //UPDATE-0.7.57->0.7.68comedi_done(dev,s);
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_event(dev, s);
+
+ break;
+
+ case APCI3120_TIMER:
+
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current, 0);
+ break;
+
+ case APCI3120_WATCHDOG:
+
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current, 0);
+ break;
+
+ default:
+
+ // disable Timer Interrupt
+
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister &
+ APCI3120_DISABLE_TIMER_INT;
+
+ outb(devpriv->b_ModeSelectRegister,
+ dev->iobase + APCI3120_WRITE_MODE_SELECT);
+
+ }
+
+ b_DummyRead = inb(dev->iobase + APCI3120_TIMER_STATUS_REGISTER);
+
+ }
+
+ if ((int_daq & 0x4) && (devpriv->b_InterruptMode == APCI3120_DMA_MODE)) {
+ if (devpriv->b_AiCyclicAcquisition == APCI3120_ENABLE) {
+
+ /****************************/
+ /* Clear Timer Write TC INT */
+ /****************************/
+
+ outl(APCI3120_CLEAR_WRITE_TC_INT,
+ devpriv->i_IobaseAmcc +
+ APCI3120_AMCC_OP_REG_INTCSR);
+
+ /************************************/
+ /* Clears the timer status register */
+ /************************************/
+ inw(dev->iobase + APCI3120_TIMER_STATUS_REGISTER);
+ v_APCI3120_InterruptDma(irq, d); // do some data transfer
+ } else {
+ /* Stops the Timer */
+ outw(devpriv->
+ us_OutputRegister & APCI3120_DISABLE_TIMER0 &
+ APCI3120_DISABLE_TIMER1,
+ dev->iobase + APCI3120_WR_ADDRESS);
+ }
+
+ }
+
+ return;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3120_InterruptHandleEos(struct comedi_device *dev) |
+| |
+| |
++----------------------------------------------------------------------------+
+| Task : This function handles EOS interrupt. |
+| This function copies the acquired data(from FIFO) |
+| to Comedi buffer. |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| |
+| |
++----------------------------------------------------------------------------+
+| Return Value : 0 |
+| |
++----------------------------------------------------------------------------+
+*/
+
+/*
+ * int i_APCI3120_InterruptHandleEos(struct comedi_device *dev)
+{
+ int n_chan,i;
+ short *data;
+ struct comedi_subdevice *s=dev->subdevices+0;
+ struct comedi_async *async = s->async;
+ data=async->data+async->buf_int_ptr;
+ n_chan=devpriv->ui_AiNbrofChannels;
+
+ for(i=0;i<n_chan;i++)
+ {
+ data[i]=inw(dev->iobase+0);
+ }
+ async->buf_int_count+=n_chan*sizeof(short);
+ async->buf_int_ptr+=n_chan*sizeof(short);
+ comedi_eos(dev,s);
+ if (s->async->buf_int_ptr>=s->async->data_len) // for buffer rool over
+ {
+*//* buffer rollover */
+/* s->async->buf_int_ptr=0;
+ comedi_eobuf(dev,s);
+ }
+ return 0;
+}*/
+int i_APCI3120_InterruptHandleEos(struct comedi_device * dev)
+{
+ int n_chan, i;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+ int err = 1;
+
+ n_chan = devpriv->ui_AiNbrofChannels;
+
+ s->async->events = 0;
+
+ for (i = 0; i < n_chan; i++)
+ err &= comedi_buf_put(s->async, inw(dev->iobase + 0));
+
+ s->async->events |= COMEDI_CB_EOS;
+
+ if (err == 0)
+ s->async->events |= COMEDI_CB_OVERFLOW;
+
+ comedi_event(dev, s);
+
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name : void v_APCI3120_InterruptDma(int irq, void *d) |
+| |
++----------------------------------------------------------------------------+
+| Task : This is a handler for the DMA interrupt |
+| This function copies the data to Comedi Buffer. |
+| For continuous DMA it reinitializes the DMA operation. |
+| For single mode DMA it stop the acquisition. |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : int irq, void *d |
+| |
++----------------------------------------------------------------------------+
+| Return Value : void |
+| |
++----------------------------------------------------------------------------+
+*/
+
+void v_APCI3120_InterruptDma(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+ unsigned int next_dma_buf, samplesinbuf;
+ unsigned long low_word, high_word, var;
+
+ UINT ui_Tmp;
+ samplesinbuf =
+ devpriv->ui_DmaBufferUsesize[devpriv->ui_DmaActualBuffer] -
+ inl(devpriv->i_IobaseAmcc + AMCC_OP_REG_MWTC);
+
+ if (samplesinbuf <
+ devpriv->ui_DmaBufferUsesize[devpriv->ui_DmaActualBuffer]) {
+ comedi_error(dev, "Interrupted DMA transfer!");
+ }
+ if (samplesinbuf & 1) {
+ comedi_error(dev, "Odd count of bytes in DMA ring!");
+ i_APCI3120_StopCyclicAcquisition(dev, s);
+ devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE;
+
+ return;
+ }
+ samplesinbuf = samplesinbuf >> 1; // number of received samples
+ if (devpriv->b_DmaDoubleBuffer) {
+ // switch DMA buffers if is used double buffering
+ next_dma_buf = 1 - devpriv->ui_DmaActualBuffer;
+
+ ui_Tmp = AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO;
+ outl(ui_Tmp, devpriv->i_IobaseAddon + AMCC_OP_REG_AGCSTS);
+
+ // changed since 16 bit interface for add on
+ outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0);
+ outw(APCI3120_ENABLE_TRANSFER_ADD_ON_LOW,
+ devpriv->i_IobaseAddon + 2);
+ outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0);
+ outw(APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH, devpriv->i_IobaseAddon + 2); // 0x1000 is out putted in windows driver
+
+ var = devpriv->ul_DmaBufferHw[next_dma_buf];
+ low_word = var & 0xffff;
+ var = devpriv->ul_DmaBufferHw[next_dma_buf];
+ high_word = var / 65536;
+
+ /* DMA Start Adress Low */
+ outw(APCI3120_ADD_ON_MWAR_LOW, devpriv->i_IobaseAddon + 0);
+ outw(low_word, devpriv->i_IobaseAddon + 2);
+
+ /* DMA Start Adress High */
+ outw(APCI3120_ADD_ON_MWAR_HIGH, devpriv->i_IobaseAddon + 0);
+ outw(high_word, devpriv->i_IobaseAddon + 2);
+
+ var = devpriv->ui_DmaBufferUsesize[next_dma_buf];
+ low_word = var & 0xffff;
+ var = devpriv->ui_DmaBufferUsesize[next_dma_buf];
+ high_word = var / 65536;
+
+ /* Nbr of acquisition LOW */
+ outw(APCI3120_ADD_ON_MWTC_LOW, devpriv->i_IobaseAddon + 0);
+ outw(low_word, devpriv->i_IobaseAddon + 2);
+
+ /* Nbr of acquisition HIGH */
+ outw(APCI3120_ADD_ON_MWTC_HIGH, devpriv->i_IobaseAddon + 0);
+ outw(high_word, devpriv->i_IobaseAddon + 2);
+
+ // To configure A2P FIFO
+ // ENABLE A2P FIFO WRITE AND ENABLE AMWEN
+ // AMWEN_ENABLE | A2P_FIFO_WRITE_ENABLE (0x01|0x02)=0x03
+ outw(3, devpriv->i_IobaseAddon + 4);
+ //initialise end of dma interrupt AINT_WRITE_COMPL = ENABLE_WRITE_TC_INT(ADDI)
+ outl((APCI3120_FIFO_ADVANCE_ON_BYTE_2 |
+ APCI3120_ENABLE_WRITE_TC_INT),
+ devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR);
+
+ }
+/*UPDATE-0.7.57->0.7.68
+ ptr=(short *)devpriv->ul_DmaBufferVirtual[devpriv->ui_DmaActualBuffer];
+
+
+ // if there is not enough space left in the buffer to copy all data contained in the DMABufferVirtual
+ if(s->async->buf_int_ptr+samplesinbuf*sizeof(short)>=devpriv->ui_AiDataLength)
+ {
+ m=(devpriv->ui_AiDataLength-s->async->buf_int_ptr)/sizeof(short);
+ v_APCI3120_InterruptDmaMoveBlock16bit(dev,s,(void *)ptr,((void *)(devpriv->AiData))+s->async->buf_int_ptr,m);
+ s->async->buf_int_count+=m*sizeof(short);
+ ptr+=m*sizeof(short);
+ samplesinbuf-=m;
+ s->async->buf_int_ptr=0;
+ comedi_eobuf(dev,s);
+ }
+
+ if (samplesinbuf)
+ {
+ v_APCI3120_InterruptDmaMoveBlock16bit(dev,s,(void *)ptr,((void *)(devpriv->AiData))+s->async->buf_int_ptr,samplesinbuf);
+
+ s->async->buf_int_count+=samplesinbuf*sizeof(short);
+ s->async->buf_int_ptr+=samplesinbuf*sizeof(short);
+ if (!(devpriv->ui_AiFlags & TRIG_WAKE_EOS))
+ {
+ comedi_bufcheck(dev,s);
+ }
+ }
+ if (!devpriv->b_AiContinuous)
+ if ( devpriv->ui_AiActualScan>=devpriv->ui_AiNbrofScans )
+ {
+ // all data sampled
+ i_APCI3120_StopCyclicAcquisition(dev,s);
+ devpriv->b_AiCyclicAcquisition=APCI3120_DISABLE;
+ //DPRINTK("\n Single DMA completed..\n");
+ comedi_done(dev,s);
+ return;
+ }
+*/
+ if (samplesinbuf) {
+ v_APCI3120_InterruptDmaMoveBlock16bit(dev, s,
+ devpriv->ul_DmaBufferVirtual[devpriv->
+ ui_DmaActualBuffer], samplesinbuf);
+
+ if (!(devpriv->ui_AiFlags & TRIG_WAKE_EOS)) {
+ s->async->events |= COMEDI_CB_EOS;
+ comedi_event(dev, s);
+ }
+ }
+ if (!devpriv->b_AiContinuous)
+ if (devpriv->ui_AiActualScan >= devpriv->ui_AiNbrofScans) {
+ // all data sampled
+ i_APCI3120_StopCyclicAcquisition(dev, s);
+ devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE;
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ return;
+ }
+
+ if (devpriv->b_DmaDoubleBuffer) { // switch dma buffers
+ devpriv->ui_DmaActualBuffer = 1 - devpriv->ui_DmaActualBuffer;
+ } else {
+ // restart DMA if is not used double buffering
+ //ADDED REINITIALISE THE DMA
+ ui_Tmp = AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO;
+ outl(ui_Tmp, devpriv->i_IobaseAddon + AMCC_OP_REG_AGCSTS);
+
+ // changed since 16 bit interface for add on
+ outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0);
+ outw(APCI3120_ENABLE_TRANSFER_ADD_ON_LOW,
+ devpriv->i_IobaseAddon + 2);
+ outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0);
+ outw(APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH, devpriv->i_IobaseAddon + 2); //
+ // A2P FIFO MANAGEMENT
+ // A2P fifo reset & transfer control enable
+ outl(APCI3120_A2P_FIFO_MANAGEMENT,
+ devpriv->i_IobaseAmcc + AMCC_OP_REG_MCSR);
+
+ var = devpriv->ul_DmaBufferHw[0];
+ low_word = var & 0xffff;
+ var = devpriv->ul_DmaBufferHw[0];
+ high_word = var / 65536;
+ outw(APCI3120_ADD_ON_MWAR_LOW, devpriv->i_IobaseAddon + 0);
+ outw(low_word, devpriv->i_IobaseAddon + 2);
+ outw(APCI3120_ADD_ON_MWAR_HIGH, devpriv->i_IobaseAddon + 0);
+ outw(high_word, devpriv->i_IobaseAddon + 2);
+
+ var = devpriv->ui_DmaBufferUsesize[0];
+ low_word = var & 0xffff; //changed
+ var = devpriv->ui_DmaBufferUsesize[0];
+ high_word = var / 65536;
+ outw(APCI3120_ADD_ON_MWTC_LOW, devpriv->i_IobaseAddon + 0);
+ outw(low_word, devpriv->i_IobaseAddon + 2);
+ outw(APCI3120_ADD_ON_MWTC_HIGH, devpriv->i_IobaseAddon + 0);
+ outw(high_word, devpriv->i_IobaseAddon + 2);
+
+ // To configure A2P FIFO
+ //ENABLE A2P FIFO WRITE AND ENABLE AMWEN
+ // AMWEN_ENABLE | A2P_FIFO_WRITE_ENABLE (0x01|0x02)=0x03
+ outw(3, devpriv->i_IobaseAddon + 4);
+ //initialise end of dma interrupt AINT_WRITE_COMPL = ENABLE_WRITE_TC_INT(ADDI)
+ outl((APCI3120_FIFO_ADVANCE_ON_BYTE_2 |
+ APCI3120_ENABLE_WRITE_TC_INT),
+ devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR);
+ }
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name :void v_APCI3120_InterruptDmaMoveBlock16bit(comedi_device|
+|*dev,struct comedi_subdevice *s,short *dma,short *data,int n) |
+| |
++----------------------------------------------------------------------------+
+| Task : This function copies the data from DMA buffer to the |
+| Comedi buffer |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| struct comedi_subdevice *s |
+| short *dma |
+| short *data,int n |
++----------------------------------------------------------------------------+
+| Return Value : void |
+| |
++----------------------------------------------------------------------------+
+*/
+
+/*void v_APCI3120_InterruptDmaMoveBlock16bit(struct comedi_device *dev,struct comedi_subdevice *s,short *dma,short *data,int n)
+{
+ int i,j,m;
+
+ j=s->async->cur_chan;
+ m=devpriv->ui_AiActualScanPosition;
+ for(i=0;i<n;i++)
+ {
+ *data=*dma;
+ data++; dma++;
+ j++;
+ if(j>=devpriv->ui_AiNbrofChannels)
+ {
+ m+=j;
+ j=0;
+ if(m>=devpriv->ui_AiScanLength)
+ {
+ m=0;
+ devpriv->ui_AiActualScan++;
+ if (devpriv->ui_AiFlags & TRIG_WAKE_EOS)
+;//UPDATE-0.7.57->0.7.68 comedi_eos(dev,s);
+ }
+ }
+ }
+ devpriv->ui_AiActualScanPosition=m;
+ s->async->cur_chan=j;
+
+}
+*/
+void v_APCI3120_InterruptDmaMoveBlock16bit(struct comedi_device * dev,
+ struct comedi_subdevice * s, short * dma_buffer, unsigned int num_samples)
+{
+ devpriv->ui_AiActualScan +=
+ (s->async->cur_chan + num_samples) / devpriv->ui_AiScanLength;
+ s->async->cur_chan += num_samples;
+ s->async->cur_chan %= devpriv->ui_AiScanLength;
+
+ cfc_write_array_to_buffer(s, dma_buffer, num_samples * sizeof(short));
+}
+
+/*
++----------------------------------------------------------------------------+
+| TIMER SUBDEVICE |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3120_InsnConfigTimer(struct comedi_device *dev, |
+| struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) |
+| |
++----------------------------------------------------------------------------+
+| Task :Configure Timer 2 |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| struct comedi_subdevice *s |
+| struct comedi_insn *insn |
+| unsigned int *data |
+| |
+| data[0]= TIMER configure as timer |
+| = WATCHDOG configure as watchdog |
+| data[1] = Timer constant |
+| data[2] = Timer2 interrupt (1)enable or(0) disable |
+| |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_InsnConfigTimer(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ UINT ui_Timervalue2;
+ USHORT us_TmpValue;
+ BYTE b_Tmp;
+
+ if (!data[1])
+ comedi_error(dev, "config:No timer constant !");
+
+ devpriv->b_Timer2Interrupt = (BYTE) data[2]; // save info whether to enable or disable interrupt
+
+ ui_Timervalue2 = data[1] / 1000; // convert nano seconds to u seconds
+
+ //this_board->i_hwdrv_InsnConfigTimer(dev, ui_Timervalue2,(BYTE)data[0]);
+ us_TmpValue = (USHORT) inw(devpriv->iobase + APCI3120_RD_STATUS);
+
+ //EL250804: Testing if board APCI3120 have the new Quartz or if it is an APCI3001
+ // and calculate the time value to set in the timer
+ if ((us_TmpValue & 0x00B0) == 0x00B0
+ || !strcmp(this_board->pc_DriverName, "apci3001")) {
+ //Calculate the time value to set in the timer
+ ui_Timervalue2 = ui_Timervalue2 / 50;
+ } else {
+ //Calculate the time value to set in the timer
+ ui_Timervalue2 = ui_Timervalue2 / 70;
+ }
+
+ //Reset gate 2 of Timer 2 to disable it (Set Bit D14 to 0)
+ devpriv->us_OutputRegister =
+ devpriv->us_OutputRegister & APCI3120_DISABLE_TIMER2;
+ outw(devpriv->us_OutputRegister, devpriv->iobase + APCI3120_WR_ADDRESS);
+
+ // Disable TIMER Interrupt
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister & APCI3120_DISABLE_TIMER_INT & 0xEF;
+
+ // Disable Eoc and Eos Interrupts
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister & APCI3120_DISABLE_EOC_INT &
+ APCI3120_DISABLE_EOS_INT;
+ outb(devpriv->b_ModeSelectRegister,
+ devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
+ if (data[0] == APCI3120_TIMER) //initialize timer
+ {
+
+ //devpriv->b_ModeSelectRegister=devpriv->b_ModeSelectRegister| APCI3120_ENABLE_TIMER_INT ;
+ //outb(devpriv->b_ModeSelectRegister,devpriv->iobase+APCI3120_WRITE_MODE_SELECT);
+
+ //Set the Timer 2 in mode 2(Timer)
+ devpriv->b_TimerSelectMode =
+ (devpriv->
+ b_TimerSelectMode & 0x0F) | APCI3120_TIMER_2_MODE_2;
+ outb(devpriv->b_TimerSelectMode,
+ devpriv->iobase + APCI3120_TIMER_CRT1);
+
+ //Configure the timer 2 for writing the LOW WORD of timer is Delay value
+ //You must make a b_tmp variable with DigitalOutPutRegister because at Address_1+APCI3120_TIMER_CRT0
+ //you can set the digital output and configure the timer 2,and if you don't make this, digital output
+ //are erase (Set to 0)
+
+ //Writing LOW WORD
+ b_Tmp = ((devpriv->
+ b_DigitalOutputRegister) & 0xF0) |
+ APCI3120_SELECT_TIMER_2_LOW_WORD;
+ outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
+ outw(LOWORD(ui_Timervalue2),
+ devpriv->iobase + APCI3120_TIMER_VALUE);
+
+ //Writing HIGH WORD
+ b_Tmp = ((devpriv->
+ b_DigitalOutputRegister) & 0xF0) |
+ APCI3120_SELECT_TIMER_2_HIGH_WORD;
+ outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
+ outw(HIWORD(ui_Timervalue2),
+ devpriv->iobase + APCI3120_TIMER_VALUE);
+ // timer2 in Timer mode enabled
+ devpriv->b_Timer2Mode = APCI3120_TIMER;
+
+ } else // Initialize Watch dog
+ {
+
+ //Set the Timer 2 in mode 5(Watchdog)
+
+ devpriv->b_TimerSelectMode =
+ (devpriv->
+ b_TimerSelectMode & 0x0F) | APCI3120_TIMER_2_MODE_5;
+ outb(devpriv->b_TimerSelectMode,
+ devpriv->iobase + APCI3120_TIMER_CRT1);
+
+ //Configure the timer 2 for writing the LOW WORD of timer is Delay value
+ //You must make a b_tmp variable with DigitalOutPutRegister because at Address_1+APCI3120_TIMER_CRT0
+ //you can set the digital output and configure the timer 2,and if you don't make this, digital output
+ //are erase (Set to 0)
+
+ //Writing LOW WORD
+ b_Tmp = ((devpriv->
+ b_DigitalOutputRegister) & 0xF0) |
+ APCI3120_SELECT_TIMER_2_LOW_WORD;
+ outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
+ outw(LOWORD(ui_Timervalue2),
+ devpriv->iobase + APCI3120_TIMER_VALUE);
+
+ //Writing HIGH WORD
+ b_Tmp = ((devpriv->
+ b_DigitalOutputRegister) & 0xF0) |
+ APCI3120_SELECT_TIMER_2_HIGH_WORD;
+ outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
+
+ outw(HIWORD(ui_Timervalue2),
+ devpriv->iobase + APCI3120_TIMER_VALUE);
+ //watchdog enabled
+ devpriv->b_Timer2Mode = APCI3120_WATCHDOG;
+
+ }
+
+ return insn->n;
+
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3120_InsnWriteTimer(struct comedi_device *dev, |
+| struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data) |
+| |
++----------------------------------------------------------------------------+
+| Task : To start and stop the timer |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| struct comedi_subdevice *s |
+| struct comedi_insn *insn |
+| unsigned int *data |
+| |
+| data[0] = 1 (start) |
+| data[0] = 0 (stop ) |
+| data[0] = 2 (write new value) |
+| data[1]= new value |
+| |
+| devpriv->b_Timer2Mode = 0 DISABLE |
+| 1 Timer |
+| 2 Watch dog |
+| |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_InsnWriteTimer(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ UINT ui_Timervalue2 = 0;
+ USHORT us_TmpValue;
+ BYTE b_Tmp;
+
+ if ((devpriv->b_Timer2Mode != APCI3120_WATCHDOG)
+ && (devpriv->b_Timer2Mode != APCI3120_TIMER)) {
+ comedi_error(dev, "\nwrite:timer2 not configured ");
+ return -EINVAL;
+ }
+
+ if (data[0] == 2) // write new value
+ {
+ if (devpriv->b_Timer2Mode != APCI3120_TIMER) {
+ comedi_error(dev,
+ "write :timer2 not configured in TIMER MODE");
+ return -EINVAL;
+ }
+
+ if (data[1])
+ ui_Timervalue2 = data[1];
+ else
+ ui_Timervalue2 = 0;
+ }
+
+ //this_board->i_hwdrv_InsnWriteTimer(dev,data[0],ui_Timervalue2);
+
+ switch (data[0]) {
+ case APCI3120_START:
+
+ // Reset FC_TIMER BIT
+ inb(devpriv->iobase + APCI3120_TIMER_STATUS_REGISTER);
+ if (devpriv->b_Timer2Mode == APCI3120_TIMER) //start timer
+ {
+ //Enable Timer
+ devpriv->b_ModeSelectRegister =
+ devpriv->b_ModeSelectRegister & 0x0B;
+ } else //start watch dog
+ {
+ //Enable WatchDog
+ devpriv->b_ModeSelectRegister =
+ (devpriv->
+ b_ModeSelectRegister & 0x0B) |
+ APCI3120_ENABLE_WATCHDOG;
+ }
+
+ //enable disable interrupt
+ if ((devpriv->b_Timer2Interrupt) == APCI3120_ENABLE) {
+
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister |
+ APCI3120_ENABLE_TIMER_INT;
+ // save the task structure to pass info to user
+ devpriv->tsk_Current = current;
+ } else {
+
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister &
+ APCI3120_DISABLE_TIMER_INT;
+ }
+ outb(devpriv->b_ModeSelectRegister,
+ devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
+
+ if (devpriv->b_Timer2Mode == APCI3120_TIMER) //start timer
+ {
+ //For Timer mode is Gate2 must be activated **timer started
+ devpriv->us_OutputRegister =
+ devpriv->
+ us_OutputRegister | APCI3120_ENABLE_TIMER2;
+ outw(devpriv->us_OutputRegister,
+ devpriv->iobase + APCI3120_WR_ADDRESS);
+ }
+
+ break;
+
+ case APCI3120_STOP:
+ if (devpriv->b_Timer2Mode == APCI3120_TIMER) {
+ //Disable timer
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister &
+ APCI3120_DISABLE_TIMER_COUNTER;
+ } else {
+ //Disable WatchDog
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister &
+ APCI3120_DISABLE_WATCHDOG;
+ }
+ // Disable timer interrupt
+ devpriv->b_ModeSelectRegister =
+ devpriv->
+ b_ModeSelectRegister & APCI3120_DISABLE_TIMER_INT;
+
+ // Write above states to register
+ outb(devpriv->b_ModeSelectRegister,
+ devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
+
+ // Reset Gate 2
+ devpriv->us_OutputRegister =
+ devpriv->us_OutputRegister & APCI3120_DISABLE_TIMER_INT;
+ outw(devpriv->us_OutputRegister,
+ devpriv->iobase + APCI3120_WR_ADDRESS);
+
+ // Reset FC_TIMER BIT
+ inb(devpriv->iobase + APCI3120_TIMER_STATUS_REGISTER);
+
+ // Disable timer
+ //devpriv->b_Timer2Mode=APCI3120_DISABLE;
+
+ break;
+
+ case 2: //write new value to Timer
+ if (devpriv->b_Timer2Mode != APCI3120_TIMER) {
+ comedi_error(dev,
+ "write :timer2 not configured in TIMER MODE");
+ return -EINVAL;
+ }
+ // ui_Timervalue2=data[1]; // passed as argument
+ us_TmpValue =
+ (USHORT) inw(devpriv->iobase + APCI3120_RD_STATUS);
+
+ //EL250804: Testing if board APCI3120 have the new Quartz or if it is an APCI3001
+ // and calculate the time value to set in the timer
+ if ((us_TmpValue & 0x00B0) == 0x00B0
+ || !strcmp(this_board->pc_DriverName, "apci3001")) {
+ //Calculate the time value to set in the timer
+ ui_Timervalue2 = ui_Timervalue2 / 50;
+ } else {
+ //Calculate the time value to set in the timer
+ ui_Timervalue2 = ui_Timervalue2 / 70;
+ }
+ //Writing LOW WORD
+ b_Tmp = ((devpriv->
+ b_DigitalOutputRegister) & 0xF0) |
+ APCI3120_SELECT_TIMER_2_LOW_WORD;
+ outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
+
+ outw(LOWORD(ui_Timervalue2),
+ devpriv->iobase + APCI3120_TIMER_VALUE);
+
+ //Writing HIGH WORD
+ b_Tmp = ((devpriv->
+ b_DigitalOutputRegister) & 0xF0) |
+ APCI3120_SELECT_TIMER_2_HIGH_WORD;
+ outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
+
+ outw(HIWORD(ui_Timervalue2),
+ devpriv->iobase + APCI3120_TIMER_VALUE);
+
+ break;
+ default:
+ return -EINVAL; // Not a valid input
+ }
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name : int i_APCI3120_InsnReadTimer(struct comedi_device *dev, |
+| struct comedi_subdevice *s,struct comedi_insn *insn, unsigned int *data) |
+| |
+| |
++----------------------------------------------------------------------------+
+| Task : read the Timer value |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| struct comedi_subdevice *s |
+| struct comedi_insn *insn |
+| unsigned int *data |
+| |
++----------------------------------------------------------------------------+
+| Return Value : |
+| for Timer: data[0]= Timer constant |
+| |
+| for watchdog: data[0]=0 (still running) |
+| data[0]=1 (run down) |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI3120_InsnReadTimer(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ BYTE b_Tmp;
+ USHORT us_TmpValue, us_TmpValue_2, us_StatusValue;
+
+ if ((devpriv->b_Timer2Mode != APCI3120_WATCHDOG)
+ && (devpriv->b_Timer2Mode != APCI3120_TIMER)) {
+ comedi_error(dev, "\nread:timer2 not configured ");
+ }
+
+ //this_board->i_hwdrv_InsnReadTimer(dev,data);
+ if (devpriv->b_Timer2Mode == APCI3120_TIMER) {
+
+ //Read the LOW WORD of Timer 2 register
+ b_Tmp = ((devpriv->
+ b_DigitalOutputRegister) & 0xF0) |
+ APCI3120_SELECT_TIMER_2_LOW_WORD;
+ outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
+
+ us_TmpValue = inw(devpriv->iobase + APCI3120_TIMER_VALUE);
+
+ //Read the HIGH WORD of Timer 2 register
+ b_Tmp = ((devpriv->
+ b_DigitalOutputRegister) & 0xF0) |
+ APCI3120_SELECT_TIMER_2_HIGH_WORD;
+ outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
+
+ us_TmpValue_2 = inw(devpriv->iobase + APCI3120_TIMER_VALUE);
+
+ // combining both words
+ data[0] = (UINT) ((us_TmpValue) | ((us_TmpValue_2) << 16));
+
+ } else // Read watch dog status
+ {
+
+ us_StatusValue = inw(devpriv->iobase + APCI3120_RD_STATUS);
+ us_StatusValue =
+ ((us_StatusValue & APCI3120_FC_TIMER) >> 12) & 1;
+ if (us_StatusValue == 1) {
+ // RESET FC_TIMER BIT
+ inb(devpriv->iobase + APCI3120_TIMER_STATUS_REGISTER);
+ }
+ data[0] = us_StatusValue; // when data[0] = 1 then the watch dog has rundown
+ }
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| DIGITAL INPUT SUBDEVICE |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3120_InsnReadDigitalInput(struct comedi_device *dev, |
+| struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data) |
+| |
+| |
++----------------------------------------------------------------------------+
+| Task : Reads the value of the specified Digital input channel|
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| struct comedi_subdevice *s |
+| struct comedi_insn *insn |
+| unsigned int *data |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_InsnReadDigitalInput(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ UINT ui_Chan, ui_TmpValue;
+
+ ui_Chan = CR_CHAN(insn->chanspec); // channel specified
+
+ //this_board->i_hwdrv_InsnReadDigitalInput(dev,ui_Chan,data);
+ if (ui_Chan >= 0 && ui_Chan <= 3) {
+ ui_TmpValue = (UINT) inw(devpriv->iobase + APCI3120_RD_STATUS);
+
+ // since only 1 channel reqd to bring it to last bit it is rotated
+ // 8 +(chan - 1) times then ANDed with 1 for last bit.
+ *data = (ui_TmpValue >> (ui_Chan + 8)) & 1;
+ //return 0;
+ } else {
+ // comedi_error(dev," chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+ }
+ return insn->n;
+
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3120_InsnBitsDigitalInput(struct comedi_device *dev, |
+|struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data) |
+| |
++----------------------------------------------------------------------------+
+| Task : Reads the value of the Digital input Port i.e.4channels|
+| value is returned in data[0] |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| struct comedi_subdevice *s |
+| struct comedi_insn *insn |
+| unsigned int *data |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI3120_InsnBitsDigitalInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_TmpValue;
+ ui_TmpValue = (UINT) inw(devpriv->iobase + APCI3120_RD_STATUS);
+ /***** state of 4 channels in the 11, 10, 9, 8 bits of status reg
+ rotated right 8 times to bring them to last four bits
+ ANDed with oxf for value.
+ *****/
+
+ *data = (ui_TmpValue >> 8) & 0xf;
+ //this_board->i_hwdrv_InsnBitsDigitalInput(dev,data);
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| DIGITAL OUTPUT SUBDEVICE |
++----------------------------------------------------------------------------+
+*/
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3120_InsnConfigDigitalOutput(struct comedi_device |
+| *dev,struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) |
+| |
++----------------------------------------------------------------------------+
+| Task :Configure the output memory ON or OFF |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters :struct comedi_device *dev |
+| struct comedi_subdevice *s |
+| struct comedi_insn *insn |
+| unsigned int *data |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_InsnConfigDigitalOutput(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+
+ if ((data[0] != 0) && (data[0] != 1)) {
+ comedi_error(dev,
+ "Not a valid Data !!! ,Data should be 1 or 0\n");
+ return -EINVAL;
+ }
+ if (data[0]) {
+ devpriv->b_OutputMemoryStatus = APCI3120_ENABLE;
+
+ } else {
+ devpriv->b_OutputMemoryStatus = APCI3120_DISABLE;
+ devpriv->b_DigitalOutputRegister = 0;
+ }
+ if (!devpriv->b_OutputMemoryStatus) {
+ ui_Temp = 0;
+
+ } //if(!devpriv->b_OutputMemoryStatus )
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3120_InsnBitsDigitalOutput(struct comedi_device *dev, |
+| struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data) |
+| |
++----------------------------------------------------------------------------+
+| Task : write diatal output port |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| struct comedi_subdevice *s |
+| struct comedi_insn *insn |
+| unsigned int *data |
+ data[0] Value to be written
+ data[1] :1 Set digital o/p ON
+ data[1] 2 Set digital o/p OFF with memory ON
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_InsnBitsDigitalOutput(struct comedi_device * dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if ((data[0] > this_board->i_DoMaxdata) || (data[0] < 0)) {
+
+ comedi_error(dev, "Data is not valid !!! \n");
+ return -EINVAL;
+ }
+
+ switch (data[1]) {
+ case 1:
+ data[0] = (data[0] << 4) | devpriv->b_DigitalOutputRegister;
+ break;
+
+ case 2:
+ data[0] = data[0];
+ break;
+ default:
+ printk("\nThe parameter passed is in error \n");
+ return -EINVAL;
+ } // switch(data[1])
+ outb(data[0], devpriv->iobase + APCI3120_DIGITAL_OUTPUT);
+
+ devpriv->b_DigitalOutputRegister = data[0] & 0xF0;
+
+ return insn->n;
+
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3120_InsnWriteDigitalOutput(struct comedi_device *dev,|
+|struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) |
+| |
++----------------------------------------------------------------------------+
+| Task : Write digiatl output |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| struct comedi_subdevice *s |
+| struct comedi_insn *insn |
+| unsigned int *data |
+ data[0] Value to be written
+ data[1] :1 Set digital o/p ON
+ data[1] 2 Set digital o/p OFF with memory ON
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_InsnWriteDigitalOutput(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+
+ UINT ui_Temp1;
+
+ UINT ui_NoOfChannel = CR_CHAN(insn->chanspec); // get the channel
+
+ if ((data[0] != 0) && (data[0] != 1)) {
+ comedi_error(dev,
+ "Not a valid Data !!! ,Data should be 1 or 0\n");
+ return -EINVAL;
+ }
+ if ((ui_NoOfChannel > (this_board->i_NbrDoChannel - 1))
+ || (ui_NoOfChannel < 0)) {
+ comedi_error(dev,
+ "This board doesn't have specified channel !!! \n");
+ return -EINVAL;
+ }
+
+ switch (data[1]) {
+ case 1:
+ data[0] = (data[0] << ui_NoOfChannel);
+//ES05 data[0]=(data[0]<<4)|ui_Temp;
+ data[0] = (data[0] << 4) | devpriv->b_DigitalOutputRegister;
+ break;
+
+ case 2:
+ data[0] = ~data[0] & 0x1;
+ ui_Temp1 = 1;
+ ui_Temp1 = ui_Temp1 << ui_NoOfChannel;
+ ui_Temp1 = ui_Temp1 << 4;
+//ES05 ui_Temp=ui_Temp|ui_Temp1;
+ devpriv->b_DigitalOutputRegister =
+ devpriv->b_DigitalOutputRegister | ui_Temp1;
+
+ data[0] = (data[0] << ui_NoOfChannel) ^ 0xf;
+ data[0] = data[0] << 4;
+//ES05 data[0]=data[0]& ui_Temp;
+ data[0] = data[0] & devpriv->b_DigitalOutputRegister;
+ break;
+ default:
+ printk("\nThe parameter passed is in error \n");
+ return -EINVAL;
+ } // switch(data[1])
+ outb(data[0], devpriv->iobase + APCI3120_DIGITAL_OUTPUT);
+
+//ES05 ui_Temp=data[0] & 0xf0;
+ devpriv->b_DigitalOutputRegister = data[0] & 0xf0;
+ return (insn->n);
+
+}
+
+/*
++----------------------------------------------------------------------------+
+| ANALOG OUTPUT SUBDEVICE |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3120_InsnWriteAnalogOutput(struct comedi_device *dev,|
+|struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data) |
+| |
++----------------------------------------------------------------------------+
+| Task : Write analog output |
+| |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
+| struct comedi_subdevice *s |
+| struct comedi_insn *insn |
+| unsigned int *data |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3120_InsnWriteAnalogOutput(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ UINT ui_Range, ui_Channel;
+ USHORT us_TmpValue;
+
+ ui_Range = CR_RANGE(insn->chanspec);
+ ui_Channel = CR_CHAN(insn->chanspec);
+
+ //this_board->i_hwdrv_InsnWriteAnalogOutput(dev, ui_Range, ui_Channel,data[0]);
+ if (ui_Range) // if 1 then unipolar
+ {
+
+ if (data[0] != 0)
+ data[0] =
+ ((((ui_Channel & 0x03) << 14) & 0xC000) | (1 <<
+ 13) | (data[0] + 8191));
+ else
+ data[0] =
+ ((((ui_Channel & 0x03) << 14) & 0xC000) | (1 <<
+ 13) | 8192);
+
+ } else // if 0 then bipolar
+ {
+ data[0] =
+ ((((ui_Channel & 0x03) << 14) & 0xC000) | (0 << 13) |
+ data[0]);
+
+ }
+
+ //out put n values at the given channel.
+ // rt_printk("\nwaiting for DA_READY BIT");
+ do //Waiting of DA_READY BIT
+ {
+ us_TmpValue =
+ ((USHORT) inw(devpriv->iobase +
+ APCI3120_RD_STATUS)) & 0x0001;
+ } while (us_TmpValue != 0x0001);
+
+ if (ui_Channel <= 3)
+ // for channel 0-3 out at the register 1 (wrDac1-8)
+ // data[i] typecasted to ushort since word write is to be done
+ outw((USHORT) data[0],
+ devpriv->iobase + APCI3120_ANALOG_OUTPUT_1);
+ else
+ // for channel 4-7 out at the register 2 (wrDac5-8)
+ //data[i] typecasted to ushort since word write is to be done
+ outw((USHORT) data[0],
+ devpriv->iobase + APCI3120_ANALOG_OUTPUT_2);
+
+ return insn->n;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.h b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.h
new file mode 100644
index 000000000000..59d5d87a845b
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.h
@@ -0,0 +1,241 @@
+
+// hwdrv_apci3120.h
+
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+// comedi related defines
+
+//ANALOG INPUT RANGE
+static const struct comedi_lrange range_apci3120_ai = { 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1)
+ }
+};
+
+// ANALOG OUTPUT RANGE
+static const struct comedi_lrange range_apci3120_ao = { 2, {
+ BIP_RANGE(10),
+ UNI_RANGE(10)
+ }
+};
+
+#define APCI3120_BIPOLAR_RANGES 4 // used for test on mixture of BIP/UNI ranges
+
+#define APCI3120_BOARD_VENDOR_ID 0x10E8
+#define APCI3120_ADDRESS_RANGE 16
+
+#define APCI3120_DISABLE 0
+#define APCI3120_ENABLE 1
+
+#define APCI3120_START 1
+#define APCI3120_STOP 0
+
+#define APCI3120_EOC_MODE 1
+#define APCI3120_EOS_MODE 2
+#define APCI3120_DMA_MODE 3
+
+//DIGITAL INPUT-OUTPUT DEFINE
+
+#define APCI3120_DIGITAL_OUTPUT 0x0D
+#define APCI3120_RD_STATUS 0x02
+#define APCI3120_RD_FIFO 0x00
+
+// digital output insn_write ON /OFF selection
+#define APCI3120_SET4DIGITALOUTPUTON 1
+#define APCI3120_SET4DIGITALOUTPUTOFF 0
+
+// analog output SELECT BIT
+#define APCI3120_ANALOG_OP_CHANNEL_1 0x0000
+#define APCI3120_ANALOG_OP_CHANNEL_2 0x4000
+#define APCI3120_ANALOG_OP_CHANNEL_3 0x8000
+#define APCI3120_ANALOG_OP_CHANNEL_4 0xC000
+#define APCI3120_ANALOG_OP_CHANNEL_5 0x0000
+#define APCI3120_ANALOG_OP_CHANNEL_6 0x4000
+#define APCI3120_ANALOG_OP_CHANNEL_7 0x8000
+#define APCI3120_ANALOG_OP_CHANNEL_8 0xC000
+
+// Enable external trigger bit in nWrAddress
+#define APCI3120_ENABLE_EXT_TRIGGER 0x8000
+
+//ANALOG OUTPUT AND INPUT DEFINE
+#define APCI3120_UNIPOLAR 0x80 //$$ RAM sequence polarity BIT
+#define APCI3120_BIPOLAR 0x00 //$$ RAM sequence polarity BIT
+#define APCI3120_ANALOG_OUTPUT_1 0x08 // (ADDRESS )
+#define APCI3120_ANALOG_OUTPUT_2 0x0A // (ADDRESS )
+#define APCI3120_1_GAIN 0x00 //$$ RAM sequence Gain Bits for gain 1
+#define APCI3120_2_GAIN 0x10 //$$ RAM sequence Gain Bits for gain 2
+#define APCI3120_5_GAIN 0x20 //$$ RAM sequence Gain Bits for gain 5
+#define APCI3120_10_GAIN 0x30 //$$ RAM sequence Gain Bits for gain 10
+#define APCI3120_SEQ_RAM_ADDRESS 0x06 //$$ EARLIER NAMED APCI3120_FIFO_ADDRESS
+#define APCI3120_RESET_FIFO 0x0C //(ADDRESS)
+#define APCI3120_TIMER_0_MODE_2 0x01 //$$ Bits for timer mode
+#define APCI3120_TIMER_0_MODE_4 0x2
+#define APCI3120_SELECT_TIMER_0_WORD 0x00
+#define APCI3120_ENABLE_TIMER0 0x1000 //$$Gatebit 0 in nWrAddress
+#define APCI3120_CLEAR_PR 0xF0FF
+#define APCI3120_CLEAR_PA 0xFFF0
+#define APCI3120_CLEAR_PA_PR (APCI3120_CLEAR_PR & APCI3120_CLEAR_PA)
+
+// nWrMode_Select
+#define APCI3120_ENABLE_SCAN 0x8 //$$ bit in nWrMode_Select
+#define APCI3120_DISABLE_SCAN (~APCI3120_ENABLE_SCAN)
+#define APCI3120_ENABLE_EOS_INT 0x2 //$$ bit in nWrMode_Select
+
+#define APCI3120_DISABLE_EOS_INT (~APCI3120_ENABLE_EOS_INT)
+#define APCI3120_ENABLE_EOC_INT 0x1
+#define APCI3120_DISABLE_EOC_INT (~APCI3120_ENABLE_EOC_INT)
+#define APCI3120_DISABLE_ALL_INTERRUPT_WITHOUT_TIMER (APCI3120_DISABLE_EOS_INT & APCI3120_DISABLE_EOC_INT)
+#define APCI3120_DISABLE_ALL_INTERRUPT (APCI3120_DISABLE_TIMER_INT & APCI3120_DISABLE_EOS_INT & APCI3120_DISABLE_EOC_INT)
+
+//status register bits
+#define APCI3120_EOC 0x8000
+#define APCI3120_EOS 0x2000
+
+// software trigger dummy register
+#define APCI3120_START_CONVERSION 0x02 //(ADDRESS)
+
+//TIMER DEFINE
+#define APCI3120_QUARTZ_A 70
+#define APCI3120_QUARTZ_B 50
+#define APCI3120_TIMER 1
+#define APCI3120_WATCHDOG 2
+#define APCI3120_TIMER_DISABLE 0
+#define APCI3120_TIMER_ENABLE 1
+#define APCI3120_ENABLE_TIMER2 0x4000 //$$ gatebit 2 in nWrAddress
+#define APCI3120_DISABLE_TIMER2 (~APCI3120_ENABLE_TIMER2)
+#define APCI3120_ENABLE_TIMER_INT 0x04 //$$ ENAIRQ_FC_Bit in nWrModeSelect
+#define APCI3120_DISABLE_TIMER_INT (~APCI3120_ENABLE_TIMER_INT)
+#define APCI3120_WRITE_MODE_SELECT 0x0E // (ADDRESS)
+#define APCI3120_SELECT_TIMER_0_WORD 0x00
+#define APCI3120_SELECT_TIMER_1_WORD 0x01
+#define APCI3120_TIMER_1_MODE_2 0x4
+
+//$$ BIT FOR MODE IN nCsTimerCtr1
+#define APCI3120_TIMER_2_MODE_0 0x0
+#define APCI3120_TIMER_2_MODE_2 0x10
+#define APCI3120_TIMER_2_MODE_5 0x30
+
+//$$ BIT FOR MODE IN nCsTimerCtr0
+#define APCI3120_SELECT_TIMER_2_LOW_WORD 0x02
+#define APCI3120_SELECT_TIMER_2_HIGH_WORD 0x03
+
+#define APCI3120_TIMER_CRT0 0x0D //(ADDRESS for cCsTimerCtr0)
+#define APCI3120_TIMER_CRT1 0x0C //(ADDRESS for cCsTimerCtr1)
+
+#define APCI3120_TIMER_VALUE 0x04 //ADDRESS for nCsTimerWert
+#define APCI3120_TIMER_STATUS_REGISTER 0x0D //ADDRESS for delete timer 2 interrupt
+#define APCI3120_RD_STATUS 0x02 //ADDRESS
+#define APCI3120_WR_ADDRESS 0x00 //ADDRESS
+#define APCI3120_ENABLE_WATCHDOG 0x20 //$$BIT in nWrMode_Select
+#define APCI3120_DISABLE_WATCHDOG (~APCI3120_ENABLE_WATCHDOG)
+#define APCI3120_ENABLE_TIMER_COUNTER 0x10 //$$BIT in nWrMode_Select
+#define APCI3120_DISABLE_TIMER_COUNTER (~APCI3120_ENABLE_TIMER_COUNTER)
+#define APCI3120_FC_TIMER 0x1000 //bit in status register
+#define APCI3120_ENABLE_TIMER0 0x1000
+#define APCI3120_ENABLE_TIMER1 0x2000
+#define APCI3120_ENABLE_TIMER2 0x4000
+#define APCI3120_DISABLE_TIMER0 (~APCI3120_ENABLE_TIMER0)
+#define APCI3120_DISABLE_TIMER1 (~APCI3120_ENABLE_TIMER1)
+#define APCI3120_DISABLE_TIMER2 (~APCI3120_ENABLE_TIMER2)
+
+#define APCI3120_TIMER2_SELECT_EOS 0xC0 // ADDED on 20-6
+#define APCI3120_COUNTER 3 // on 20-6
+#define APCI3120_DISABLE_ALL_TIMER ( APCI3120_DISABLE_TIMER0 & APCI3120_DISABLE_TIMER1 & APCI3120_DISABLE_TIMER2 ) // on 20-6
+
+#define MAX_ANALOGINPUT_CHANNELS 32
+
+typedef struct {
+ BYTE b_Type; /* EOC or EOS */
+ BYTE b_InterruptFlag; /* Interrupt use or not */
+ UINT ui_ConvertTiming; /* Selection of the convertion time */
+ BYTE b_NbrOfChannel; /* Number of channel to read */
+ UINT ui_ChannelList[MAX_ANALOGINPUT_CHANNELS]; /* Number of the channel to be read */
+ UINT ui_RangeList[MAX_ANALOGINPUT_CHANNELS]; /* Gain of each channel */
+
+} str_AnalogReadInformation;
+
+// Function Declaration For APCI-3120
+
+// Internal functions
+int i_APCI3120_SetupChannelList(struct comedi_device *dev, struct comedi_subdevice *s,
+ int n_chan, unsigned int *chanlist, char check);
+int i_APCI3120_ExttrigEnable(struct comedi_device *dev);
+int i_APCI3120_ExttrigDisable(struct comedi_device *dev);
+int i_APCI3120_StopCyclicAcquisition(struct comedi_device *dev, struct comedi_subdevice *s);
+int i_APCI3120_Reset(struct comedi_device *dev);
+int i_APCI3120_CyclicAnalogInput(int mode, struct comedi_device *dev,
+ struct comedi_subdevice *s);
+// Interrupt functions
+void v_APCI3120_Interrupt(int irq, void *d);
+//UPDATE-0.7.57->0.7.68 void v_APCI3120_InterruptDmaMoveBlock16bit(struct comedi_device *dev,struct comedi_subdevice *s,short *dma,short *data,int n);
+void v_APCI3120_InterruptDmaMoveBlock16bit(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ short *dma_buffer,
+ unsigned int num_samples);
+int i_APCI3120_InterruptHandleEos(struct comedi_device *dev);
+void v_APCI3120_InterruptDma(int irq, void *d);
+
+// TIMER
+
+int i_APCI3120_InsnConfigTimer(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI3120_InsnWriteTimer(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI3120_InsnReadTimer(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+//DI
+// for di read
+
+int i_APCI3120_InsnBitsDigitalInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI3120_InsnReadDigitalInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+//DO
+//int i_APCI3120_WriteDigitalOutput(struct comedi_device *dev, BYTE data);
+int i_APCI3120_InsnConfigDigitalOutput(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn,
+ unsigned int *data);
+int i_APCI3120_InsnBitsDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI3120_InsnWriteDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+//AO
+//int i_APCI3120_Write1AnalogValue(struct comedi_device *dev,UINT ui_Range,UINT ui_Channel,UINT data );
+int i_APCI3120_InsnWriteAnalogOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+//AI HArdware layer
+
+int i_APCI3120_InsnConfigAnalogInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI3120_InsnReadAnalogInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI3120_CommandTestAnalogInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd);
+int i_APCI3120_CommandAnalogInput(struct comedi_device *dev, struct comedi_subdevice *s);
+//int i_APCI3120_CancelAnalogInput(struct comedi_device *dev, struct comedi_subdevice *s);
+int i_APCI3120_StopCyclicAcquisition(struct comedi_device *dev, struct comedi_subdevice *s);
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3200.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3200.c
new file mode 100644
index 000000000000..1ff66bee548d
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3200.c
@@ -0,0 +1,3642 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-------------------------------+---------------------------------------+
+ | Project : APCI-3200 | Compiler : GCC |
+ | Module name : hwdrv_apci3200.c| Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-------------------------------+---------------------------------------+
+ | Description : Hardware Layer Acces For APCI-3200 |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +----------+-----------+------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | 02.07.04 | J. Krauth | Modification from the driver in order to |
+ | | | correct some errors when using several boards. |
+ | | | |
+ | | | |
+ +----------+-----------+------------------------------------------------+
+ | 26.10.04 | J. Krauth | - Update for COMEDI 0.7.68 |
+ | | | - Read eeprom value |
+ | | | - Append APCI-3300 |
+ +----------+-----------+------------------------------------------------+
+*/
+
+/*
+ +----------------------------------------------------------------------------+
+ | Included files |
+ +----------------------------------------------------------------------------+
+*/
+#include "hwdrv_apci3200.h"
+//Begin JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+#include "addi_amcc_S5920.h"
+//#define PRINT_INFO
+
+//End JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+
+//BEGIN JK 06.07.04: Management of sevrals boards
+/*
+ INT i_CJCAvailable=1;
+ INT i_CJCPolarity=0;
+ INT i_CJCGain=2;//changed from 0 to 2
+ INT i_InterruptFlag=0;
+ INT i_ADDIDATAPolarity;
+ INT i_ADDIDATAGain;
+ INT i_AutoCalibration=0; //: auto calibration
+ INT i_ADDIDATAConversionTime;
+ INT i_ADDIDATAConversionTimeUnit;
+ INT i_ADDIDATAType;
+ INT i_ChannelNo;
+ INT i_ChannelCount=0;
+ INT i_ScanType;
+ INT i_FirstChannel;
+ INT i_LastChannel;
+ INT i_Sum=0;
+ INT i_Offset;
+ UINT ui_Channel_num=0;
+ static int i_Count=0;
+ INT i_Initialised=0;
+ UINT ui_InterruptChannelValue[96]; //Buffer
+*/
+str_BoardInfos s_BoardInfos[100]; // 100 will be the max number of boards to be used
+//END JK 06.07.04: Management of sevrals boards
+
+//Begin JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+
+/*+----------------------------------------------------------------------------+*/
+/*| Function Name : INT i_AddiHeaderRW_ReadEeprom |*/
+/*| (INT i_NbOfWordsToRead, |*/
+/*| DWORD dw_PCIBoardEepromAddress, |*/
+/*| WORD w_EepromStartAddress, |*/
+/*| PWORD pw_DataRead) |*/
+/*+----------------------------------------------------------------------------+*/
+/*| Task : Read word from the 5920 eeprom. |*/
+/*+----------------------------------------------------------------------------+*/
+/*| Input Parameters : INT i_NbOfWordsToRead : Nbr. of word to read |*/
+/*| DWORD dw_PCIBoardEepromAddress : Address of the eeprom |*/
+/*| WORD w_EepromStartAddress : Eeprom strat address |*/
+/*+----------------------------------------------------------------------------+*/
+/*| Output Parameters : PWORD pw_DataRead : Read data |*/
+/*+----------------------------------------------------------------------------+*/
+/*| Return Value : - |*/
+/*+----------------------------------------------------------------------------+*/
+
+INT i_AddiHeaderRW_ReadEeprom(INT i_NbOfWordsToRead,
+ DWORD dw_PCIBoardEepromAddress,
+ WORD w_EepromStartAddress, PWORD pw_DataRead)
+{
+ DWORD dw_eeprom_busy = 0;
+ INT i_Counter = 0;
+ INT i_WordCounter;
+ INT i;
+ BYTE pb_ReadByte[1];
+ BYTE b_ReadLowByte = 0;
+ BYTE b_ReadHighByte = 0;
+ BYTE b_SelectedAddressLow = 0;
+ BYTE b_SelectedAddressHigh = 0;
+ WORD w_ReadWord = 0;
+
+ for (i_WordCounter = 0; i_WordCounter < i_NbOfWordsToRead;
+ i_WordCounter++) {
+ do {
+ dw_eeprom_busy =
+ inl(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR);
+ dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
+ }
+ while (dw_eeprom_busy == EEPROM_BUSY);
+
+ for (i_Counter = 0; i_Counter < 2; i_Counter++) {
+ b_SelectedAddressLow = (w_EepromStartAddress + i_Counter) % 256; //Read the low 8 bit part
+ b_SelectedAddressHigh = (w_EepromStartAddress + i_Counter) / 256; //Read the high 8 bit part
+
+ //Select the load low address mode
+ outb(NVCMD_LOAD_LOW,
+ dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
+ 3);
+
+ //Wait on busy
+ do {
+ dw_eeprom_busy =
+ inl(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR);
+ dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
+ }
+ while (dw_eeprom_busy == EEPROM_BUSY);
+
+ //Load the low address
+ outb(b_SelectedAddressLow,
+ dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
+ 2);
+
+ //Wait on busy
+ do {
+ dw_eeprom_busy =
+ inl(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR);
+ dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
+ }
+ while (dw_eeprom_busy == EEPROM_BUSY);
+
+ //Select the load high address mode
+ outb(NVCMD_LOAD_HIGH,
+ dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
+ 3);
+
+ //Wait on busy
+ do {
+ dw_eeprom_busy =
+ inl(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR);
+ dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
+ }
+ while (dw_eeprom_busy == EEPROM_BUSY);
+
+ //Load the high address
+ outb(b_SelectedAddressHigh,
+ dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
+ 2);
+
+ //Wait on busy
+ do {
+ dw_eeprom_busy =
+ inl(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR);
+ dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
+ }
+ while (dw_eeprom_busy == EEPROM_BUSY);
+
+ //Select the READ mode
+ outb(NVCMD_BEGIN_READ,
+ dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
+ 3);
+
+ //Wait on busy
+ do {
+ dw_eeprom_busy =
+ inl(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR);
+ dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
+ }
+ while (dw_eeprom_busy == EEPROM_BUSY);
+
+ //Read data into the EEPROM
+ *pb_ReadByte =
+ inb(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR + 2);
+
+ //Wait on busy
+ do {
+ dw_eeprom_busy =
+ inl(dw_PCIBoardEepromAddress +
+ AMCC_OP_REG_MCSR);
+ dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
+ }
+ while (dw_eeprom_busy == EEPROM_BUSY);
+
+ //Select the upper address part
+ if (i_Counter == 0) {
+ b_ReadLowByte = pb_ReadByte[0];
+ } else {
+ b_ReadHighByte = pb_ReadByte[0];
+ }
+
+ //Sleep
+ for (i = 0; i < 10000; i++) ;
+
+ }
+ w_ReadWord =
+ (b_ReadLowByte | (((unsigned short)b_ReadHighByte) *
+ 256));
+
+ pw_DataRead[i_WordCounter] = w_ReadWord;
+
+ w_EepromStartAddress += 2; // to read the next word
+
+ } // for (...) i_NbOfWordsToRead
+ return (0);
+}
+
+/*+----------------------------------------------------------------------------+*/
+/*| Function Name : void v_GetAPCI3200EepromCalibrationValue (void) |*/
+/*+----------------------------------------------------------------------------+*/
+/*| Task : Read calibration value from the APCI-3200 eeprom. |*/
+/*+----------------------------------------------------------------------------+*/
+/*| Input Parameters : - |*/
+/*+----------------------------------------------------------------------------+*/
+/*| Output Parameters : - |*/
+/*+----------------------------------------------------------------------------+*/
+/*| Return Value : - |*/
+/*+----------------------------------------------------------------------------+*/
+
+void v_GetAPCI3200EepromCalibrationValue(DWORD dw_PCIBoardEepromAddress,
+ str_BoardInfos * BoardInformations)
+{
+ WORD w_AnalogInputMainHeaderAddress;
+ WORD w_AnalogInputComponentAddress;
+ WORD w_NumberOfModuls = 0;
+ WORD w_CurrentSources[2];
+ WORD w_ModulCounter = 0;
+ WORD w_FirstHeaderSize = 0;
+ WORD w_NumberOfInputs = 0;
+ WORD w_CJCFlag = 0;
+ WORD w_NumberOfGainValue = 0;
+ WORD w_SingleHeaderAddress = 0;
+ WORD w_SingleHeaderSize = 0;
+ WORD w_Input = 0;
+ WORD w_GainFactorAddress = 0;
+ WORD w_GainFactorValue[2];
+ WORD w_GainIndex = 0;
+ WORD w_GainValue = 0;
+
+ /*****************************************/
+ /** Get the Analog input header address **/
+ /*****************************************/
+ i_AddiHeaderRW_ReadEeprom(1, //i_NbOfWordsToRead
+ dw_PCIBoardEepromAddress, 0x116, //w_EepromStartAddress: Analog input header address
+ &w_AnalogInputMainHeaderAddress);
+
+ /*******************************************/
+ /** Compute the real analog input address **/
+ /*******************************************/
+ w_AnalogInputMainHeaderAddress = w_AnalogInputMainHeaderAddress + 0x100;
+
+ /******************************/
+ /** Get the number of moduls **/
+ /******************************/
+ i_AddiHeaderRW_ReadEeprom(1, //i_NbOfWordsToRead
+ dw_PCIBoardEepromAddress, w_AnalogInputMainHeaderAddress + 0x02, //w_EepromStartAddress: Number of conponment
+ &w_NumberOfModuls);
+
+ for (w_ModulCounter = 0; w_ModulCounter < w_NumberOfModuls;
+ w_ModulCounter++) {
+ /***********************************/
+ /** Compute the component address **/
+ /***********************************/
+ w_AnalogInputComponentAddress =
+ w_AnalogInputMainHeaderAddress +
+ (w_FirstHeaderSize * w_ModulCounter) + 0x04;
+
+ /****************************/
+ /** Read first header size **/
+ /****************************/
+ i_AddiHeaderRW_ReadEeprom(1, //i_NbOfWordsToRead
+ dw_PCIBoardEepromAddress, w_AnalogInputComponentAddress, // Address of the first header
+ &w_FirstHeaderSize);
+
+ w_FirstHeaderSize = w_FirstHeaderSize >> 4;
+
+ /***************************/
+ /** Read number of inputs **/
+ /***************************/
+ i_AddiHeaderRW_ReadEeprom(1, //i_NbOfWordsToRead
+ dw_PCIBoardEepromAddress, w_AnalogInputComponentAddress + 0x06, // Number of inputs for the first modul
+ &w_NumberOfInputs);
+
+ w_NumberOfInputs = w_NumberOfInputs >> 4;
+
+ /***********************/
+ /** Read the CJC flag **/
+ /***********************/
+ i_AddiHeaderRW_ReadEeprom(1, //i_NbOfWordsToRead
+ dw_PCIBoardEepromAddress, w_AnalogInputComponentAddress + 0x08, // CJC flag
+ &w_CJCFlag);
+
+ w_CJCFlag = (w_CJCFlag >> 3) & 0x1; // Get only the CJC flag
+
+ /*******************************/
+ /** Read number of gain value **/
+ /*******************************/
+ i_AddiHeaderRW_ReadEeprom(1, //i_NbOfWordsToRead
+ dw_PCIBoardEepromAddress, w_AnalogInputComponentAddress + 0x44, // Number of gain value
+ &w_NumberOfGainValue);
+
+ w_NumberOfGainValue = w_NumberOfGainValue & 0xFF;
+
+ /***********************************/
+ /** Compute single header address **/
+ /***********************************/
+ w_SingleHeaderAddress =
+ w_AnalogInputComponentAddress + 0x46 +
+ (((w_NumberOfGainValue / 16) + 1) * 2) +
+ (6 * w_NumberOfGainValue) +
+ (4 * (((w_NumberOfGainValue / 16) + 1) * 2));
+
+ /********************************************/
+ /** Read current sources value for input 1 **/
+ /********************************************/
+ i_AddiHeaderRW_ReadEeprom(1, //i_NbOfWordsToRead
+ dw_PCIBoardEepromAddress, w_SingleHeaderAddress, //w_EepromStartAddress: Single header address
+ &w_SingleHeaderSize);
+
+ w_SingleHeaderSize = w_SingleHeaderSize >> 4;
+
+ /*************************************/
+ /** Read gain factor for the module **/
+ /*************************************/
+ w_GainFactorAddress = w_AnalogInputComponentAddress;
+
+ for (w_GainIndex = 0; w_GainIndex < w_NumberOfGainValue;
+ w_GainIndex++) {
+ /************************************/
+ /** Read gain value for the module **/
+ /************************************/
+ i_AddiHeaderRW_ReadEeprom(1, //i_NbOfWordsToRead
+ dw_PCIBoardEepromAddress, w_AnalogInputComponentAddress + 70 + (2 * (1 + (w_NumberOfGainValue / 16))) + (0x02 * w_GainIndex), // Gain value
+ &w_GainValue);
+
+ BoardInformations->s_Module[w_ModulCounter].
+ w_GainValue[w_GainIndex] = w_GainValue;
+
+# ifdef PRINT_INFO
+ printk("\n Gain value = %d",
+ BoardInformations->s_Module[w_ModulCounter].
+ w_GainValue[w_GainIndex]);
+# endif
+
+ /*************************************/
+ /** Read gain factor for the module **/
+ /*************************************/
+ i_AddiHeaderRW_ReadEeprom(2, //i_NbOfWordsToRead
+ dw_PCIBoardEepromAddress, w_AnalogInputComponentAddress + 70 + ((2 * w_NumberOfGainValue) + (2 * (1 + (w_NumberOfGainValue / 16)))) + (0x04 * w_GainIndex), // Gain factor
+ w_GainFactorValue);
+
+ BoardInformations->s_Module[w_ModulCounter].
+ ul_GainFactor[w_GainIndex] =
+ (w_GainFactorValue[1] << 16) +
+ w_GainFactorValue[0];
+
+# ifdef PRINT_INFO
+ printk("\n w_GainFactorValue [%d] = %lu", w_GainIndex,
+ BoardInformations->s_Module[w_ModulCounter].
+ ul_GainFactor[w_GainIndex]);
+# endif
+ }
+
+ /***************************************************************/
+ /** Read current source value for each channels of the module **/
+ /***************************************************************/
+ for (w_Input = 0; w_Input < w_NumberOfInputs; w_Input++) {
+ /********************************************/
+ /** Read current sources value for input 1 **/
+ /********************************************/
+ i_AddiHeaderRW_ReadEeprom(2, //i_NbOfWordsToRead
+ dw_PCIBoardEepromAddress,
+ (w_Input * w_SingleHeaderSize) +
+ w_SingleHeaderAddress + 0x0C, w_CurrentSources);
+
+ /************************************/
+ /** Save the current sources value **/
+ /************************************/
+ BoardInformations->s_Module[w_ModulCounter].
+ ul_CurrentSource[w_Input] =
+ (w_CurrentSources[0] +
+ ((w_CurrentSources[1] & 0xFFF) << 16));
+
+# ifdef PRINT_INFO
+ printk("\n Current sources [%d] = %lu", w_Input,
+ BoardInformations->s_Module[w_ModulCounter].
+ ul_CurrentSource[w_Input]);
+# endif
+ }
+
+ /***************************************/
+ /** Read the CJC current source value **/
+ /***************************************/
+ i_AddiHeaderRW_ReadEeprom(2, //i_NbOfWordsToRead
+ dw_PCIBoardEepromAddress,
+ (w_Input * w_SingleHeaderSize) + w_SingleHeaderAddress +
+ 0x0C, w_CurrentSources);
+
+ /************************************/
+ /** Save the current sources value **/
+ /************************************/
+ BoardInformations->s_Module[w_ModulCounter].
+ ul_CurrentSourceCJC =
+ (w_CurrentSources[0] +
+ ((w_CurrentSources[1] & 0xFFF) << 16));
+
+# ifdef PRINT_INFO
+ printk("\n Current sources CJC = %lu",
+ BoardInformations->s_Module[w_ModulCounter].
+ ul_CurrentSourceCJC);
+# endif
+ }
+}
+
+INT i_APCI3200_GetChannelCalibrationValue(struct comedi_device * dev,
+ unsigned int ui_Channel_num, unsigned int * CJCCurrentSource,
+ unsigned int * ChannelCurrentSource, unsigned int * ChannelGainFactor)
+{
+ int i_DiffChannel = 0;
+ int i_Module = 0;
+
+#ifdef PRINT_INFO
+ printk("\n Channel = %u", ui_Channel_num);
+#endif
+
+ //Test if single or differential mode
+ if (s_BoardInfos[dev->minor].i_ConnectionType == 1) {
+ //if diff
+
+ if ((ui_Channel_num >= 0) && (ui_Channel_num <= 1))
+ i_DiffChannel = ui_Channel_num, i_Module = 0;
+ else if ((ui_Channel_num >= 2) && (ui_Channel_num <= 3))
+ i_DiffChannel = ui_Channel_num - 2, i_Module = 1;
+ else if ((ui_Channel_num >= 4) && (ui_Channel_num <= 5))
+ i_DiffChannel = ui_Channel_num - 4, i_Module = 2;
+ else if ((ui_Channel_num >= 6) && (ui_Channel_num <= 7))
+ i_DiffChannel = ui_Channel_num - 6, i_Module = 3;
+
+ } else {
+ // if single
+ if ((ui_Channel_num == 0) || (ui_Channel_num == 1))
+ i_DiffChannel = 0, i_Module = 0;
+ else if ((ui_Channel_num == 2) || (ui_Channel_num == 3))
+ i_DiffChannel = 1, i_Module = 0;
+ else if ((ui_Channel_num == 4) || (ui_Channel_num == 5))
+ i_DiffChannel = 0, i_Module = 1;
+ else if ((ui_Channel_num == 6) || (ui_Channel_num == 7))
+ i_DiffChannel = 1, i_Module = 1;
+ else if ((ui_Channel_num == 8) || (ui_Channel_num == 9))
+ i_DiffChannel = 0, i_Module = 2;
+ else if ((ui_Channel_num == 10) || (ui_Channel_num == 11))
+ i_DiffChannel = 1, i_Module = 2;
+ else if ((ui_Channel_num == 12) || (ui_Channel_num == 13))
+ i_DiffChannel = 0, i_Module = 3;
+ else if ((ui_Channel_num == 14) || (ui_Channel_num == 15))
+ i_DiffChannel = 1, i_Module = 3;
+ }
+
+ //Test if thermocouple or RTD mode
+ *CJCCurrentSource =
+ s_BoardInfos[dev->minor].s_Module[i_Module].ul_CurrentSourceCJC;
+#ifdef PRINT_INFO
+ printk("\n CJCCurrentSource = %lu", *CJCCurrentSource);
+#endif
+
+ *ChannelCurrentSource =
+ s_BoardInfos[dev->minor].s_Module[i_Module].
+ ul_CurrentSource[i_DiffChannel];
+#ifdef PRINT_INFO
+ printk("\n ChannelCurrentSource = %lu", *ChannelCurrentSource);
+#endif
+ // }
+ // }
+
+ //Channle gain factor
+ *ChannelGainFactor =
+ s_BoardInfos[dev->minor].s_Module[i_Module].
+ ul_GainFactor[s_BoardInfos[dev->minor].i_ADDIDATAGain];
+#ifdef PRINT_INFO
+ printk("\n ChannelGainFactor = %lu", *ChannelGainFactor);
+#endif
+ //End JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+
+ return (0);
+}
+
+//End JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI3200_ReadDigitalInput |
+ | (struct comedi_device *dev,struct comedi_subdevice *s, |
+ | struct comedi_insn *insn,unsigned int *data) |
+ +----------------------------------------------------------------------------+
+ | Task : Read value of the selected channel or port |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | UINT ui_NoOfChannels : No Of Channels To read for Port
+ Channel Numberfor single channel
+ | UINT data[0] : 0: Read single channel
+ 1: Read port value
+ data[1] Port number
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- data[0] :Read status value
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+
+INT i_APCI3200_ReadDigitalInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Temp = 0;
+ UINT ui_NoOfChannel = 0;
+ ui_NoOfChannel = CR_CHAN(insn->chanspec);
+ ui_Temp = data[0];
+ *data = inl(devpriv->i_IobaseReserved);
+
+ if (ui_Temp == 0) {
+ *data = (*data >> ui_NoOfChannel) & 0x1;
+ } //if (ui_Temp==0)
+ else {
+ if (ui_Temp == 1) {
+ if (data[1] < 0 || data[1] > 1) {
+ printk("\nThe port number is in error\n");
+ return -EINVAL;
+ } //if(data[1] < 0 || data[1] >1)
+ switch (ui_NoOfChannel) {
+
+ case 2:
+ *data = (*data >> (2 * data[1])) & 0x3;
+ break;
+ case 3:
+ *data = (*data & 15);
+ break;
+ default:
+ comedi_error(dev, " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+
+ } //switch(ui_NoOfChannels)
+ } //if (ui_Temp==1)
+ else {
+ printk("\nSpecified channel not supported \n");
+ } //elseif (ui_Temp==1)
+ }
+ return insn->n;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI3200_ConfigDigitalOutput |
+ | (struct comedi_device *dev,struct comedi_subdevice *s, |
+ | struct comedi_insn *insn,unsigned int *data) |
+ +----------------------------------------------------------------------------+
+ | Task : Configures The Digital Output Subdevice. |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | data[0] :1 Memory enable
+ 0 Memory Disable
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+int i_APCI3200_ConfigDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ if ((data[0] != 0) && (data[0] != 1)) {
+ comedi_error(dev,
+ "Not a valid Data !!! ,Data should be 1 or 0\n");
+ return -EINVAL;
+ } //if ( (data[0]!=0) && (data[0]!=1) )
+ if (data[0]) {
+ devpriv->b_OutputMemoryStatus = ADDIDATA_ENABLE;
+ } // if (data[0])
+ else {
+ devpriv->b_OutputMemoryStatus = ADDIDATA_DISABLE;
+ } //else if (data[0])
+ return insn->n;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI3200_WriteDigitalOutput |
+ | (struct comedi_device *dev,struct comedi_subdevice *s, |
+ | struct comedi_insn *insn,unsigned int *data) |
+ +----------------------------------------------------------------------------+
+ | Task : writes To the digital Output Subdevice |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | struct comedi_subdevice *s : Subdevice Pointer |
+ | struct comedi_insn *insn : Insn Structure Pointer |
+ | unsigned int *data : Data Pointer contains |
+ | configuration parameters as below |
+ | data[0] :Value to output
+ data[1] : 0 o/p single channel
+ 1 o/p port
+ data[2] : port no
+ data[3] :0 set the digital o/p on
+ 1 set the digital o/p off
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+INT i_APCI3200_WriteDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Temp = 0, ui_Temp1 = 0;
+ UINT ui_NoOfChannel = CR_CHAN(insn->chanspec); // get the channel
+ if (devpriv->b_OutputMemoryStatus) {
+ ui_Temp = inl(devpriv->i_IobaseAddon);
+
+ } //if(devpriv->b_OutputMemoryStatus )
+ else {
+ ui_Temp = 0;
+ } //if(devpriv->b_OutputMemoryStatus )
+ if (data[3] == 0) {
+ if (data[1] == 0) {
+ data[0] = (data[0] << ui_NoOfChannel) | ui_Temp;
+ outl(data[0], devpriv->i_IobaseAddon);
+ } //if(data[1]==0)
+ else {
+ if (data[1] == 1) {
+ switch (ui_NoOfChannel) {
+
+ case 2:
+ data[0] =
+ (data[0] << (2 *
+ data[2])) | ui_Temp;
+ break;
+ case 3:
+ data[0] = (data[0] | ui_Temp);
+ break;
+ } //switch(ui_NoOfChannels)
+
+ outl(data[0], devpriv->i_IobaseAddon);
+ } // if(data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } //else if(data[1]==1)
+ } //elseif(data[1]==0)
+ } //if(data[3]==0)
+ else {
+ if (data[3] == 1) {
+ if (data[1] == 0) {
+ data[0] = ~data[0] & 0x1;
+ ui_Temp1 = 1;
+ ui_Temp1 = ui_Temp1 << ui_NoOfChannel;
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] = (data[0] << ui_NoOfChannel) ^ 0xf;
+ data[0] = data[0] & ui_Temp;
+ outl(data[0], devpriv->i_IobaseAddon);
+ } //if(data[1]==0)
+ else {
+ if (data[1] == 1) {
+ switch (ui_NoOfChannel) {
+
+ case 2:
+ data[0] = ~data[0] & 0x3;
+ ui_Temp1 = 3;
+ ui_Temp1 =
+ ui_Temp1 << 2 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (2 *
+ data
+ [2])) ^
+ 0xf) & ui_Temp;
+
+ break;
+ case 3:
+ break;
+
+ default:
+ comedi_error(dev,
+ " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+ } //switch(ui_NoOfChannels)
+
+ outl(data[0], devpriv->i_IobaseAddon);
+ } // if(data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } //else if(data[1]==1)
+ } //elseif(data[1]==0)
+ } //if(data[3]==1);
+ else {
+ printk("\nSpecified functionality does not exist\n");
+ return -EINVAL;
+ } //if else data[3]==1)
+ } //if else data[3]==0)
+ return insn->n;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI3200_ReadDigitalOutput |
+ | (struct comedi_device *dev,struct comedi_subdevice *s, |
+ | struct comedi_insn *insn,unsigned int *data) |
+ +----------------------------------------------------------------------------+
+ | Task : Read value of the selected channel or port |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | UINT ui_NoOfChannels : No Of Channels To read |
+ | UINT *data : Data Pointer to read status |
+ data[0] :0 read single channel
+ 1 read port value
+ data[1] port no
+
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+INT i_APCI3200_ReadDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Temp;
+ UINT ui_NoOfChannel;
+ ui_NoOfChannel = CR_CHAN(insn->chanspec);
+ ui_Temp = data[0];
+ *data = inl(devpriv->i_IobaseAddon);
+ if (ui_Temp == 0) {
+ *data = (*data >> ui_NoOfChannel) & 0x1;
+ } // if (ui_Temp==0)
+ else {
+ if (ui_Temp == 1) {
+ if (data[1] < 0 || data[1] > 1) {
+ printk("\nThe port selection is in error\n");
+ return -EINVAL;
+ } //if(data[1] <0 ||data[1] >1)
+ switch (ui_NoOfChannel) {
+ case 2:
+ *data = (*data >> (2 * data[1])) & 3;
+ break;
+
+ case 3:
+ break;
+
+ default:
+ comedi_error(dev, " chan spec wrong");
+ return -EINVAL; // "sorry channel spec wrong "
+ break;
+ } // switch(ui_NoOfChannels)
+ } // if (ui_Temp==1)
+ else {
+ printk("\nSpecified channel not supported \n");
+ } // else if (ui_Temp==1)
+ } // else if (ui_Temp==0)
+ return insn->n;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : INT i_APCI3200_ConfigAnalogInput |
+ | (struct comedi_device *dev,struct comedi_subdevice *s, |
+ | struct comedi_insn *insn,unsigned int *data) |
+ +----------------------------------------------------------------------------+
+ | Task : Configures The Analog Input Subdevice |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | struct comedi_subdevice *s : Subdevice Pointer |
+ | struct comedi_insn *insn : Insn Structure Pointer |
+ | unsigned int *data : Data Pointer contains |
+ | configuration parameters as below |
+ | |
+ | data[0]
+ | 0:Normal AI |
+ | 1:RTD |
+ | 2:THERMOCOUPLE |
+ | data[1] : Gain To Use |
+ | |
+ | data[2] : Polarity
+ | 0:Bipolar |
+ | 1:Unipolar |
+ | |
+ | data[3] : Offset Range
+ | |
+ | data[4] : Coupling
+ | 0:DC Coupling |
+ | 1:AC Coupling |
+ | |
+ | data[5] :Differential/Single
+ | 0:Single |
+ | 1:Differential |
+ | |
+ | data[6] :TimerReloadValue
+ | |
+ | data[7] :ConvertingTimeUnit
+ | |
+ | data[8] :0 Analog voltage measurement
+ 1 Resistance measurement
+ 2 Temperature measurement
+ | data[9] :Interrupt
+ | 0:Disable
+ | 1:Enable
+ data[10] :Type of Thermocouple
+ | data[11] : 0: single channel
+ Module Number
+ |
+ | data[12]
+ | 0:Single Read
+ | 1:Read more channel
+ 2:Single scan
+ | 3:Continous Scan
+ data[13] :Number of channels to read
+ | data[14] :RTD connection type
+ :0:RTD not used
+ 1:RTD 2 wire connection
+ 2:RTD 3 wire connection
+ 3:RTD 4 wire connection
+ | |
+ | |
+ | |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+INT i_APCI3200_ConfigAnalogInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ UINT ul_Config = 0, ul_Temp = 0;
+ UINT ui_ChannelNo = 0;
+ UINT ui_Dummy = 0;
+ INT i_err = 0;
+
+ //Begin JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+
+#ifdef PRINT_INFO
+ INT i = 0, i2 = 0;
+#endif
+ //End JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ // Initialize the structure
+ if (s_BoardInfos[dev->minor].b_StructInitialized != 1) {
+ s_BoardInfos[dev->minor].i_CJCAvailable = 1;
+ s_BoardInfos[dev->minor].i_CJCPolarity = 0;
+ s_BoardInfos[dev->minor].i_CJCGain = 2; //changed from 0 to 2
+ s_BoardInfos[dev->minor].i_InterruptFlag = 0;
+ s_BoardInfos[dev->minor].i_AutoCalibration = 0; //: auto calibration
+ s_BoardInfos[dev->minor].i_ChannelCount = 0;
+ s_BoardInfos[dev->minor].i_Sum = 0;
+ s_BoardInfos[dev->minor].ui_Channel_num = 0;
+ s_BoardInfos[dev->minor].i_Count = 0;
+ s_BoardInfos[dev->minor].i_Initialised = 0;
+ s_BoardInfos[dev->minor].b_StructInitialized = 1;
+
+ //Begin JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ s_BoardInfos[dev->minor].i_ConnectionType = 0;
+ //End JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+
+ //Begin JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ memset(s_BoardInfos[dev->minor].s_Module, 0,
+ sizeof(s_BoardInfos[dev->minor].s_Module[MAX_MODULE]));
+
+ v_GetAPCI3200EepromCalibrationValue(devpriv->i_IobaseAmcc,
+ &s_BoardInfos[dev->minor]);
+
+#ifdef PRINT_INFO
+ for (i = 0; i < MAX_MODULE; i++) {
+ printk("\n s_Module[%i].ul_CurrentSourceCJC = %lu", i,
+ s_BoardInfos[dev->minor].s_Module[i].
+ ul_CurrentSourceCJC);
+
+ for (i2 = 0; i2 < 5; i2++) {
+ printk("\n s_Module[%i].ul_CurrentSource [%i] = %lu", i, i2, s_BoardInfos[dev->minor].s_Module[i].ul_CurrentSource[i2]);
+ }
+
+ for (i2 = 0; i2 < 8; i2++) {
+ printk("\n s_Module[%i].ul_GainFactor [%i] = %lu", i, i2, s_BoardInfos[dev->minor].s_Module[i].ul_GainFactor[i2]);
+ }
+
+ for (i2 = 0; i2 < 8; i2++) {
+ printk("\n s_Module[%i].w_GainValue [%i] = %u",
+ i, i2,
+ s_BoardInfos[dev->minor].s_Module[i].
+ w_GainValue[i2]);
+ }
+ }
+#endif
+ //End JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ }
+
+ if (data[0] != 0 && data[0] != 1 && data[0] != 2) {
+ printk("\nThe selection of acquisition type is in error\n");
+ i_err++;
+ } //if(data[0]!=0 && data[0]!=1 && data[0]!=2)
+ if (data[0] == 1) {
+ if (data[14] != 0 && data[14] != 1 && data[14] != 2
+ && data[14] != 4) {
+ printk("\n Error in selection of RTD connection type\n");
+ i_err++;
+ } //if(data[14]!=0 && data[14]!=1 && data[14]!=2 && data[14]!=4)
+ } //if(data[0]==1 )
+ if (data[1] < 0 || data[1] > 7) {
+ printk("\nThe selection of gain is in error\n");
+ i_err++;
+ } // if(data[1]<0 || data[1]>7)
+ if (data[2] != 0 && data[2] != 1) {
+ printk("\nThe selection of polarity is in error\n");
+ i_err++;
+ } //if(data[2]!=0 && data[2]!=1)
+ if (data[3] != 0) {
+ printk("\nThe selection of offset range is in error\n");
+ i_err++;
+ } // if(data[3]!=0)
+ if (data[4] != 0 && data[4] != 1) {
+ printk("\nThe selection of coupling is in error\n");
+ i_err++;
+ } //if(data[4]!=0 && data[4]!=1)
+ if (data[5] != 0 && data[5] != 1) {
+ printk("\nThe selection of single/differential mode is in error\n");
+ i_err++;
+ } //if(data[5]!=0 && data[5]!=1)
+ if (data[8] != 0 && data[8] != 1 && data[2] != 2) {
+ printk("\nError in selection of functionality\n");
+ } //if(data[8]!=0 && data[8]!=1 && data[2]!=2)
+ if (data[12] == 0 || data[12] == 1) {
+ if (data[6] != 20 && data[6] != 40 && data[6] != 80
+ && data[6] != 160) {
+ printk("\nThe selection of conversion time reload value is in error\n");
+ i_err++;
+ } // if (data[6]!=20 && data[6]!=40 && data[6]!=80 && data[6]!=160 )
+ if (data[7] != 2) {
+ printk("\nThe selection of conversion time unit is in error\n");
+ i_err++;
+ } // if(data[7]!=2)
+ }
+ if (data[9] != 0 && data[9] != 1) {
+ printk("\nThe selection of interrupt enable is in error\n");
+ i_err++;
+ } //if(data[9]!=0 && data[9]!=1)
+ if (data[11] < 0 || data[11] > 4) {
+ printk("\nThe selection of module is in error\n");
+ i_err++;
+ } //if(data[11] <0 || data[11]>1)
+ if (data[12] < 0 || data[12] > 3) {
+ printk("\nThe selection of singlechannel/scan selection is in error\n");
+ i_err++;
+ } //if(data[12] < 0 || data[12]> 3)
+ if (data[13] < 0 || data[13] > 16) {
+ printk("\nThe selection of number of channels is in error\n");
+ i_err++;
+ } // if(data[13] <0 ||data[13] >15)
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ /*
+ i_ChannelCount=data[13];
+ i_ScanType=data[12];
+ i_ADDIDATAPolarity = data[2];
+ i_ADDIDATAGain=data[1];
+ i_ADDIDATAConversionTime=data[6];
+ i_ADDIDATAConversionTimeUnit=data[7];
+ i_ADDIDATAType=data[0];
+ */
+
+ // Save acquisition configuration for the actual board
+ s_BoardInfos[dev->minor].i_ChannelCount = data[13];
+ s_BoardInfos[dev->minor].i_ScanType = data[12];
+ s_BoardInfos[dev->minor].i_ADDIDATAPolarity = data[2];
+ s_BoardInfos[dev->minor].i_ADDIDATAGain = data[1];
+ s_BoardInfos[dev->minor].i_ADDIDATAConversionTime = data[6];
+ s_BoardInfos[dev->minor].i_ADDIDATAConversionTimeUnit = data[7];
+ s_BoardInfos[dev->minor].i_ADDIDATAType = data[0];
+ //Begin JK 19.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+ s_BoardInfos[dev->minor].i_ConnectionType = data[5];
+ //End JK 19.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+ //END JK 06.07.04: Management of sevrals boards
+
+ //Begin JK 19.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+ memset(s_BoardInfos[dev->minor].ui_ScanValueArray, 0, (7 + 12) * sizeof(unsigned int)); // 7 is the maximal number of channels
+ //End JK 19.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+
+ //BEGIN JK 02.07.04 : This while can't be do, it block the process when using severals boards
+ //while(i_InterruptFlag==1)
+ while (s_BoardInfos[dev->minor].i_InterruptFlag == 1) {
+#ifndef MSXBOX
+ udelay(1);
+#else
+ // In the case where the driver is compiled for the MSX-Box
+ // we used a printk to have a little delay because udelay
+ // seems to be broken under the MSX-Box.
+ // This solution hat to be studied.
+ printk("");
+#endif
+ }
+ //END JK 02.07.04 : This while can't be do, it block the process when using severals boards
+
+ ui_ChannelNo = CR_CHAN(insn->chanspec); // get the channel
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_ChannelNo=ui_ChannelNo;
+ //ui_Channel_num =ui_ChannelNo;
+
+ s_BoardInfos[dev->minor].i_ChannelNo = ui_ChannelNo;
+ s_BoardInfos[dev->minor].ui_Channel_num = ui_ChannelNo;
+
+ //END JK 06.07.04: Management of sevrals boards
+
+ if (data[5] == 0) {
+ if (ui_ChannelNo < 0 || ui_ChannelNo > 15) {
+ printk("\nThe Selection of the channel is in error\n");
+ i_err++;
+ } // if(ui_ChannelNo<0 || ui_ChannelNo>15)
+ } //if(data[5]==0)
+ else {
+ if (data[14] == 2) {
+ if (ui_ChannelNo < 0 || ui_ChannelNo > 3) {
+ printk("\nThe Selection of the channel is in error\n");
+ i_err++;
+ } // if(ui_ChannelNo<0 || ui_ChannelNo>3)
+ } //if(data[14]==2)
+ else {
+ if (ui_ChannelNo < 0 || ui_ChannelNo > 7) {
+ printk("\nThe Selection of the channel is in error\n");
+ i_err++;
+ } // if(ui_ChannelNo<0 || ui_ChannelNo>7)
+ } //elseif(data[14]==2)
+ } //elseif(data[5]==0)
+ if (data[12] == 0 || data[12] == 1) {
+ switch (data[5]) {
+ case 0:
+ if (ui_ChannelNo >= 0 && ui_ChannelNo <= 3) {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Offset=0;
+ s_BoardInfos[dev->minor].i_Offset = 0;
+ //END JK 06.07.04: Management of sevrals boards
+ } //if(ui_ChannelNo >=0 && ui_ChannelNo <=3)
+ if (ui_ChannelNo >= 4 && ui_ChannelNo <= 7) {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Offset=64;
+ s_BoardInfos[dev->minor].i_Offset = 64;
+ //END JK 06.07.04: Management of sevrals boards
+ } //if(ui_ChannelNo >=4 && ui_ChannelNo <=7)
+ if (ui_ChannelNo >= 8 && ui_ChannelNo <= 11) {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Offset=128;
+ s_BoardInfos[dev->minor].i_Offset = 128;
+ //END JK 06.07.04: Management of sevrals boards
+ } //if(ui_ChannelNo >=8 && ui_ChannelNo <=11)
+ if (ui_ChannelNo >= 12 && ui_ChannelNo <= 15) {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Offset=192;
+ s_BoardInfos[dev->minor].i_Offset = 192;
+ //END JK 06.07.04: Management of sevrals boards
+ } //if(ui_ChannelNo >=12 && ui_ChannelNo <=15)
+ break;
+ case 1:
+ if (data[14] == 2) {
+ if (ui_ChannelNo == 0) {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Offset=0;
+ s_BoardInfos[dev->minor].i_Offset = 0;
+ //END JK 06.07.04: Management of sevrals boards
+ } //if(ui_ChannelNo ==0 )
+ if (ui_ChannelNo == 1) {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Offset=0;
+ s_BoardInfos[dev->minor].i_Offset = 64;
+ //END JK 06.07.04: Management of sevrals boards
+ } // if(ui_ChannelNo ==1)
+ if (ui_ChannelNo == 2) {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Offset=128;
+ s_BoardInfos[dev->minor].i_Offset = 128;
+ //END JK 06.07.04: Management of sevrals boards
+ } //if(ui_ChannelNo ==2 )
+ if (ui_ChannelNo == 3) {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Offset=192;
+ s_BoardInfos[dev->minor].i_Offset = 192;
+ //END JK 06.07.04: Management of sevrals boards
+ } //if(ui_ChannelNo ==3)
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_ChannelNo=0;
+ s_BoardInfos[dev->minor].i_ChannelNo = 0;
+ //END JK 06.07.04: Management of sevrals boards
+ ui_ChannelNo = 0;
+ break;
+ } //if(data[14]==2)
+ if (ui_ChannelNo >= 0 && ui_ChannelNo <= 1) {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Offset=0;
+ s_BoardInfos[dev->minor].i_Offset = 0;
+ //END JK 06.07.04: Management of sevrals boards
+ } //if(ui_ChannelNo >=0 && ui_ChannelNo <=1)
+ if (ui_ChannelNo >= 2 && ui_ChannelNo <= 3) {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_ChannelNo=i_ChannelNo-2;
+ //i_Offset=64;
+ s_BoardInfos[dev->minor].i_ChannelNo =
+ s_BoardInfos[dev->minor].i_ChannelNo -
+ 2;
+ s_BoardInfos[dev->minor].i_Offset = 64;
+ //END JK 06.07.04: Management of sevrals boards
+ ui_ChannelNo = ui_ChannelNo - 2;
+ } //if(ui_ChannelNo >=2 && ui_ChannelNo <=3)
+ if (ui_ChannelNo >= 4 && ui_ChannelNo <= 5) {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_ChannelNo=i_ChannelNo-4;
+ //i_Offset=128;
+ s_BoardInfos[dev->minor].i_ChannelNo =
+ s_BoardInfos[dev->minor].i_ChannelNo -
+ 4;
+ s_BoardInfos[dev->minor].i_Offset = 128;
+ //END JK 06.07.04: Management of sevrals boards
+ ui_ChannelNo = ui_ChannelNo - 4;
+ } //if(ui_ChannelNo >=4 && ui_ChannelNo <=5)
+ if (ui_ChannelNo >= 6 && ui_ChannelNo <= 7) {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_ChannelNo=i_ChannelNo-6;
+ //i_Offset=192;
+ s_BoardInfos[dev->minor].i_ChannelNo =
+ s_BoardInfos[dev->minor].i_ChannelNo -
+ 6;
+ s_BoardInfos[dev->minor].i_Offset = 192;
+ //END JK 06.07.04: Management of sevrals boards
+ ui_ChannelNo = ui_ChannelNo - 6;
+ } //if(ui_ChannelNo >=6 && ui_ChannelNo <=7)
+ break;
+
+ default:
+ printk("\n This selection of polarity does not exist\n");
+ i_err++;
+ } //switch(data[2])
+ } //if(data[12]==0 || data[12]==1)
+ else {
+ switch (data[11]) {
+ case 1:
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Offset=0;
+ s_BoardInfos[dev->minor].i_Offset = 0;
+ //END JK 06.07.04: Management of sevrals boards
+ break;
+ case 2:
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Offset=64;
+ s_BoardInfos[dev->minor].i_Offset = 64;
+ //END JK 06.07.04: Management of sevrals boards
+ break;
+ case 3:
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Offset=128;
+ s_BoardInfos[dev->minor].i_Offset = 128;
+ //END JK 06.07.04: Management of sevrals boards
+ break;
+ case 4:
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Offset=192;
+ s_BoardInfos[dev->minor].i_Offset = 192;
+ //END JK 06.07.04: Management of sevrals boards
+ break;
+ default:
+ printk("\nError in module selection\n");
+ i_err++;
+ } // switch(data[11])
+ } // elseif(data[12]==0 || data[12]==1)
+ if (i_err) {
+ i_APCI3200_Reset(dev);
+ return -EINVAL;
+ }
+ //if(i_ScanType!=1)
+ if (s_BoardInfos[dev->minor].i_ScanType != 1) {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Count=0;
+ //i_Sum=0;
+ s_BoardInfos[dev->minor].i_Count = 0;
+ s_BoardInfos[dev->minor].i_Sum = 0;
+ //END JK 06.07.04: Management of sevrals boards
+ } //if(i_ScanType!=1)
+
+ ul_Config =
+ data[1] | (data[2] << 6) | (data[5] << 7) | (data[3] << 8) |
+ (data[4] << 9);
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //END JK 06.07.04: Management of sevrals boards
+ /*********************************/
+ /* Write the channel to configure */
+ /*********************************/
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //outl(0 | ui_ChannelNo , devpriv->iobase+i_Offset + 0x4);
+ outl(0 | ui_ChannelNo,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 0x4);
+ //END JK 06.07.04: Management of sevrals boards
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //END JK 06.07.04: Management of sevrals boards
+ /**************************/
+ /* Reset the configuration */
+ /**************************/
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //outl(0 , devpriv->iobase+i_Offset + 0x0);
+ outl(0, devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 0x0);
+ //END JK 06.07.04: Management of sevrals boards
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //END JK 06.07.04: Management of sevrals boards
+
+ /***************************/
+ /* Write the configuration */
+ /***************************/
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //outl(ul_Config , devpriv->iobase+i_Offset + 0x0);
+ outl(ul_Config,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 0x0);
+ //END JK 06.07.04: Management of sevrals boards
+
+ /***************************/
+ /*Reset the calibration bit */
+ /***************************/
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //ul_Temp = inl(devpriv->iobase+i_Offset + 12);
+ ul_Temp = inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 12);
+ //END JK 06.07.04: Management of sevrals boards
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //END JK 06.07.04: Management of sevrals boards
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //outl((ul_Temp & 0xFFF9FFFF) , devpriv->iobase+.i_Offset + 12);
+ outl((ul_Temp & 0xFFF9FFFF),
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 12);
+ //END JK 06.07.04: Management of sevrals boards
+
+ if (data[9] == 1) {
+ devpriv->tsk_Current = current;
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_InterruptFlag=1;
+ s_BoardInfos[dev->minor].i_InterruptFlag = 1;
+ //END JK 06.07.04: Management of sevrals boards
+ } // if(data[9]==1)
+ else {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_InterruptFlag=0;
+ s_BoardInfos[dev->minor].i_InterruptFlag = 0;
+ //END JK 06.07.04: Management of sevrals boards
+ } //else if(data[9]==1)
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Initialised=1;
+ s_BoardInfos[dev->minor].i_Initialised = 1;
+ //END JK 06.07.04: Management of sevrals boards
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //if(i_ScanType==1)
+ if (s_BoardInfos[dev->minor].i_ScanType == 1)
+ //END JK 06.07.04: Management of sevrals boards
+ {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //i_Sum=i_Sum+1;
+ s_BoardInfos[dev->minor].i_Sum =
+ s_BoardInfos[dev->minor].i_Sum + 1;
+ //END JK 06.07.04: Management of sevrals boards
+
+ insn->unused[0] = 0;
+ i_APCI3200_ReadAnalogInput(dev, s, insn, &ui_Dummy);
+ }
+
+ return insn->n;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI3200_ReadAnalogInput |
+ | (struct comedi_device *dev,struct comedi_subdevice *s, |
+ | struct comedi_insn *insn,unsigned int *data) |
+ +----------------------------------------------------------------------------+
+ | Task : Read value of the selected channel |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | UINT ui_NoOfChannels : No Of Channels To read |
+ | UINT *data : Data Pointer to read status |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ | data[0] : Digital Value Of Input |
+ | data[1] : Calibration Offset Value |
+ | data[2] : Calibration Gain Value
+ | data[3] : CJC value
+ | data[4] : CJC offset value
+ | data[5] : CJC gain value
+ | Begin JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ | data[6] : CJC current source from eeprom
+ | data[7] : Channel current source from eeprom
+ | data[8] : Channle gain factor from eeprom
+ | End JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+INT i_APCI3200_ReadAnalogInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_DummyValue = 0;
+ int i_ConvertCJCCalibration;
+ int i = 0;
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //if(i_Initialised==0)
+ if (s_BoardInfos[dev->minor].i_Initialised == 0)
+ //END JK 06.07.04: Management of sevrals boards
+ {
+ i_APCI3200_Reset(dev);
+ return -EINVAL;
+ } //if(i_Initialised==0);
+
+#ifdef PRINT_INFO
+ printk("\n insn->unused[0] = %i", insn->unused[0]);
+#endif
+
+ switch (insn->unused[0]) {
+ case 0:
+
+ i_APCI3200_Read1AnalogInputChannel(dev, s, insn,
+ &ui_DummyValue);
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //ui_InterruptChannelValue[i_Count+0]=ui_DummyValue;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[s_BoardInfos[dev->minor].
+ i_Count + 0] = ui_DummyValue;
+ //END JK 06.07.04: Management of sevrals boards
+
+ //Begin JK 25.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ i_APCI3200_GetChannelCalibrationValue(dev,
+ s_BoardInfos[dev->minor].ui_Channel_num,
+ &s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[s_BoardInfos[dev->minor].
+ i_Count + 6],
+ &s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[s_BoardInfos[dev->minor].
+ i_Count + 7],
+ &s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[s_BoardInfos[dev->minor].
+ i_Count + 8]);
+
+#ifdef PRINT_INFO
+ printk("\n s_BoardInfos [dev->minor].ui_InterruptChannelValue[s_BoardInfos [dev->minor].i_Count+6] = %lu", s_BoardInfos[dev->minor].ui_InterruptChannelValue[s_BoardInfos[dev->minor].i_Count + 6]);
+
+ printk("\n s_BoardInfos [dev->minor].ui_InterruptChannelValue[s_BoardInfos [dev->minor].i_Count+7] = %lu", s_BoardInfos[dev->minor].ui_InterruptChannelValue[s_BoardInfos[dev->minor].i_Count + 7]);
+
+ printk("\n s_BoardInfos [dev->minor].ui_InterruptChannelValue[s_BoardInfos [dev->minor].i_Count+8] = %lu", s_BoardInfos[dev->minor].ui_InterruptChannelValue[s_BoardInfos[dev->minor].i_Count + 8]);
+#endif
+
+ //End JK 25.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //if((i_ADDIDATAType==2) && (i_InterruptFlag == FALSE) && (i_CJCAvailable==1))
+ if ((s_BoardInfos[dev->minor].i_ADDIDATAType == 2)
+ && (s_BoardInfos[dev->minor].i_InterruptFlag == FALSE)
+ && (s_BoardInfos[dev->minor].i_CJCAvailable == 1))
+ //END JK 06.07.04: Management of sevrals boards
+ {
+ i_APCI3200_ReadCJCValue(dev, &ui_DummyValue);
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //ui_InterruptChannelValue[i_Count + 3]=ui_DummyValue;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[s_BoardInfos[dev->
+ minor].i_Count + 3] = ui_DummyValue;
+ //END JK 06.07.04: Management of sevrals boards
+ } //if((i_ADDIDATAType==2) && (i_InterruptFlag == FALSE))
+ else {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //ui_InterruptChannelValue[i_Count + 3]=0;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[s_BoardInfos[dev->
+ minor].i_Count + 3] = 0;
+ //END JK 06.07.04: Management of sevrals boards
+ } //elseif((i_ADDIDATAType==2) && (i_InterruptFlag == FALSE) && (i_CJCAvailable==1))
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //if (( i_AutoCalibration == FALSE) && (i_InterruptFlag == FALSE))
+ if ((s_BoardInfos[dev->minor].i_AutoCalibration == FALSE)
+ && (s_BoardInfos[dev->minor].i_InterruptFlag == FALSE))
+ //END JK 06.07.04: Management of sevrals boards
+ {
+ i_APCI3200_ReadCalibrationOffsetValue(dev,
+ &ui_DummyValue);
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //ui_InterruptChannelValue[i_Count + 1]=ui_DummyValue;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[s_BoardInfos[dev->
+ minor].i_Count + 1] = ui_DummyValue;
+ //END JK 06.07.04: Management of sevrals boards
+ i_APCI3200_ReadCalibrationGainValue(dev,
+ &ui_DummyValue);
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //ui_InterruptChannelValue[i_Count + 2]=ui_DummyValue;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[s_BoardInfos[dev->
+ minor].i_Count + 2] = ui_DummyValue;
+ //END JK 06.07.04: Management of sevrals boards
+ } //if (( i_AutoCalibration == FALSE) && (i_InterruptFlag == FALSE))
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //if((i_ADDIDATAType==2) && (i_InterruptFlag == FALSE)&& (i_CJCAvailable==1))
+ if ((s_BoardInfos[dev->minor].i_ADDIDATAType == 2)
+ && (s_BoardInfos[dev->minor].i_InterruptFlag == FALSE)
+ && (s_BoardInfos[dev->minor].i_CJCAvailable == 1))
+ //END JK 06.07.04: Management of sevrals boards
+ {
+ /**********************************************************/
+ /*Test if the Calibration channel must be read for the CJC */
+ /**********************************************************/
+ /**********************************/
+ /*Test if the polarity is the same */
+ /**********************************/
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //if(i_CJCPolarity!=i_ADDIDATAPolarity)
+ if (s_BoardInfos[dev->minor].i_CJCPolarity !=
+ s_BoardInfos[dev->minor].i_ADDIDATAPolarity)
+ //END JK 06.07.04: Management of sevrals boards
+ {
+ i_ConvertCJCCalibration = 1;
+ } //if(i_CJCPolarity!=i_ADDIDATAPolarity)
+ else {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //if(i_CJCGain==i_ADDIDATAGain)
+ if (s_BoardInfos[dev->minor].i_CJCGain ==
+ s_BoardInfos[dev->minor].i_ADDIDATAGain)
+ //END JK 06.07.04: Management of sevrals boards
+ {
+ i_ConvertCJCCalibration = 0;
+ } //if(i_CJCGain==i_ADDIDATAGain)
+ else {
+ i_ConvertCJCCalibration = 1;
+ } //elseif(i_CJCGain==i_ADDIDATAGain)
+ } //elseif(i_CJCPolarity!=i_ADDIDATAPolarity)
+ if (i_ConvertCJCCalibration == 1) {
+ i_APCI3200_ReadCJCCalOffset(dev,
+ &ui_DummyValue);
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //ui_InterruptChannelValue[i_Count+4]=ui_DummyValue;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[s_BoardInfos
+ [dev->minor].i_Count + 4] =
+ ui_DummyValue;
+ //END JK 06.07.04: Management of sevrals boards
+
+ i_APCI3200_ReadCJCCalGain(dev, &ui_DummyValue);
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //ui_InterruptChannelValue[i_Count+5]=ui_DummyValue;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[s_BoardInfos
+ [dev->minor].i_Count + 5] =
+ ui_DummyValue;
+ //END JK 06.07.04: Management of sevrals boards
+ } //if(i_ConvertCJCCalibration==1)
+ else {
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //ui_InterruptChannelValue[i_Count+4]=0;
+ //ui_InterruptChannelValue[i_Count+5]=0;
+
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[s_BoardInfos
+ [dev->minor].i_Count + 4] = 0;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[s_BoardInfos
+ [dev->minor].i_Count + 5] = 0;
+ //END JK 06.07.04: Management of sevrals boards
+ } //elseif(i_ConvertCJCCalibration==1)
+ } //if((i_ADDIDATAType==2) && (i_InterruptFlag == FALSE))
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //if(i_ScanType!=1)
+ if (s_BoardInfos[dev->minor].i_ScanType != 1) {
+ //i_Count=0;
+ s_BoardInfos[dev->minor].i_Count = 0;
+ } //if(i_ScanType!=1)
+ else {
+ //i_Count=i_Count +6;
+ //Begin JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ //s_BoardInfos [dev->minor].i_Count=s_BoardInfos [dev->minor].i_Count +6;
+ s_BoardInfos[dev->minor].i_Count =
+ s_BoardInfos[dev->minor].i_Count + 9;
+ //End JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ } //else if(i_ScanType!=1)
+
+ //if((i_ScanType==1) &&(i_InterruptFlag==1))
+ if ((s_BoardInfos[dev->minor].i_ScanType == 1)
+ && (s_BoardInfos[dev->minor].i_InterruptFlag == 1)) {
+ //i_Count=i_Count-6;
+ //Begin JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ //s_BoardInfos [dev->minor].i_Count=s_BoardInfos [dev->minor].i_Count-6;
+ s_BoardInfos[dev->minor].i_Count =
+ s_BoardInfos[dev->minor].i_Count - 9;
+ //End JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ }
+ //if(i_ScanType==0)
+ if (s_BoardInfos[dev->minor].i_ScanType == 0) {
+ /*
+ data[0]= ui_InterruptChannelValue[0];
+ data[1]= ui_InterruptChannelValue[1];
+ data[2]= ui_InterruptChannelValue[2];
+ data[3]= ui_InterruptChannelValue[3];
+ data[4]= ui_InterruptChannelValue[4];
+ data[5]= ui_InterruptChannelValue[5];
+ */
+#ifdef PRINT_INFO
+ printk("\n data[0]= s_BoardInfos [dev->minor].ui_InterruptChannelValue[0];");
+#endif
+ data[0] =
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[0];
+ data[1] =
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[1];
+ data[2] =
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[2];
+ data[3] =
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[3];
+ data[4] =
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[4];
+ data[5] =
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[5];
+
+ //Begin JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ //printk("\n 0 - i_APCI3200_GetChannelCalibrationValue data [6] = %lu, data [7] = %lu, data [8] = %lu", data [6], data [7], data [8]);
+ i_APCI3200_GetChannelCalibrationValue(dev,
+ s_BoardInfos[dev->minor].ui_Channel_num,
+ &data[6], &data[7], &data[8]);
+ //End JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ }
+ break;
+ case 1:
+
+ for (i = 0; i < insn->n; i++) {
+ //data[i]=ui_InterruptChannelValue[i];
+ data[i] =
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue[i];
+ }
+
+ //i_Count=0;
+ //i_Sum=0;
+ //if(i_ScanType==1)
+ s_BoardInfos[dev->minor].i_Count = 0;
+ s_BoardInfos[dev->minor].i_Sum = 0;
+ if (s_BoardInfos[dev->minor].i_ScanType == 1) {
+ //i_Initialised=0;
+ //i_InterruptFlag=0;
+ s_BoardInfos[dev->minor].i_Initialised = 0;
+ s_BoardInfos[dev->minor].i_InterruptFlag = 0;
+ //END JK 06.07.04: Management of sevrals boards
+ }
+ break;
+ default:
+ printk("\nThe parameters passed are in error\n");
+ i_APCI3200_Reset(dev);
+ return -EINVAL;
+ } //switch(insn->unused[0])
+
+ return insn->n;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI3200_Read1AnalogInputChannel |
+ | (struct comedi_device *dev,struct comedi_subdevice *s, |
+ | struct comedi_insn *insn,unsigned int *data) |
+ +----------------------------------------------------------------------------+
+ | Task : Read value of the selected channel |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | UINT ui_NoOfChannel : Channel No to read |
+ | UINT *data : Data Pointer to read status |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ | data[0] : Digital Value read |
+ |
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+INT i_APCI3200_Read1AnalogInputChannel(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_EOC = 0;
+ UINT ui_ChannelNo = 0;
+ UINT ui_CommandRegister = 0;
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //ui_ChannelNo=i_ChannelNo;
+ ui_ChannelNo = s_BoardInfos[dev->minor].i_ChannelNo;
+
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ /*********************************/
+ /* Write the channel to configure */
+ /*********************************/
+ //Begin JK 20.10.2004: Bad channel value is used when using differential mode
+ //outl(0 | ui_Channel_num , devpriv->iobase+i_Offset + 0x4);
+ //outl(0 | s_BoardInfos [dev->minor].ui_Channel_num , devpriv->iobase+s_BoardInfos [dev->minor].i_Offset + 0x4);
+ outl(0 | s_BoardInfos[dev->minor].i_ChannelNo,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 0x4);
+ //End JK 20.10.2004: Bad channel value is used when using differential mode
+
+ /*******************************/
+ /* Set the convert timing unit */
+ /*******************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+
+ //outl(i_ADDIDATAConversionTimeUnit , devpriv->iobase+i_Offset + 36);
+ outl(s_BoardInfos[dev->minor].i_ADDIDATAConversionTimeUnit,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 36);
+
+ /**************************/
+ /* Set the convert timing */
+ /**************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+
+ //outl(i_ADDIDATAConversionTime , devpriv->iobase+i_Offset + 32);
+ outl(s_BoardInfos[dev->minor].i_ADDIDATAConversionTime,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 32);
+
+ /**************************************************************************/
+ /* Set the start end stop index to the selected channel and set the start */
+ /**************************************************************************/
+
+ ui_CommandRegister = ui_ChannelNo | (ui_ChannelNo << 8) | 0x80000;
+
+ /*********************************/
+ /*Test if the interrupt is enable */
+ /*********************************/
+
+ //if (i_InterruptFlag == ADDIDATA_ENABLE)
+ if (s_BoardInfos[dev->minor].i_InterruptFlag == ADDIDATA_ENABLE) {
+ /************************/
+ /* Enable the interrupt */
+ /************************/
+ ui_CommandRegister = ui_CommandRegister | 0x00100000;
+ } //if (i_InterruptFlag == ADDIDATA_ENABLE)
+
+ /******************************/
+ /* Write the command register */
+ /******************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+
+ //outl(ui_CommandRegister, devpriv->iobase+i_Offset + 8);
+ outl(ui_CommandRegister,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 8);
+
+ /*****************************/
+ /*Test if interrupt is enable */
+ /*****************************/
+ //if (i_InterruptFlag == ADDIDATA_DISABLE)
+ if (s_BoardInfos[dev->minor].i_InterruptFlag == ADDIDATA_DISABLE) {
+ do {
+ /*************************/
+ /*Read the EOC Status bit */
+ /*************************/
+
+ //ui_EOC = inl(devpriv->iobase+i_Offset + 20) & 1;
+ ui_EOC = inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 20) & 1;
+
+ } while (ui_EOC != 1);
+
+ /***************************************/
+ /* Read the digital value of the input */
+ /***************************************/
+
+ //data[0] = inl (devpriv->iobase+i_Offset + 28);
+ data[0] =
+ inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 28);
+ //END JK 06.07.04: Management of sevrals boards
+
+ } // if (i_InterruptFlag == ADDIDATA_DISABLE)
+ return 0;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI3200_ReadCalibrationOffsetValue |
+ | (struct comedi_device *dev,struct comedi_subdevice *s, |
+ | struct comedi_insn *insn,unsigned int *data) |
+ +----------------------------------------------------------------------------+
+ | Task : Read calibration offset value of the selected channel|
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | UINT *data : Data Pointer to read status |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ | data[0] : Calibration offset Value |
+ |
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+int i_APCI3200_ReadCalibrationOffsetValue(struct comedi_device * dev, UINT * data)
+{
+ UINT ui_Temp = 0, ui_EOC = 0;
+ UINT ui_CommandRegister = 0;
+
+ //BEGIN JK 06.07.04: Management of sevrals boards
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ /*********************************/
+ /* Write the channel to configure */
+ /*********************************/
+ //Begin JK 20.10.2004: This seems not necessary !
+ //outl(0 | ui_Channel_num , devpriv->iobase+i_Offset + 0x4);
+ //outl(0 | s_BoardInfos [dev->minor].ui_Channel_num , devpriv->iobase+s_BoardInfos [dev->minor].i_Offset + 0x4);
+ //End JK 20.10.2004: This seems not necessary !
+
+ /*******************************/
+ /* Set the convert timing unit */
+ /*******************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(i_ADDIDATAConversionTimeUnit , devpriv->iobase+i_Offset + 36);
+ outl(s_BoardInfos[dev->minor].i_ADDIDATAConversionTimeUnit,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 36);
+ /**************************/
+ /* Set the convert timing */
+ /**************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(i_ADDIDATAConversionTime , devpriv->iobase+i_Offset + 32);
+ outl(s_BoardInfos[dev->minor].i_ADDIDATAConversionTime,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 32);
+ /*****************************/
+ /*Read the calibration offset */
+ /*****************************/
+ //ui_Temp = inl(devpriv->iobase+i_Offset + 12);
+ ui_Temp = inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 12);
+
+ /*********************************/
+ /*Configure the Offset Conversion */
+ /*********************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl((ui_Temp | 0x00020000), devpriv->iobase+i_Offset + 12);
+ outl((ui_Temp | 0x00020000),
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 12);
+ /*******************************/
+ /*Initialise ui_CommandRegister */
+ /*******************************/
+
+ ui_CommandRegister = 0;
+
+ /*********************************/
+ /*Test if the interrupt is enable */
+ /*********************************/
+
+ //if (i_InterruptFlag == ADDIDATA_ENABLE)
+ if (s_BoardInfos[dev->minor].i_InterruptFlag == ADDIDATA_ENABLE) {
+
+ /**********************/
+ /*Enable the interrupt */
+ /**********************/
+
+ ui_CommandRegister = ui_CommandRegister | 0x00100000;
+
+ } //if (i_InterruptFlag == ADDIDATA_ENABLE)
+
+ /**********************/
+ /*Start the conversion */
+ /**********************/
+ ui_CommandRegister = ui_CommandRegister | 0x00080000;
+
+ /***************************/
+ /*Write the command regiter */
+ /***************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(ui_CommandRegister, devpriv->iobase+i_Offset + 8);
+ outl(ui_CommandRegister,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 8);
+
+ /*****************************/
+ /*Test if interrupt is enable */
+ /*****************************/
+
+ //if (i_InterruptFlag == ADDIDATA_DISABLE)
+ if (s_BoardInfos[dev->minor].i_InterruptFlag == ADDIDATA_DISABLE) {
+
+ do {
+ /*******************/
+ /*Read the EOC flag */
+ /*******************/
+
+ //ui_EOC = inl (devpriv->iobase+i_Offset + 20) & 1;
+ ui_EOC = inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 20) & 1;
+
+ } while (ui_EOC != 1);
+
+ /**************************************************/
+ /*Read the digital value of the calibration Offset */
+ /**************************************************/
+
+ //data[0] = inl(devpriv->iobase+i_Offset+ 28);
+ data[0] =
+ inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 28);
+ } //if (i_InterruptFlag == ADDIDATA_DISABLE)
+ return 0;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI3200_ReadCalibrationGainValue |
+ | (struct comedi_device *dev,struct comedi_subdevice *s, |
+ | struct comedi_insn *insn,unsigned int *data) |
+ +----------------------------------------------------------------------------+
+ | Task : Read calibration gain value of the selected channel |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | UINT *data : Data Pointer to read status |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ | data[0] : Calibration gain Value Of Input |
+ |
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+int i_APCI3200_ReadCalibrationGainValue(struct comedi_device * dev, UINT * data)
+{
+ UINT ui_EOC = 0;
+ INT ui_CommandRegister = 0;
+
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ /*********************************/
+ /* Write the channel to configure */
+ /*********************************/
+ //Begin JK 20.10.2004: This seems not necessary !
+ //outl(0 | ui_Channel_num , devpriv->iobase+i_Offset + 0x4);
+ //outl(0 | s_BoardInfos [dev->minor].ui_Channel_num , devpriv->iobase+s_BoardInfos [dev->minor].i_Offset + 0x4);
+ //End JK 20.10.2004: This seems not necessary !
+
+ /***************************/
+ /*Read the calibration gain */
+ /***************************/
+ /*******************************/
+ /* Set the convert timing unit */
+ /*******************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(i_ADDIDATAConversionTimeUnit , devpriv->iobase+i_Offset + 36);
+ outl(s_BoardInfos[dev->minor].i_ADDIDATAConversionTimeUnit,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 36);
+ /**************************/
+ /* Set the convert timing */
+ /**************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(i_ADDIDATAConversionTime , devpriv->iobase+i_Offset + 32);
+ outl(s_BoardInfos[dev->minor].i_ADDIDATAConversionTime,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 32);
+ /*******************************/
+ /*Configure the Gain Conversion */
+ /*******************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(0x00040000 , devpriv->iobase+i_Offset + 12);
+ outl(0x00040000,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 12);
+
+ /*******************************/
+ /*Initialise ui_CommandRegister */
+ /*******************************/
+
+ ui_CommandRegister = 0;
+
+ /*********************************/
+ /*Test if the interrupt is enable */
+ /*********************************/
+
+ //if (i_InterruptFlag == ADDIDATA_ENABLE)
+ if (s_BoardInfos[dev->minor].i_InterruptFlag == ADDIDATA_ENABLE) {
+
+ /**********************/
+ /*Enable the interrupt */
+ /**********************/
+
+ ui_CommandRegister = ui_CommandRegister | 0x00100000;
+
+ } //if (i_InterruptFlag == ADDIDATA_ENABLE)
+
+ /**********************/
+ /*Start the conversion */
+ /**********************/
+
+ ui_CommandRegister = ui_CommandRegister | 0x00080000;
+ /***************************/
+ /*Write the command regiter */
+ /***************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(ui_CommandRegister , devpriv->iobase+i_Offset + 8);
+ outl(ui_CommandRegister,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 8);
+
+ /*****************************/
+ /*Test if interrupt is enable */
+ /*****************************/
+
+ //if (i_InterruptFlag == ADDIDATA_DISABLE)
+ if (s_BoardInfos[dev->minor].i_InterruptFlag == ADDIDATA_DISABLE) {
+
+ do {
+
+ /*******************/
+ /*Read the EOC flag */
+ /*******************/
+
+ //ui_EOC = inl(devpriv->iobase+i_Offset + 20) & 1;
+ ui_EOC = inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 20) & 1;
+
+ } while (ui_EOC != 1);
+
+ /************************************************/
+ /*Read the digital value of the calibration Gain */
+ /************************************************/
+
+ //data[0] = inl(devpriv->iobase+i_Offset + 28);
+ data[0] =
+ inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 28);
+
+ } //if (i_InterruptFlag == ADDIDATA_DISABLE)
+ return 0;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI3200_ReadCJCValue |
+ | (struct comedi_device *dev,struct comedi_subdevice *s, |
+ | struct comedi_insn *insn,unsigned int *data) |
+ +----------------------------------------------------------------------------+
+ | Task : Read CJC value of the selected channel |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | UINT *data : Data Pointer to read status |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ | data[0] : CJC Value |
+ |
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+
+int i_APCI3200_ReadCJCValue(struct comedi_device * dev, unsigned int * data)
+{
+ UINT ui_EOC = 0;
+ INT ui_CommandRegister = 0;
+
+ /******************************/
+ /*Set the converting time unit */
+ /******************************/
+
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+
+ //outl(i_ADDIDATAConversionTimeUnit , devpriv->iobase+i_Offset + 36);
+ outl(s_BoardInfos[dev->minor].i_ADDIDATAConversionTimeUnit,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 36);
+ /**************************/
+ /* Set the convert timing */
+ /**************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+
+ //outl(i_ADDIDATAConversionTime , devpriv->iobase+i_Offset + 32);
+ outl(s_BoardInfos[dev->minor].i_ADDIDATAConversionTime,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 32);
+
+ /******************************/
+ /*Configure the CJC Conversion */
+ /******************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+
+ //outl( 0x00000400 , devpriv->iobase+i_Offset + 4);
+ outl(0x00000400,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 4);
+ /*******************************/
+ /*Initialise dw_CommandRegister */
+ /*******************************/
+ ui_CommandRegister = 0;
+ /*********************************/
+ /*Test if the interrupt is enable */
+ /*********************************/
+ //if (i_InterruptFlag == ADDIDATA_ENABLE)
+ if (s_BoardInfos[dev->minor].i_InterruptFlag == ADDIDATA_ENABLE) {
+ /**********************/
+ /*Enable the interrupt */
+ /**********************/
+ ui_CommandRegister = ui_CommandRegister | 0x00100000;
+ }
+
+ /**********************/
+ /*Start the conversion */
+ /**********************/
+
+ ui_CommandRegister = ui_CommandRegister | 0x00080000;
+
+ /***************************/
+ /*Write the command regiter */
+ /***************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(ui_CommandRegister , devpriv->iobase+i_Offset + 8);
+ outl(ui_CommandRegister,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 8);
+
+ /*****************************/
+ /*Test if interrupt is enable */
+ /*****************************/
+
+ //if (i_InterruptFlag == ADDIDATA_DISABLE)
+ if (s_BoardInfos[dev->minor].i_InterruptFlag == ADDIDATA_DISABLE) {
+ do {
+
+ /*******************/
+ /*Read the EOC flag */
+ /*******************/
+
+ //ui_EOC = inl(devpriv->iobase+i_Offset + 20) & 1;
+ ui_EOC = inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 20) & 1;
+
+ } while (ui_EOC != 1);
+
+ /***********************************/
+ /*Read the digital value of the CJC */
+ /***********************************/
+
+ //data[0] = inl(devpriv->iobase+i_Offset + 28);
+ data[0] =
+ inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 28);
+
+ } //if (i_InterruptFlag == ADDIDATA_DISABLE)
+ return 0;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI3200_ReadCJCCalOffset |
+ | (struct comedi_device *dev,struct comedi_subdevice *s, |
+ | struct comedi_insn *insn,unsigned int *data) |
+ +----------------------------------------------------------------------------+
+ | Task : Read CJC calibration offset value of the selected channel
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | UINT *data : Data Pointer to read status |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ | data[0] : CJC calibration offset Value
+ |
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+int i_APCI3200_ReadCJCCalOffset(struct comedi_device * dev, unsigned int * data)
+{
+ UINT ui_EOC = 0;
+ INT ui_CommandRegister = 0;
+ /*******************************************/
+ /*Read calibration offset value for the CJC */
+ /*******************************************/
+ /*******************************/
+ /* Set the convert timing unit */
+ /*******************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(i_ADDIDATAConversionTimeUnit , devpriv->iobase+i_Offset + 36);
+ outl(s_BoardInfos[dev->minor].i_ADDIDATAConversionTimeUnit,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 36);
+ /**************************/
+ /* Set the convert timing */
+ /**************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(i_ADDIDATAConversionTime , devpriv->iobase+i_Offset + 32);
+ outl(s_BoardInfos[dev->minor].i_ADDIDATAConversionTime,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 32);
+ /******************************/
+ /*Configure the CJC Conversion */
+ /******************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(0x00000400 , devpriv->iobase+i_Offset + 4);
+ outl(0x00000400,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 4);
+ /*********************************/
+ /*Configure the Offset Conversion */
+ /*********************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(0x00020000, devpriv->iobase+i_Offset + 12);
+ outl(0x00020000,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 12);
+ /*******************************/
+ /*Initialise ui_CommandRegister */
+ /*******************************/
+ ui_CommandRegister = 0;
+ /*********************************/
+ /*Test if the interrupt is enable */
+ /*********************************/
+
+ //if (i_InterruptFlag == ADDIDATA_ENABLE)
+ if (s_BoardInfos[dev->minor].i_InterruptFlag == ADDIDATA_ENABLE) {
+ /**********************/
+ /*Enable the interrupt */
+ /**********************/
+ ui_CommandRegister = ui_CommandRegister | 0x00100000;
+
+ }
+
+ /**********************/
+ /*Start the conversion */
+ /**********************/
+ ui_CommandRegister = ui_CommandRegister | 0x00080000;
+ /***************************/
+ /*Write the command regiter */
+ /***************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(ui_CommandRegister,devpriv->iobase+i_Offset + 8);
+ outl(ui_CommandRegister,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 8);
+ //if (i_InterruptFlag == ADDIDATA_DISABLE)
+ if (s_BoardInfos[dev->minor].i_InterruptFlag == ADDIDATA_DISABLE) {
+ do {
+ /*******************/
+ /*Read the EOC flag */
+ /*******************/
+ //ui_EOC = inl(devpriv->iobase+i_Offset + 20) & 1;
+ ui_EOC = inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 20) & 1;
+ } while (ui_EOC != 1);
+
+ /**************************************************/
+ /*Read the digital value of the calibration Offset */
+ /**************************************************/
+ //data[0] = inl(devpriv->iobase+i_Offset + 28);
+ data[0] =
+ inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 28);
+ } //if (i_InterruptFlag == ADDIDATA_DISABLE)
+ return 0;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI3200_ReadCJCGainValue |
+ | (struct comedi_device *dev,struct comedi_subdevice *s, |
+ | struct comedi_insn *insn,unsigned int *data) |
+ +----------------------------------------------------------------------------+
+ | Task : Read CJC calibration gain value
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | UINT ui_NoOfChannels : No Of Channels To read |
+ | UINT *data : Data Pointer to read status |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ | data[0] : CJC calibration gain value
+ |
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+int i_APCI3200_ReadCJCCalGain(struct comedi_device * dev, unsigned int * data)
+{
+ UINT ui_EOC = 0;
+ INT ui_CommandRegister = 0;
+ /*******************************/
+ /* Set the convert timing unit */
+ /*******************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(i_ADDIDATAConversionTimeUnit , devpriv->iobase+i_Offset + 36);
+ outl(s_BoardInfos[dev->minor].i_ADDIDATAConversionTimeUnit,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 36);
+ /**************************/
+ /* Set the convert timing */
+ /**************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(i_ADDIDATAConversionTime , devpriv->iobase+i_Offset + 32);
+ outl(s_BoardInfos[dev->minor].i_ADDIDATAConversionTime,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 32);
+ /******************************/
+ /*Configure the CJC Conversion */
+ /******************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(0x00000400,devpriv->iobase+i_Offset + 4);
+ outl(0x00000400,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 4);
+ /*******************************/
+ /*Configure the Gain Conversion */
+ /*******************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(0x00040000,devpriv->iobase+i_Offset + 12);
+ outl(0x00040000,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 12);
+
+ /*******************************/
+ /*Initialise dw_CommandRegister */
+ /*******************************/
+ ui_CommandRegister = 0;
+ /*********************************/
+ /*Test if the interrupt is enable */
+ /*********************************/
+ //if (i_InterruptFlag == ADDIDATA_ENABLE)
+ if (s_BoardInfos[dev->minor].i_InterruptFlag == ADDIDATA_ENABLE) {
+ /**********************/
+ /*Enable the interrupt */
+ /**********************/
+ ui_CommandRegister = ui_CommandRegister | 0x00100000;
+ }
+ /**********************/
+ /*Start the conversion */
+ /**********************/
+ ui_CommandRegister = ui_CommandRegister | 0x00080000;
+ /***************************/
+ /*Write the command regiter */
+ /***************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(ui_CommandRegister ,devpriv->iobase+i_Offset + 8);
+ outl(ui_CommandRegister,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 8);
+ //if (i_InterruptFlag == ADDIDATA_DISABLE)
+ if (s_BoardInfos[dev->minor].i_InterruptFlag == ADDIDATA_DISABLE) {
+ do {
+ /*******************/
+ /*Read the EOC flag */
+ /*******************/
+ //ui_EOC = inl(devpriv->iobase+i_Offset + 20) & 1;
+ ui_EOC = inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 20) & 1;
+ } while (ui_EOC != 1);
+ /************************************************/
+ /*Read the digital value of the calibration Gain */
+ /************************************************/
+ //data[0] = inl (devpriv->iobase+i_Offset + 28);
+ data[0] =
+ inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 28);
+ } //if (i_InterruptFlag == ADDIDATA_DISABLE)
+ return 0;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI3200_InsnBits_AnalogInput_Test |
+ | (struct comedi_device *dev,struct comedi_subdevice *s, |
+ | struct comedi_insn *insn,unsigned int *data) |
+ +----------------------------------------------------------------------------+
+ | Task : Tests the Selected Anlog Input Channel |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | struct comedi_subdevice *s : Subdevice Pointer |
+ | struct comedi_insn *insn : Insn Structure Pointer |
+ | unsigned int *data : Data Pointer contains |
+ | configuration parameters as below |
+ |
+ |
+ | data[0] : 0 TestAnalogInputShortCircuit
+ | 1 TestAnalogInputConnection |
+
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ | data[0] : Digital value obtained |
+ | data[1] : calibration offset |
+ | data[2] : calibration gain |
+ | |
+ | |
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+
+INT i_APCI3200_InsnBits_AnalogInput_Test(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Configuration = 0;
+ INT i_Temp; //,i_TimeUnit;
+ //if(i_Initialised==0)
+
+ if (s_BoardInfos[dev->minor].i_Initialised == 0) {
+ i_APCI3200_Reset(dev);
+ return -EINVAL;
+ } //if(i_Initialised==0);
+ if (data[0] != 0 && data[0] != 1) {
+ printk("\nError in selection of functionality\n");
+ i_APCI3200_Reset(dev);
+ return -EINVAL;
+ } //if(data[0]!=0 && data[0]!=1)
+
+ if (data[0] == 1) //Perform Short Circuit TEST
+ {
+ /**************************/
+ /*Set the short-cicuit bit */
+ /**************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].
+ i_Offset + 12) >> 19) & 1) !=
+ 1) ;
+ //outl((0x00001000 |i_ChannelNo) , devpriv->iobase+i_Offset + 4);
+ outl((0x00001000 | s_BoardInfos[dev->minor].i_ChannelNo),
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 4);
+ /*************************/
+ /*Set the time unit to ns */
+ /*************************/
+ /* i_TimeUnit= i_ADDIDATAConversionTimeUnit;
+ i_ADDIDATAConversionTimeUnit= 1; */
+ //i_Temp= i_InterruptFlag ;
+ i_Temp = s_BoardInfos[dev->minor].i_InterruptFlag;
+ //i_InterruptFlag = ADDIDATA_DISABLE;
+ s_BoardInfos[dev->minor].i_InterruptFlag = ADDIDATA_DISABLE;
+ i_APCI3200_Read1AnalogInputChannel(dev, s, insn, data);
+ //if(i_AutoCalibration == FALSE)
+ if (s_BoardInfos[dev->minor].i_AutoCalibration == FALSE) {
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].
+ i_Offset +
+ 12) >> 19) & 1) != 1) ;
+
+ //outl((0x00001000 |i_ChannelNo) , devpriv->iobase+i_Offset + 4);
+ outl((0x00001000 | s_BoardInfos[dev->minor].
+ i_ChannelNo),
+ devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 4);
+ data++;
+ i_APCI3200_ReadCalibrationOffsetValue(dev, data);
+ data++;
+ i_APCI3200_ReadCalibrationGainValue(dev, data);
+ }
+ } else {
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].
+ i_Offset + 12) >> 19) & 1) !=
+ 1) ;
+ //outl((0x00000800|i_ChannelNo) , devpriv->iobase+i_Offset + 4);
+ outl((0x00000800 | s_BoardInfos[dev->minor].i_ChannelNo),
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 4);
+ //ui_Configuration = inl(devpriv->iobase+i_Offset + 0);
+ ui_Configuration =
+ inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 0);
+ /*************************/
+ /*Set the time unit to ns */
+ /*************************/
+ /* i_TimeUnit= i_ADDIDATAConversionTimeUnit;
+ i_ADDIDATAConversionTimeUnit= 1; */
+ //i_Temp= i_InterruptFlag ;
+ i_Temp = s_BoardInfos[dev->minor].i_InterruptFlag;
+ //i_InterruptFlag = ADDIDATA_DISABLE;
+ s_BoardInfos[dev->minor].i_InterruptFlag = ADDIDATA_DISABLE;
+ i_APCI3200_Read1AnalogInputChannel(dev, s, insn, data);
+ //if(i_AutoCalibration == FALSE)
+ if (s_BoardInfos[dev->minor].i_AutoCalibration == FALSE) {
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].
+ i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl((0x00000800|i_ChannelNo) , devpriv->iobase+i_Offset + 4);
+ outl((0x00000800 | s_BoardInfos[dev->minor].
+ i_ChannelNo),
+ devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 4);
+ data++;
+ i_APCI3200_ReadCalibrationOffsetValue(dev, data);
+ data++;
+ i_APCI3200_ReadCalibrationGainValue(dev, data);
+ }
+ }
+ //i_InterruptFlag=i_Temp ;
+ s_BoardInfos[dev->minor].i_InterruptFlag = i_Temp;
+ //printk("\ni_InterruptFlag=%d\n",i_InterruptFlag);
+ return insn->n;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI3200_InsnWriteReleaseAnalogInput |
+ | (struct comedi_device *dev,struct comedi_subdevice *s, |
+ | struct comedi_insn *insn,unsigned int *data) |
+ +----------------------------------------------------------------------------+
+ | Task : Resets the channels |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev : Driver handle |
+ | struct comedi_subdevice *s : Subdevice Pointer |
+ | struct comedi_insn *insn : Insn Structure Pointer |
+ | unsigned int *data : Data Pointer
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+
+INT i_APCI3200_InsnWriteReleaseAnalogInput(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ i_APCI3200_Reset(dev);
+ return insn->n;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function name :int i_APCI3200_CommandTestAnalogInput(struct comedi_device *dev|
+ | ,struct comedi_subdevice *s,struct comedi_cmd *cmd) |
+ | |
+ +----------------------------------------------------------------------------+
+ | Task : Test validity for a command for cyclic anlog input |
+ | acquisition |
+ | |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev |
+ | struct comedi_subdevice *s |
+ | struct comedi_cmd *cmd |
+ | |
+ |
+ | |
+ | |
+ | |
+ +----------------------------------------------------------------------------+
+ | Return Value :0 |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+
+int i_APCI3200_CommandTestAnalogInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+
+ int err = 0;
+ int tmp; // divisor1,divisor2;
+ UINT ui_ConvertTime = 0;
+ UINT ui_ConvertTimeBase = 0;
+ UINT ui_DelayTime = 0;
+ UINT ui_DelayTimeBase = 0;
+ INT i_Triggermode = 0;
+ INT i_TriggerEdge = 0;
+ INT i_NbrOfChannel = 0;
+ INT i_Cpt = 0;
+ double d_ConversionTimeForAllChannels = 0.0;
+ double d_SCANTimeNewUnit = 0.0;
+ // step 1: make sure trigger sources are trivially valid
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_EXT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+ //if(i_InterruptFlag==0)
+ if (s_BoardInfos[dev->minor].i_InterruptFlag == 0) {
+ err++;
+ // printk("\nThe interrupt should be enabled\n");
+ }
+ if (err) {
+ i_APCI3200_Reset(dev);
+ return 1;
+ }
+
+ if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT) {
+ err++;
+ }
+ if (cmd->start_src == TRIG_EXT) {
+ i_TriggerEdge = cmd->start_arg & 0xFFFF;
+ i_Triggermode = cmd->start_arg >> 16;
+ if (i_TriggerEdge < 1 || i_TriggerEdge > 3) {
+ err++;
+ printk("\nThe trigger edge selection is in error\n");
+ }
+ if (i_Triggermode != 2) {
+ err++;
+ printk("\nThe trigger mode selection is in error\n");
+ }
+ }
+
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_FOLLOW)
+ err++;
+
+ if (cmd->convert_src != TRIG_TIMER)
+ err++;
+
+ if (cmd->scan_end_src != TRIG_COUNT) {
+ cmd->scan_end_src = TRIG_COUNT;
+ err++;
+ }
+
+ if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
+ err++;
+
+ if (err) {
+ i_APCI3200_Reset(dev);
+ return 2;
+ }
+ //i_FirstChannel=cmd->chanlist[0];
+ s_BoardInfos[dev->minor].i_FirstChannel = cmd->chanlist[0];
+ //i_LastChannel=cmd->chanlist[1];
+ s_BoardInfos[dev->minor].i_LastChannel = cmd->chanlist[1];
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ ui_ConvertTime = cmd->convert_arg & 0xFFFF;
+ ui_ConvertTimeBase = cmd->convert_arg >> 16;
+ if (ui_ConvertTime != 20 && ui_ConvertTime != 40
+ && ui_ConvertTime != 80 && ui_ConvertTime != 160)
+ {
+ printk("\nThe selection of conversion time reload value is in error\n");
+ err++;
+ } // if (ui_ConvertTime!=20 && ui_ConvertTime!=40 && ui_ConvertTime!=80 && ui_ConvertTime!=160 )
+ if (ui_ConvertTimeBase != 2) {
+ printk("\nThe selection of conversion time unit is in error\n");
+ err++;
+ } //if(ui_ConvertTimeBase!=2)
+ } else {
+ ui_ConvertTime = 0;
+ ui_ConvertTimeBase = 0;
+ }
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ ui_DelayTime = 0;
+ ui_DelayTimeBase = 0;
+ } //if(cmd->scan_begin_src==TRIG_FOLLOW)
+ else {
+ ui_DelayTime = cmd->scan_begin_arg & 0xFFFF;
+ ui_DelayTimeBase = cmd->scan_begin_arg >> 16;
+ if (ui_DelayTimeBase != 2 && ui_DelayTimeBase != 3) {
+ err++;
+ printk("\nThe Delay time base selection is in error\n");
+ }
+ if (ui_DelayTime < 1 && ui_DelayTime > 1023) {
+ err++;
+ printk("\nThe Delay time value is in error\n");
+ }
+ if (err) {
+ i_APCI3200_Reset(dev);
+ return 3;
+ }
+ fpu_begin();
+ d_SCANTimeNewUnit = (double)ui_DelayTime;
+ //i_NbrOfChannel= i_LastChannel-i_FirstChannel + 4;
+ i_NbrOfChannel =
+ s_BoardInfos[dev->minor].i_LastChannel -
+ s_BoardInfos[dev->minor].i_FirstChannel + 4;
+ /**********************************************************/
+ /*calculate the total conversion time for all the channels */
+ /**********************************************************/
+ d_ConversionTimeForAllChannels =
+ (double)((double)ui_ConvertTime /
+ (double)i_NbrOfChannel);
+
+ /*******************************/
+ /*Convert the frequence in time */
+ /*******************************/
+ d_ConversionTimeForAllChannels =
+ (double)1.0 / d_ConversionTimeForAllChannels;
+ ui_ConvertTimeBase = 3;
+ /***********************************/
+ /*Test if the time unit is the same */
+ /***********************************/
+
+ if (ui_DelayTimeBase <= ui_ConvertTimeBase) {
+
+ for (i_Cpt = 0;
+ i_Cpt < (ui_ConvertTimeBase - ui_DelayTimeBase);
+ i_Cpt++) {
+
+ d_ConversionTimeForAllChannels =
+ d_ConversionTimeForAllChannels * 1000;
+ d_ConversionTimeForAllChannels =
+ d_ConversionTimeForAllChannels + 1;
+ }
+ } else {
+ for (i_Cpt = 0;
+ i_Cpt < (ui_DelayTimeBase - ui_ConvertTimeBase);
+ i_Cpt++) {
+ d_SCANTimeNewUnit = d_SCANTimeNewUnit * 1000;
+
+ }
+ }
+
+ if (d_ConversionTimeForAllChannels >= d_SCANTimeNewUnit) {
+
+ printk("\nSCAN Delay value cannot be used\n");
+ /*********************************/
+ /*SCAN Delay value cannot be used */
+ /*********************************/
+ err++;
+ }
+ fpu_end();
+ } //else if(cmd->scan_begin_src==TRIG_FOLLOW)
+
+ if (err) {
+ i_APCI3200_Reset(dev);
+ return 4;
+ }
+
+ return 0;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function name :int i_APCI3200_StopCyclicAcquisition(struct comedi_device *dev,|
+ | struct comedi_subdevice *s)|
+ | |
+ +----------------------------------------------------------------------------+
+ | Task : Stop the acquisition |
+ | |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev |
+ | struct comedi_subdevice *s |
+ | |
+ +----------------------------------------------------------------------------+
+ | Return Value :0 |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+
+int i_APCI3200_StopCyclicAcquisition(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ UINT ui_Configuration = 0;
+ //i_InterruptFlag=0;
+ //i_Initialised=0;
+ //i_Count=0;
+ //i_Sum=0;
+ s_BoardInfos[dev->minor].i_InterruptFlag = 0;
+ s_BoardInfos[dev->minor].i_Initialised = 0;
+ s_BoardInfos[dev->minor].i_Count = 0;
+ s_BoardInfos[dev->minor].i_Sum = 0;
+
+ /*******************/
+ /*Read the register */
+ /*******************/
+ //ui_Configuration = inl(devpriv->iobase+i_Offset + 8);
+ ui_Configuration =
+ inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 8);
+ /*****************************/
+ /*Reset the START and IRQ bit */
+ /*****************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl((ui_Configuration & 0xFFE7FFFF),devpriv->iobase+i_Offset + 8);
+ outl((ui_Configuration & 0xFFE7FFFF),
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 8);
+ return 0;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function name : int i_APCI3200_CommandAnalogInput(struct comedi_device *dev, |
+ | struct comedi_subdevice *s) |
+ | |
+ +----------------------------------------------------------------------------+
+ | Task : Does asynchronous acquisition |
+ | Determines the mode 1 or 2. |
+ | |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev |
+ | struct comedi_subdevice *s |
+ | |
+ | |
+ +----------------------------------------------------------------------------+
+ | Return Value : |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+
+int i_APCI3200_CommandAnalogInput(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ UINT ui_Configuration = 0;
+ //INT i_CurrentSource = 0;
+ UINT ui_Trigger = 0;
+ UINT ui_TriggerEdge = 0;
+ UINT ui_Triggermode = 0;
+ UINT ui_ScanMode = 0;
+ UINT ui_ConvertTime = 0;
+ UINT ui_ConvertTimeBase = 0;
+ UINT ui_DelayTime = 0;
+ UINT ui_DelayTimeBase = 0;
+ UINT ui_DelayMode = 0;
+ //i_FirstChannel=cmd->chanlist[0];
+ //i_LastChannel=cmd->chanlist[1];
+ s_BoardInfos[dev->minor].i_FirstChannel = cmd->chanlist[0];
+ s_BoardInfos[dev->minor].i_LastChannel = cmd->chanlist[1];
+ if (cmd->start_src == TRIG_EXT) {
+ ui_Trigger = 1;
+ ui_TriggerEdge = cmd->start_arg & 0xFFFF;
+ ui_Triggermode = cmd->start_arg >> 16;
+ } //if(cmd->start_src==TRIG_EXT)
+ else {
+ ui_Trigger = 0;
+ } //elseif(cmd->start_src==TRIG_EXT)
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ ui_ScanMode = 0;
+ } // if (cmd->stop_src==TRIG_COUNT)
+ else {
+ ui_ScanMode = 2;
+ } //else if (cmd->stop_src==TRIG_COUNT)
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ ui_DelayTime = 0;
+ ui_DelayTimeBase = 0;
+ ui_DelayMode = 0;
+ } //if(cmd->scan_begin_src==TRIG_FOLLOW)
+ else {
+ ui_DelayTime = cmd->scan_begin_arg & 0xFFFF;
+ ui_DelayTimeBase = cmd->scan_begin_arg >> 16;
+ ui_DelayMode = 1;
+ } //else if(cmd->scan_begin_src==TRIG_FOLLOW)
+ // printk("\nui_DelayTime=%u\n",ui_DelayTime);
+ // printk("\nui_DelayTimeBase=%u\n",ui_DelayTimeBase);
+ if (cmd->convert_src == TRIG_TIMER) {
+ ui_ConvertTime = cmd->convert_arg & 0xFFFF;
+ ui_ConvertTimeBase = cmd->convert_arg >> 16;
+ } else {
+ ui_ConvertTime = 0;
+ ui_ConvertTimeBase = 0;
+ }
+
+ // if(i_ADDIDATAType ==1 || ((i_ADDIDATAType==2)))
+ // {
+ /**************************************************/
+ /*Read the old configuration of the current source */
+ /**************************************************/
+ //ui_Configuration = inl(devpriv->iobase+i_Offset + 12);
+ ui_Configuration =
+ inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 12);
+ /***********************************************/
+ /*Write the configuration of the current source */
+ /***********************************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl((ui_Configuration & 0xFFC00000 ), devpriv->iobase+i_Offset +12);
+ outl((ui_Configuration & 0xFFC00000),
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 12);
+ // }
+ ui_Configuration = 0;
+ // printk("\nfirstchannel=%u\n",i_FirstChannel);
+ // printk("\nlastchannel=%u\n",i_LastChannel);
+ // printk("\nui_Trigger=%u\n",ui_Trigger);
+ // printk("\nui_TriggerEdge=%u\n",ui_TriggerEdge);
+ // printk("\nui_Triggermode=%u\n",ui_Triggermode);
+ // printk("\nui_DelayMode=%u\n",ui_DelayMode);
+ // printk("\nui_ScanMode=%u\n",ui_ScanMode);
+
+ //ui_Configuration = i_FirstChannel |(i_LastChannel << 8)| 0x00100000 |
+ ui_Configuration =
+ s_BoardInfos[dev->minor].i_FirstChannel | (s_BoardInfos[dev->
+ minor].
+ i_LastChannel << 8) | 0x00100000 | (ui_Trigger << 24) |
+ (ui_TriggerEdge << 25) | (ui_Triggermode << 27) | (ui_DelayMode
+ << 18) | (ui_ScanMode << 16);
+
+ /*************************/
+ /*Write the Configuration */
+ /*************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl( ui_Configuration, devpriv->iobase+i_Offset + 0x8);
+ outl(ui_Configuration,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 0x8);
+ /***********************/
+ /*Write the Delay Value */
+ /***********************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(ui_DelayTime,devpriv->iobase+i_Offset + 40);
+ outl(ui_DelayTime,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 40);
+ /***************************/
+ /*Write the Delay time base */
+ /***************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(ui_DelayTimeBase,devpriv->iobase+i_Offset + 44);
+ outl(ui_DelayTimeBase,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 44);
+ /*********************************/
+ /*Write the conversion time value */
+ /*********************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(ui_ConvertTime,devpriv->iobase+i_Offset + 32);
+ outl(ui_ConvertTime,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 32);
+
+ /********************************/
+ /*Write the conversion time base */
+ /********************************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl(ui_ConvertTimeBase,devpriv->iobase+i_Offset + 36);
+ outl(ui_ConvertTimeBase,
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 36);
+ /*******************/
+ /*Read the register */
+ /*******************/
+ //ui_Configuration = inl(devpriv->iobase+i_Offset + 4);
+ ui_Configuration =
+ inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 4);
+ /******************/
+ /*Set the SCAN bit */
+ /******************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+
+ //outl(((ui_Configuration & 0x1E0FF) | 0x00002000),devpriv->iobase+i_Offset + 4);
+ outl(((ui_Configuration & 0x1E0FF) | 0x00002000),
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 4);
+ /*******************/
+ /*Read the register */
+ /*******************/
+ ui_Configuration = 0;
+ //ui_Configuration = inl(devpriv->iobase+i_Offset + 8);
+ ui_Configuration =
+ inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 8);
+
+ /*******************/
+ /*Set the START bit */
+ /*******************/
+ //while (((inl(devpriv->iobase+i_Offset+12)>>19) & 1) != 1);
+ while (((inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset +
+ 12) >> 19) & 1) != 1) ;
+ //outl((ui_Configuration | 0x00080000),devpriv->iobase+i_Offset + 8);
+ outl((ui_Configuration | 0x00080000),
+ devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 8);
+ return 0;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : int i_APCI3200_Reset(struct comedi_device *dev) |
+ | |
+ +----------------------------------------------------------------------------+
+ | Task :Resets the registers of the card |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ +----------------------------------------------------------------------------+
+ | Return Value : |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+
+int i_APCI3200_Reset(struct comedi_device * dev)
+{
+ INT i_Temp;
+ DWORD dw_Dummy;
+ //i_InterruptFlag=0;
+ //i_Initialised==0;
+ //i_Count=0;
+ //i_Sum=0;
+
+ s_BoardInfos[dev->minor].i_InterruptFlag = 0;
+ s_BoardInfos[dev->minor].i_Initialised = 0;
+ s_BoardInfos[dev->minor].i_Count = 0;
+ s_BoardInfos[dev->minor].i_Sum = 0;
+ s_BoardInfos[dev->minor].b_StructInitialized = 0;
+
+ outl(0x83838383, devpriv->i_IobaseAmcc + 0x60);
+
+ // Enable the interrupt for the controler
+ dw_Dummy = inl(devpriv->i_IobaseAmcc + 0x38);
+ outl(dw_Dummy | 0x2000, devpriv->i_IobaseAmcc + 0x38);
+ outl(0, devpriv->i_IobaseAddon); //Resets the output
+ /***************/
+ /*Empty the buffer */
+ /**************/
+ for (i_Temp = 0; i_Temp <= 95; i_Temp++) {
+ //ui_InterruptChannelValue[i_Temp]=0;
+ s_BoardInfos[dev->minor].ui_InterruptChannelValue[i_Temp] = 0;
+ } //for(i_Temp=0;i_Temp<=95;i_Temp++)
+ /*****************************/
+ /*Reset the START and IRQ bit */
+ /*****************************/
+ for (i_Temp = 0; i_Temp <= 192;) {
+ while (((inl(devpriv->iobase + i_Temp + 12) >> 19) & 1) != 1) ;
+ outl(0, devpriv->iobase + i_Temp + 8);
+ i_Temp = i_Temp + 64;
+ } //for(i_Temp=0;i_Temp<=192;i_Temp+64)
+ return 0;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function Name : static void v_APCI3200_Interrupt |
+ | (int irq , void *d) |
+ +----------------------------------------------------------------------------+
+ | Task : Interrupt processing Routine |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : int irq : irq number |
+ | void *d : void pointer |
+ +----------------------------------------------------------------------------+
+ | Output Parameters : -- |
+ +----------------------------------------------------------------------------+
+ | Return Value : TRUE : No error occur |
+ | : FALSE : Error occur. Return the error |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+void v_APCI3200_Interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ UINT ui_StatusRegister = 0;
+ UINT ui_ChannelNumber = 0;
+ INT i_CalibrationFlag = 0;
+ INT i_CJCFlag = 0;
+ UINT ui_DummyValue = 0;
+ UINT ui_DigitalTemperature = 0;
+ UINT ui_DigitalInput = 0;
+ int i_ConvertCJCCalibration;
+
+ //BEGIN JK TEST
+ int i_ReturnValue = 0;
+ //END JK TEST
+
+ //printk ("\n i_ScanType = %i i_ADDIDATAType = %i", s_BoardInfos [dev->minor].i_ScanType, s_BoardInfos [dev->minor].i_ADDIDATAType);
+
+ //switch(i_ScanType)
+ switch (s_BoardInfos[dev->minor].i_ScanType) {
+ case 0:
+ case 1:
+ //switch(i_ADDIDATAType)
+ switch (s_BoardInfos[dev->minor].i_ADDIDATAType) {
+ case 0:
+ case 1:
+
+ /************************************/
+ /*Read the interrupt status register */
+ /************************************/
+ //ui_StatusRegister = inl(devpriv->iobase+i_Offset + 16);
+ ui_StatusRegister =
+ inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 16);
+ if ((ui_StatusRegister & 0x2) == 0x2) {
+ //i_CalibrationFlag = ((inl(devpriv->iobase+i_Offset + 12) & 0x00060000) >> 17);
+ i_CalibrationFlag =
+ ((inl(devpriv->iobase +
+ s_BoardInfos[dev->
+ minor].
+ i_Offset +
+ 12) & 0x00060000) >>
+ 17);
+ /*************************/
+ /*Read the channel number */
+ /*************************/
+ //ui_ChannelNumber = inl(devpriv->iobase+i_Offset + 24);
+
+ /*************************************/
+ /*Read the digital analog input value */
+ /*************************************/
+ //ui_DigitalInput = inl(devpriv->iobase+i_Offset + 28);
+ ui_DigitalInput =
+ inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 28);
+
+ /***********************************************/
+ /* Test if the value read is the channel value */
+ /***********************************************/
+ if (i_CalibrationFlag == 0) {
+ //ui_InterruptChannelValue[i_Count + 0] = ui_DigitalInput;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue
+ [s_BoardInfos[dev->minor].
+ i_Count + 0] = ui_DigitalInput;
+
+ //Begin JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ /*
+ printk("\n 1 - i_APCI3200_GetChannelCalibrationValue (dev, s_BoardInfos %i", ui_ChannelNumber);
+ i_APCI3200_GetChannelCalibrationValue (dev, s_BoardInfos [dev->minor].ui_Channel_num,
+ &s_BoardInfos [dev->minor].ui_InterruptChannelValue[s_BoardInfos [dev->minor].i_Count + 6],
+ &s_BoardInfos [dev->minor].ui_InterruptChannelValue[s_BoardInfos [dev->minor].i_Count + 7],
+ &s_BoardInfos [dev->minor].ui_InterruptChannelValue[s_BoardInfos [dev->minor].i_Count + 8]);
+ */
+ //End JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+
+ /******************************************************/
+ /*Start the conversion of the calibration offset value */
+ /******************************************************/
+ i_APCI3200_ReadCalibrationOffsetValue
+ (dev, &ui_DummyValue);
+ } //if (i_CalibrationFlag == 0)
+ /**********************************************************/
+ /* Test if the value read is the calibration offset value */
+ /**********************************************************/
+
+ if (i_CalibrationFlag == 1) {
+
+ /******************/
+ /* Save the value */
+ /******************/
+
+ //ui_InterruptChannelValue[i_Count + 1] = ui_DigitalInput;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue
+ [s_BoardInfos[dev->minor].
+ i_Count + 1] = ui_DigitalInput;
+
+ /******************************************************/
+ /* Start the conversion of the calibration gain value */
+ /******************************************************/
+ i_APCI3200_ReadCalibrationGainValue(dev,
+ &ui_DummyValue);
+ } //if (i_CalibrationFlag == 1)
+ /******************************************************/
+ /*Test if the value read is the calibration gain value */
+ /******************************************************/
+
+ if (i_CalibrationFlag == 2) {
+
+ /****************/
+ /*Save the value */
+ /****************/
+ //ui_InterruptChannelValue[i_Count + 2] = ui_DigitalInput;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue
+ [s_BoardInfos[dev->minor].
+ i_Count + 2] = ui_DigitalInput;
+ //if(i_ScanType==1)
+ if (s_BoardInfos[dev->minor].
+ i_ScanType == 1) {
+
+ //i_InterruptFlag=0;
+ s_BoardInfos[dev->minor].
+ i_InterruptFlag = 0;
+ //i_Count=i_Count + 6;
+ //Begin JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ //s_BoardInfos [dev->minor].i_Count=s_BoardInfos [dev->minor].i_Count + 6;
+ s_BoardInfos[dev->minor].
+ i_Count =
+ s_BoardInfos[dev->
+ minor].i_Count + 9;
+ //End JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ } //if(i_ScanType==1)
+ else {
+ //i_Count=0;
+ s_BoardInfos[dev->minor].
+ i_Count = 0;
+ } //elseif(i_ScanType==1)
+ //if(i_ScanType!=1)
+ if (s_BoardInfos[dev->minor].
+ i_ScanType != 1) {
+ i_ReturnValue = send_sig(SIGIO, devpriv->tsk_Current, 0); // send signal to the sample
+ } //if(i_ScanType!=1)
+ else {
+ //if(i_ChannelCount==i_Sum)
+ if (s_BoardInfos[dev->minor].
+ i_ChannelCount ==
+ s_BoardInfos[dev->
+ minor].i_Sum) {
+ send_sig(SIGIO, devpriv->tsk_Current, 0); // send signal to the sample
+ }
+ } //if(i_ScanType!=1)
+ } //if (i_CalibrationFlag == 2)
+ } // if ((ui_StatusRegister & 0x2) == 0x2)
+
+ break;
+
+ case 2:
+ /************************************/
+ /*Read the interrupt status register */
+ /************************************/
+
+ //ui_StatusRegister = inl(devpriv->iobase+i_Offset + 16);
+ ui_StatusRegister =
+ inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 16);
+ /*************************/
+ /*Test if interrupt occur */
+ /*************************/
+
+ if ((ui_StatusRegister & 0x2) == 0x2) {
+
+ //i_CJCFlag = ((inl(devpriv->iobase+i_Offset + 4) & 0x00000400) >> 10);
+ i_CJCFlag =
+ ((inl(devpriv->iobase +
+ s_BoardInfos[dev->
+ minor].
+ i_Offset +
+ 4) & 0x00000400) >> 10);
+
+ //i_CalibrationFlag = ((inl(devpriv->iobase+i_Offset + 12) & 0x00060000) >> 17);
+ i_CalibrationFlag =
+ ((inl(devpriv->iobase +
+ s_BoardInfos[dev->
+ minor].
+ i_Offset +
+ 12) & 0x00060000) >>
+ 17);
+
+ /*************************/
+ /*Read the channel number */
+ /*************************/
+
+ //ui_ChannelNumber = inl(devpriv->iobase+i_Offset + 24);
+ ui_ChannelNumber =
+ inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 24);
+ //Begin JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ s_BoardInfos[dev->minor].ui_Channel_num =
+ ui_ChannelNumber;
+ //End JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+
+ /************************************/
+ /*Read the digital temperature value */
+ /************************************/
+ //ui_DigitalTemperature = inl(devpriv->iobase+i_Offset + 28);
+ ui_DigitalTemperature =
+ inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 28);
+
+ /*********************************************/
+ /*Test if the value read is the channel value */
+ /*********************************************/
+
+ if ((i_CalibrationFlag == 0)
+ && (i_CJCFlag == 0)) {
+ //ui_InterruptChannelValue[i_Count + 0]=ui_DigitalTemperature;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue
+ [s_BoardInfos[dev->minor].
+ i_Count + 0] =
+ ui_DigitalTemperature;
+
+ /*********************************/
+ /*Start the conversion of the CJC */
+ /*********************************/
+ i_APCI3200_ReadCJCValue(dev,
+ &ui_DummyValue);
+
+ } //if ((i_CalibrationFlag == 0) && (i_CJCFlag == 0))
+
+ /*****************************************/
+ /*Test if the value read is the CJC value */
+ /*****************************************/
+
+ if ((i_CJCFlag == 1)
+ && (i_CalibrationFlag == 0)) {
+ //ui_InterruptChannelValue[i_Count + 3]=ui_DigitalTemperature;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue
+ [s_BoardInfos[dev->minor].
+ i_Count + 3] =
+ ui_DigitalTemperature;
+
+ /******************************************************/
+ /*Start the conversion of the calibration offset value */
+ /******************************************************/
+ i_APCI3200_ReadCalibrationOffsetValue
+ (dev, &ui_DummyValue);
+ } // if ((i_CJCFlag == 1) && (i_CalibrationFlag == 0))
+
+ /********************************************************/
+ /*Test if the value read is the calibration offset value */
+ /********************************************************/
+
+ if ((i_CalibrationFlag == 1)
+ && (i_CJCFlag == 0)) {
+ //ui_InterruptChannelValue[i_Count + 1]=ui_DigitalTemperature;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue
+ [s_BoardInfos[dev->minor].
+ i_Count + 1] =
+ ui_DigitalTemperature;
+
+ /****************************************************/
+ /*Start the conversion of the calibration gain value */
+ /****************************************************/
+ i_APCI3200_ReadCalibrationGainValue(dev,
+ &ui_DummyValue);
+
+ } //if ((i_CalibrationFlag == 1) && (i_CJCFlag == 0))
+
+ /******************************************************/
+ /*Test if the value read is the calibration gain value */
+ /******************************************************/
+
+ if ((i_CalibrationFlag == 2)
+ && (i_CJCFlag == 0)) {
+ //ui_InterruptChannelValue[i_Count + 2]=ui_DigitalTemperature;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue
+ [s_BoardInfos[dev->minor].
+ i_Count + 2] =
+ ui_DigitalTemperature;
+
+ /**********************************************************/
+ /*Test if the Calibration channel must be read for the CJC */
+ /**********************************************************/
+
+ /*Test if the polarity is the same */
+ /**********************************/
+ //if(i_CJCPolarity!=i_ADDIDATAPolarity)
+ if (s_BoardInfos[dev->minor].
+ i_CJCPolarity !=
+ s_BoardInfos[dev->minor].
+ i_ADDIDATAPolarity) {
+ i_ConvertCJCCalibration = 1;
+ } //if(i_CJCPolarity!=i_ADDIDATAPolarity)
+ else {
+ //if(i_CJCGain==i_ADDIDATAGain)
+ if (s_BoardInfos[dev->minor].
+ i_CJCGain ==
+ s_BoardInfos[dev->
+ minor].
+ i_ADDIDATAGain) {
+ i_ConvertCJCCalibration
+ = 0;
+ } //if(i_CJCGain==i_ADDIDATAGain)
+ else {
+ i_ConvertCJCCalibration
+ = 1;
+ } //elseif(i_CJCGain==i_ADDIDATAGain)
+ } //elseif(i_CJCPolarity!=i_ADDIDATAPolarity)
+ if (i_ConvertCJCCalibration == 1) {
+ /****************************************************************/
+ /*Start the conversion of the calibration gain value for the CJC */
+ /****************************************************************/
+ i_APCI3200_ReadCJCCalOffset(dev,
+ &ui_DummyValue);
+
+ } //if(i_ConvertCJCCalibration==1)
+ else {
+ //ui_InterruptChannelValue[i_Count + 4]=0;
+ //ui_InterruptChannelValue[i_Count + 5]=0;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue
+ [s_BoardInfos[dev->
+ minor].i_Count +
+ 4] = 0;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue
+ [s_BoardInfos[dev->
+ minor].i_Count +
+ 5] = 0;
+ } //elseif(i_ConvertCJCCalibration==1)
+ } //else if ((i_CalibrationFlag == 2) && (i_CJCFlag == 0))
+
+ /********************************************************************/
+ /*Test if the value read is the calibration offset value for the CJC */
+ /********************************************************************/
+
+ if ((i_CalibrationFlag == 1)
+ && (i_CJCFlag == 1)) {
+ //ui_InterruptChannelValue[i_Count + 4]=ui_DigitalTemperature;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue
+ [s_BoardInfos[dev->minor].
+ i_Count + 4] =
+ ui_DigitalTemperature;
+
+ /****************************************************************/
+ /*Start the conversion of the calibration gain value for the CJC */
+ /****************************************************************/
+ i_APCI3200_ReadCJCCalGain(dev,
+ &ui_DummyValue);
+
+ } //if ((i_CalibrationFlag == 1) && (i_CJCFlag == 1))
+
+ /******************************************************************/
+ /*Test if the value read is the calibration gain value for the CJC */
+ /******************************************************************/
+
+ if ((i_CalibrationFlag == 2)
+ && (i_CJCFlag == 1)) {
+ //ui_InterruptChannelValue[i_Count + 5]=ui_DigitalTemperature;
+ s_BoardInfos[dev->minor].
+ ui_InterruptChannelValue
+ [s_BoardInfos[dev->minor].
+ i_Count + 5] =
+ ui_DigitalTemperature;
+
+ //if(i_ScanType==1)
+ if (s_BoardInfos[dev->minor].
+ i_ScanType == 1) {
+
+ //i_InterruptFlag=0;
+ s_BoardInfos[dev->minor].
+ i_InterruptFlag = 0;
+ //i_Count=i_Count + 6;
+ //Begin JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ //s_BoardInfos [dev->minor].i_Count=s_BoardInfos [dev->minor].i_Count + 6;
+ s_BoardInfos[dev->minor].
+ i_Count =
+ s_BoardInfos[dev->
+ minor].i_Count + 9;
+ //End JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ } //if(i_ScanType==1)
+ else {
+ //i_Count=0;
+ s_BoardInfos[dev->minor].
+ i_Count = 0;
+ } //elseif(i_ScanType==1)
+
+ //if(i_ScanType!=1)
+ if (s_BoardInfos[dev->minor].
+ i_ScanType != 1) {
+ send_sig(SIGIO, devpriv->tsk_Current, 0); // send signal to the sample
+ } //if(i_ScanType!=1)
+ else {
+ //if(i_ChannelCount==i_Sum)
+ if (s_BoardInfos[dev->minor].
+ i_ChannelCount ==
+ s_BoardInfos[dev->
+ minor].i_Sum) {
+ send_sig(SIGIO, devpriv->tsk_Current, 0); // send signal to the sample
+
+ } //if(i_ChannelCount==i_Sum)
+ } //else if(i_ScanType!=1)
+ } //if ((i_CalibrationFlag == 2) && (i_CJCFlag == 1))
+
+ } //else if ((ui_StatusRegister & 0x2) == 0x2)
+ break;
+ } //switch(i_ADDIDATAType)
+ break;
+ case 2:
+ case 3:
+ i_APCI3200_InterruptHandleEos(dev);
+ break;
+ } //switch(i_ScanType)
+ return;
+}
+
+/*
+ +----------------------------------------------------------------------------+
+ | Function name :int i_APCI3200_InterruptHandleEos(struct comedi_device *dev) |
+ | |
+ | |
+ +----------------------------------------------------------------------------+
+ | Task : . |
+ | This function copies the acquired data(from FIFO) |
+ | to Comedi buffer. |
+ | |
+ +----------------------------------------------------------------------------+
+ | Input Parameters : struct comedi_device *dev |
+ | |
+ | |
+ +----------------------------------------------------------------------------+
+ | Return Value : 0 |
+ | |
+ +----------------------------------------------------------------------------+
+*/
+int i_APCI3200_InterruptHandleEos(struct comedi_device * dev)
+{
+ UINT ui_StatusRegister = 0;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+
+ //BEGIN JK 18.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+ //comedi_async *async = s->async;
+ //UINT *data;
+ //data=async->data+async->buf_int_ptr;//new samples added from here onwards
+ int n = 0, i = 0;
+ //END JK 18.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+
+ /************************************/
+ /*Read the interrupt status register */
+ /************************************/
+ //ui_StatusRegister = inl(devpriv->iobase+i_Offset + 16);
+ ui_StatusRegister =
+ inl(devpriv->iobase + s_BoardInfos[dev->minor].i_Offset + 16);
+
+ /*************************/
+ /*Test if interrupt occur */
+ /*************************/
+
+ if ((ui_StatusRegister & 0x2) == 0x2) {
+ /*************************/
+ /*Read the channel number */
+ /*************************/
+ //ui_ChannelNumber = inl(devpriv->iobase+i_Offset + 24);
+ //BEGIN JK 18.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+ //This value is not used
+ //ui_ChannelNumber = inl(devpriv->iobase+s_BoardInfos [dev->minor].i_Offset + 24);
+ s->async->events = 0;
+ //END JK 18.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+
+ /*************************************/
+ /*Read the digital Analog Input value */
+ /*************************************/
+
+ //data[i_Count] = inl(devpriv->iobase+i_Offset + 28);
+ //Begin JK 18.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+ //data[s_BoardInfos [dev->minor].i_Count] = inl(devpriv->iobase+s_BoardInfos [dev->minor].i_Offset + 28);
+ s_BoardInfos[dev->minor].ui_ScanValueArray[s_BoardInfos[dev->
+ minor].i_Count] =
+ inl(devpriv->iobase +
+ s_BoardInfos[dev->minor].i_Offset + 28);
+ //End JK 18.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+
+ //if((i_Count == (i_LastChannel-i_FirstChannel+3)))
+ if ((s_BoardInfos[dev->minor].i_Count ==
+ (s_BoardInfos[dev->minor].i_LastChannel -
+ s_BoardInfos[dev->minor].
+ i_FirstChannel + 3))) {
+
+ //Begin JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ s_BoardInfos[dev->minor].i_Count++;
+
+ for (i = s_BoardInfos[dev->minor].i_FirstChannel;
+ i <= s_BoardInfos[dev->minor].i_LastChannel;
+ i++) {
+ i_APCI3200_GetChannelCalibrationValue(dev, i,
+ &s_BoardInfos[dev->minor].
+ ui_ScanValueArray[s_BoardInfos[dev->
+ minor].i_Count + ((i -
+ s_BoardInfos
+ [dev->minor].
+ i_FirstChannel)
+ * 3)],
+ &s_BoardInfos[dev->minor].
+ ui_ScanValueArray[s_BoardInfos[dev->
+ minor].i_Count + ((i -
+ s_BoardInfos
+ [dev->minor].
+ i_FirstChannel)
+ * 3) + 1],
+ &s_BoardInfos[dev->minor].
+ ui_ScanValueArray[s_BoardInfos[dev->
+ minor].i_Count + ((i -
+ s_BoardInfos
+ [dev->minor].
+ i_FirstChannel)
+ * 3) + 2]);
+ }
+
+ //End JK 22.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+
+ //i_Count=-1;
+
+ s_BoardInfos[dev->minor].i_Count = -1;
+
+ //async->buf_int_count+=(i_LastChannel-i_FirstChannel+4)*sizeof(UINT);
+ //Begin JK 18.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+ //async->buf_int_count+=(s_BoardInfos [dev->minor].i_LastChannel-s_BoardInfos [dev->minor].i_FirstChannel+4)*sizeof(UINT);
+ //End JK 18.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+ //async->buf_int_ptr+=(i_LastChannel-i_FirstChannel+4)*sizeof(UINT);
+ //Begin JK 18.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+ //async->buf_int_ptr+=(s_BoardInfos [dev->minor].i_LastChannel-s_BoardInfos [dev->minor].i_FirstChannel+4)*sizeof(UINT);
+ //comedi_eos(dev,s);
+
+ // Set the event type (Comedi Buffer End Of Scan)
+ s->async->events |= COMEDI_CB_EOS;
+
+ // Test if enougth memory is available and allocate it for 7 values
+ //n = comedi_buf_write_alloc(s->async, 7*sizeof(unsigned int));
+ n = comedi_buf_write_alloc(s->async,
+ (7 + 12) * sizeof(unsigned int));
+
+ // If not enougth memory available, event is set to Comedi Buffer Errror
+ if (n > ((7 + 12) * sizeof(unsigned int))) {
+ printk("\ncomedi_buf_write_alloc n = %i", n);
+ s->async->events |= COMEDI_CB_ERROR;
+ }
+ // Write all 7 scan values in the comedi buffer
+ comedi_buf_memcpy_to(s->async, 0,
+ (unsigned int *) s_BoardInfos[dev->minor].
+ ui_ScanValueArray, (7 + 12) * sizeof(unsigned int));
+
+ // Update comedi buffer pinters indexes
+ comedi_buf_write_free(s->async,
+ (7 + 12) * sizeof(unsigned int));
+
+ // Send events
+ comedi_event(dev, s);
+ //End JK 18.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+
+ //BEGIN JK 18.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+ //
+ //if (s->async->buf_int_ptr>=s->async->data_len) // for buffer rool over
+ // {
+ // /* buffer rollover */
+ // s->async->buf_int_ptr=0;
+ // comedi_eobuf(dev,s);
+ // }
+ //End JK 18.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+ }
+ //i_Count++;
+ s_BoardInfos[dev->minor].i_Count++;
+ }
+ //i_InterruptFlag=0;
+ s_BoardInfos[dev->minor].i_InterruptFlag = 0;
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3200.h b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3200.h
new file mode 100644
index 000000000000..a6f57f55e79c
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3200.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+// Card Specific information
+#define APCI3200_BOARD_VENDOR_ID 0x15B8
+//#define APCI3200_ADDRESS_RANGE 264
+
+int MODULE_NO;
+struct {
+ INT i_Gain;
+ INT i_Polarity;
+ INT i_OffsetRange;
+ INT i_Coupling;
+ INT i_SingleDiff;
+ INT i_AutoCalibration;
+ UINT ui_ReloadValue;
+ UINT ui_TimeUnitReloadVal;
+ INT i_Interrupt;
+ INT i_ModuleSelection;
+} Config_Parameters_Module1, Config_Parameters_Module2,
+ Config_Parameters_Module3, Config_Parameters_Module4;
+
+//ANALOG INPUT RANGE
+static const struct comedi_lrange range_apci3200_ai = { 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1)
+ }
+};
+
+static const struct comedi_lrange range_apci3300_ai = { 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1)
+ }
+};
+
+//Analog Input related Defines
+#define APCI3200_AI_OFFSET_GAIN 0
+#define APCI3200_AI_SC_TEST 4
+#define APCI3200_AI_IRQ 8
+#define APCI3200_AI_AUTOCAL 12
+#define APCI3200_RELOAD_CONV_TIME_VAL 32
+#define APCI3200_CONV_TIME_TIME_BASE 36
+#define APCI3200_RELOAD_DELAY_TIME_VAL 40
+#define APCI3200_DELAY_TIME_TIME_BASE 44
+#define APCI3200_AI_MODULE1 0
+#define APCI3200_AI_MODULE2 64
+#define APCI3200_AI_MODULE3 128
+#define APCI3200_AI_MODULE4 192
+#define TRUE 1
+#define FALSE 0
+#define APCI3200_AI_EOSIRQ 16
+#define APCI3200_AI_EOS 20
+#define APCI3200_AI_CHAN_ID 24
+#define APCI3200_AI_CHAN_VAL 28
+#define ANALOG_INPUT 0
+#define TEMPERATURE 1
+#define RESISTANCE 2
+
+#define ENABLE_EXT_TRIG 1
+#define ENABLE_EXT_GATE 2
+#define ENABLE_EXT_TRIG_GATE 3
+
+#define APCI3200_MAXVOLT 2.5
+#define ADDIDATA_GREATER_THAN_TEST 0
+#define ADDIDATA_LESS_THAN_TEST 1
+
+#define ADDIDATA_UNIPOLAR 1
+#define ADDIDATA_BIPOLAR 2
+
+//BEGIN JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+#define MAX_MODULE 4
+//END JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+
+typedef struct {
+ ULONG ul_NumberOfValue;
+ ULONG *pul_ResistanceValue;
+ ULONG *pul_TemperatureValue;
+} str_ADDIDATA_RTDStruct, *pstr_ADDIDATA_RTDStruct;
+
+//BEGIN JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+typedef struct {
+ // Begin JK 05/08/2003 change for Linux
+ unsigned long ul_CurrentSourceCJC;
+ unsigned long ul_CurrentSource[5];
+ // End JK 05/08/2003 change for Linux
+
+ // Begin CG 15/02/02 Rev 1.0 -> Rev 1.1 : Add Header Type 1
+ unsigned long ul_GainFactor[8]; // Gain Factor
+ unsigned int w_GainValue[10];
+ // End CG 15/02/02 Rev 1.0 -> Rev 1.1 : Add Header Type 1
+} str_Module;
+//END JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+
+//BEGIN JK 06.07.04: Management of sevrals boards
+typedef struct {
+ INT i_CJCAvailable;
+ INT i_CJCPolarity;
+ INT i_CJCGain;
+ INT i_InterruptFlag;
+ INT i_ADDIDATAPolarity;
+ INT i_ADDIDATAGain;
+ INT i_AutoCalibration;
+ INT i_ADDIDATAConversionTime;
+ INT i_ADDIDATAConversionTimeUnit;
+ INT i_ADDIDATAType;
+ INT i_ChannelNo;
+ INT i_ChannelCount;
+ INT i_ScanType;
+ INT i_FirstChannel;
+ INT i_LastChannel;
+ INT i_Sum;
+ INT i_Offset;
+ UINT ui_Channel_num;
+ INT i_Count;
+ INT i_Initialised;
+ //UINT ui_InterruptChannelValue[96]; //Buffer
+ UINT ui_InterruptChannelValue[144]; //Buffer
+ BYTE b_StructInitialized;
+ //Begin JK 19.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+ unsigned int ui_ScanValueArray[7 + 12]; // 7 is the maximal number of channels
+ //End JK 19.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68
+
+ //Begin JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+ INT i_ConnectionType;
+ INT i_NbrOfModule;
+ str_Module s_Module[MAX_MODULE];
+ //End JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values
+} str_BoardInfos;
+//END JK 06.07.04: Management of sevrals boards
+
+// Hardware Layer functions for Apci3200
+
+//AI
+
+INT i_APCI3200_ConfigAnalogInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI3200_ReadAnalogInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI3200_InsnWriteReleaseAnalogInput(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI3200_InsnBits_AnalogInput_Test(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI3200_StopCyclicAcquisition(struct comedi_device *dev, struct comedi_subdevice *s);
+INT i_APCI3200_InterruptHandleEos(struct comedi_device *dev);
+INT i_APCI3200_CommandTestAnalogInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd);
+INT i_APCI3200_CommandAnalogInput(struct comedi_device *dev, struct comedi_subdevice *s);
+INT i_APCI3200_ReadDigitalInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+//Interrupt
+void v_APCI3200_Interrupt(int irq, void *d);
+int i_APCI3200_InterruptHandleEos(struct comedi_device *dev);
+//Reset functions
+INT i_APCI3200_Reset(struct comedi_device *dev);
+
+int i_APCI3200_ReadCJCCalOffset(struct comedi_device *dev, unsigned int *data);
+int i_APCI3200_ReadCJCValue(struct comedi_device *dev, unsigned int *data);
+int i_APCI3200_ReadCalibrationGainValue(struct comedi_device *dev, UINT *data);
+int i_APCI3200_ReadCalibrationOffsetValue(struct comedi_device *dev, UINT *data);
+int i_APCI3200_Read1AnalogInputChannel(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn,
+ unsigned int *data);
+int i_APCI3200_ReadCJCCalGain(struct comedi_device *dev, unsigned int *data);
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3501.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3501.c
new file mode 100644
index 000000000000..20391a91da03
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3501.c
@@ -0,0 +1,742 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*.
+
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-------------------------------+---------------------------------------+
+ | Project : APCI-3501 | Compiler : GCC |
+ | Module name : hwdrv_apci3501.c| Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: Eric Stolz | Date : 02/12/2002 |
+ +-------------------------------+---------------------------------------+
+ | Description : Hardware Layer Acces For APCI-3501 |
+ +-----------------------------------------------------------------------+
+ | UPDATES |
+ +----------+-----------+------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | | | |
+ | | | |
+ | | | |
+ +----------+-----------+------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Included files |
++----------------------------------------------------------------------------+
+*/
+#include "hwdrv_apci3501.h"
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI3501_ReadDigitalInput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read value of the selected channel or port |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_NoOfChannels : No Of Channels To read |
+| UINT *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+INT i_APCI3501_ReadDigitalInput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Temp;
+ UINT ui_NoOfChannel;
+ ui_NoOfChannel = CR_CHAN(insn->chanspec);
+ ui_Temp = data[0];
+ *data = inl(devpriv->iobase + APCI3501_DIGITAL_IP);
+ if (ui_Temp == 0) {
+ *data = (*data >> ui_NoOfChannel) & 0x1;
+ } //if (ui_Temp==0)
+ else {
+ if (ui_Temp == 1) {
+
+ *data = *data & 0x3;
+ } //if (ui_Temp==1)
+ else {
+ printk("\nSpecified channel not supported \n");
+ } //elseif (ui_Temp==1)
+ } //elseif (ui_Temp==0)
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI3501_ConfigDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Configures The Digital Output Subdevice. |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| data[1] : 1 Enable VCC Interrupt |
+| 0 Disable VCC Interrupt |
+| data[2] : 1 Enable CC Interrupt |
+| 0 Disable CC Interrupt |
+| |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+int i_APCI3501_ConfigDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ if ((data[0] != 0) && (data[0] != 1)) {
+ comedi_error(dev,
+ "Not a valid Data !!! ,Data should be 1 or 0\n");
+ return -EINVAL;
+ } //if ( (data[0]!=0) && (data[0]!=1) )
+ if (data[0]) {
+ devpriv->b_OutputMemoryStatus = ADDIDATA_ENABLE;
+ } // if (data[0])
+ else {
+ devpriv->b_OutputMemoryStatus = ADDIDATA_DISABLE;
+ } //else if (data[0])
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI3501_WriteDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : writes To the digital Output Subdevice |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s : Subdevice Pointer |
+| struct comedi_insn *insn : Insn Structure Pointer |
+| unsigned int *data : Data Pointer contains |
+| configuration parameters as below |
+| |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI3501_WriteDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Temp, ui_Temp1;
+ UINT ui_NoOfChannel = CR_CHAN(insn->chanspec); // get the channel
+ if (devpriv->b_OutputMemoryStatus) {
+ ui_Temp = inl(devpriv->iobase + APCI3501_DIGITAL_OP);
+ } //if(devpriv->b_OutputMemoryStatus )
+ else {
+ ui_Temp = 0;
+ } //if(devpriv->b_OutputMemoryStatus )
+ if (data[3] == 0) {
+ if (data[1] == 0) {
+ data[0] = (data[0] << ui_NoOfChannel) | ui_Temp;
+ outl(data[0], devpriv->iobase + APCI3501_DIGITAL_OP);
+ } //if(data[1]==0)
+ else {
+ if (data[1] == 1) {
+ data[0] = (data[0] << (2 * data[2])) | ui_Temp;
+ outl(data[0],
+ devpriv->iobase + APCI3501_DIGITAL_OP);
+ } // if(data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } //else if(data[1]==1)
+ } //elseif(data[1]==0)
+ } //if(data[3]==0)
+ else {
+ if (data[3] == 1) {
+ if (data[1] == 0) {
+ data[0] = ~data[0] & 0x1;
+ ui_Temp1 = 1;
+ ui_Temp1 = ui_Temp1 << ui_NoOfChannel;
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ (data[0] << ui_NoOfChannel) ^
+ 0xffffffff;
+ data[0] = data[0] & ui_Temp;
+ outl(data[0],
+ devpriv->iobase + APCI3501_DIGITAL_OP);
+ } //if(data[1]==0)
+ else {
+ if (data[1] == 1) {
+ data[0] = ~data[0] & 0x3;
+ ui_Temp1 = 3;
+ ui_Temp1 = ui_Temp1 << 2 * data[2];
+ ui_Temp = ui_Temp | ui_Temp1;
+ data[0] =
+ ((data[0] << (2 *
+ data[2])) ^
+ 0xffffffff) & ui_Temp;
+ outl(data[0],
+ devpriv->iobase +
+ APCI3501_DIGITAL_OP);
+ } // if(data[1]==1)
+ else {
+ printk("\nSpecified channel not supported\n");
+ } //else if(data[1]==1)
+ } //elseif(data[1]==0)
+ } //if(data[3]==1);
+ else {
+ printk("\nSpecified functionality does not exist\n");
+ return -EINVAL;
+ } //if else data[3]==1)
+ } //if else data[3]==0)
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI3501_ReadDigitalOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read value of the selected channel or port |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT ui_NoOfChannels : No Of Channels To read |
+| UINT *data : Data Pointer to read status |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI3501_ReadDigitalOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ UINT ui_Temp;
+ UINT ui_NoOfChannel;
+
+ ui_NoOfChannel = CR_CHAN(insn->chanspec);
+ ui_Temp = data[0];
+ *data = inl(devpriv->iobase + APCI3501_DIGITAL_OP);
+ if (ui_Temp == 0) {
+ *data = (*data >> ui_NoOfChannel) & 0x1;
+ } // if (ui_Temp==0)
+ else {
+ if (ui_Temp == 1) {
+ *data = *data & 0x3;
+
+ } // if (ui_Temp==1)
+ else {
+ printk("\nSpecified channel not supported \n");
+ } // else if (ui_Temp==1)
+ } // else if (ui_Temp==0)
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI3501_ConfigAnalogOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Configures The Analog Output Subdevice |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s : Subdevice Pointer |
+| struct comedi_insn *insn : Insn Structure Pointer |
+| unsigned int *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| data[0] : Voltage Mode |
+| 0:Mode 0 |
+| 1:Mode 1 |
+| |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI3501_ConfigAnalogOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ outl(data[0],
+ devpriv->iobase + APCI3501_ANALOG_OUTPUT +
+ APCI3501_AO_VOLT_MODE);
+
+ if (data[0]) {
+ devpriv->b_InterruptMode = MODE1;
+ } else {
+ devpriv->b_InterruptMode = MODE0;
+ }
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI3501_WriteAnalogOutput |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Writes To the Selected Anlog Output Channel |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| struct comedi_subdevice *s : Subdevice Pointer |
+| struct comedi_insn *insn : Insn Structure Pointer |
+| unsigned int *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI3501_WriteAnalogOutput(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ ULONG ul_Command1 = 0, ul_Channel_no, ul_Polarity, ul_DAC_Ready = 0;;
+
+ ul_Channel_no = CR_CHAN(insn->chanspec);
+
+ if (devpriv->b_InterruptMode == MODE1) {
+ ul_Polarity = 0x80000000;
+ if ((*data < 0) || (*data > 16384)) {
+ printk("\nIn WriteAnalogOutput :: Not Valid Data\n");
+ }
+
+ } // end if(devpriv->b_InterruptMode==MODE1)
+ else {
+ ul_Polarity = 0;
+ if ((*data < 0) || (*data > 8192)) {
+ printk("\nIn WriteAnalogOutput :: Not Valid Data\n");
+ }
+
+ } // end else
+
+ if ((ul_Channel_no < 0) || (ul_Channel_no > 7)) {
+ printk("\nIn WriteAnalogOutput :: Not Valid Channel\n");
+ } // end if((ul_Channel_no<0)||(ul_Channel_no>7))
+
+ ul_DAC_Ready = inl(devpriv->iobase + APCI3501_ANALOG_OUTPUT);
+
+ while (ul_DAC_Ready == 0) {
+ ul_DAC_Ready = inl(devpriv->iobase + APCI3501_ANALOG_OUTPUT);
+ ul_DAC_Ready = (ul_DAC_Ready >> 8) & 1;
+ }
+
+ if (ul_DAC_Ready) {
+// Output the Value on the output channels.
+ ul_Command1 =
+ (ULONG) ((ULONG) (ul_Channel_no & 0xFF) |
+ (ULONG) ((*data << 0x8) & 0x7FFFFF00L) |
+ (ULONG) (ul_Polarity));
+ outl(ul_Command1,
+ devpriv->iobase + APCI3501_ANALOG_OUTPUT +
+ APCI3501_AO_PROG);
+ }
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI3501_ConfigTimerCounterWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Configures The Timer , Counter or Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| data[0] : 0 Configure As Timer |
+| 1 Configure As Counter |
+| 2 Configure As Watchdog |
+| data[1] : 1 Enable Interrupt |
+| 0 Disable Interrupt |
+| data[2] : Time Unit |
+| data[3] : Reload Value |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+INT i_APCI3501_ConfigTimerCounterWatchdog(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ ULONG ul_Command1 = 0;
+ devpriv->tsk_Current = current;
+ if (data[0] == ADDIDATA_WATCHDOG) {
+
+ devpriv->b_TimerSelectMode = ADDIDATA_WATCHDOG;
+ //Disable the watchdog
+ outl(0x0, devpriv->iobase + APCI3501_WATCHDOG + APCI3501_TCW_PROG); //disable Wa
+
+ if (data[1] == 1) {
+ //Enable TIMER int & DISABLE ALL THE OTHER int SOURCES
+ outl(0x02,
+ devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ } else {
+ outl(0x0, devpriv->iobase + APCI3501_WATCHDOG + APCI3501_TCW_PROG); //disable Timer interrupt
+ }
+
+ //Loading the Timebase value
+ outl(data[2],
+ devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_TIMEBASE);
+
+ //Loading the Reload value
+ outl(data[3],
+ devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_RELOAD_VALUE);
+ //Set the mode
+ ul_Command1 = inl(devpriv->iobase + APCI3501_WATCHDOG + APCI3501_TCW_PROG) | 0xFFF819E0UL; //e2->e0
+ outl(ul_Command1,
+ devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ } //end if(data[0]==ADDIDATA_WATCHDOG)
+
+ else if (data[0] == ADDIDATA_TIMER) {
+ //First Stop The Timer
+ ul_Command1 =
+ inl(devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ ul_Command1 = ul_Command1 & 0xFFFFF9FEUL;
+ outl(ul_Command1, devpriv->iobase + APCI3501_WATCHDOG + APCI3501_TCW_PROG); //Stop The Timer
+ devpriv->b_TimerSelectMode = ADDIDATA_TIMER;
+ if (data[1] == 1) {
+ //Enable TIMER int & DISABLE ALL THE OTHER int SOURCES
+ outl(0x02,
+ devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ } else {
+ outl(0x0, devpriv->iobase + APCI3501_WATCHDOG + APCI3501_TCW_PROG); //disable Timer interrupt
+ }
+
+ // Loading Timebase
+ outl(data[2],
+ devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_TIMEBASE);
+
+ //Loading the Reload value
+ outl(data[3],
+ devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_RELOAD_VALUE);
+
+ // printk ("\nTimer Address :: %x\n", (devpriv->iobase+APCI3501_WATCHDOG));
+ ul_Command1 =
+ inl(devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ ul_Command1 =
+ (ul_Command1 & 0xFFF719E2UL) | 2UL << 13UL | 0x10UL;
+ outl(ul_Command1, devpriv->iobase + APCI3501_WATCHDOG + APCI3501_TCW_PROG); //mode 2
+
+ } //end if(data[0]==ADDIDATA_TIMER)
+
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI3501_StartStopWriteTimerCounterWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Start / Stop The Selected Timer , Counter or Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| data[0] : 0 Timer |
+| 1 Counter |
+| 2 Watchdog | | data[1] : 1 Start |
+| 0 Stop | 2 Trigger |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3501_StartStopWriteTimerCounterWatchdog(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ ULONG ul_Command1 = 0;
+ int i_Temp;
+ if (devpriv->b_TimerSelectMode == ADDIDATA_WATCHDOG) {
+
+ if (data[1] == 1) {
+ ul_Command1 =
+ inl(devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ ul_Command1 = (ul_Command1 & 0xFFFFF9FFUL) | 0x1UL;
+ //Enable the Watchdog
+ outl(ul_Command1,
+ devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ }
+
+ else if (data[1] == 0) //Stop The Watchdog
+ {
+ //Stop The Watchdog
+ ul_Command1 =
+ inl(devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ ul_Command1 = ul_Command1 & 0xFFFFF9FEUL;
+ outl(0x0,
+ devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ } else if (data[1] == 2) {
+ ul_Command1 =
+ inl(devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ ul_Command1 = (ul_Command1 & 0xFFFFF9FFUL) | 0x200UL;
+ outl(ul_Command1,
+ devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ } //if(data[1]==2)
+ } // end if (devpriv->b_TimerSelectMode==ADDIDATA_WATCHDOG)
+
+ if (devpriv->b_TimerSelectMode == ADDIDATA_TIMER) {
+ if (data[1] == 1) {
+
+ ul_Command1 =
+ inl(devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ ul_Command1 = (ul_Command1 & 0xFFFFF9FFUL) | 0x1UL;
+ //Enable the Timer
+ outl(ul_Command1,
+ devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ } else if (data[1] == 0) {
+ //Stop The Timer
+ ul_Command1 =
+ inl(devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ ul_Command1 = ul_Command1 & 0xFFFFF9FEUL;
+ outl(ul_Command1,
+ devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ }
+
+ else if (data[1] == 2) {
+ //Trigger the Timer
+ ul_Command1 =
+ inl(devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ ul_Command1 = (ul_Command1 & 0xFFFFF9FFUL) | 0x200UL;
+ outl(ul_Command1,
+ devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_PROG);
+ }
+
+ } // end if (devpriv->b_TimerSelectMode==ADDIDATA_TIMER)
+ i_Temp = inl(devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_TRIG_STATUS) & 0x1;
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI3501_ReadTimerCounterWatchdog |
+| (struct comedi_device *dev,struct comedi_subdevice *s, |
+| struct comedi_insn *insn,unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read The Selected Timer , Counter or Watchdog |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev : Driver handle |
+| UINT *data : Data Pointer contains |
+| configuration parameters as below |
+| |
+| data[0] : 0 Timer |
+| 1 Counter |
+| 2 Watchdog | | data[1] : Timer Counter Watchdog Number |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3501_ReadTimerCounterWatchdog(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+
+ if (devpriv->b_TimerSelectMode == ADDIDATA_WATCHDOG) {
+ data[0] =
+ inl(devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_TRIG_STATUS) & 0x1;
+ data[1] = inl(devpriv->iobase + APCI3501_WATCHDOG);
+ } // end if (devpriv->b_TimerSelectMode==ADDIDATA_WATCHDOG)
+
+ else if (devpriv->b_TimerSelectMode == ADDIDATA_TIMER) {
+ data[0] =
+ inl(devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_TRIG_STATUS) & 0x1;
+ data[1] = inl(devpriv->iobase + APCI3501_WATCHDOG);
+ } // end if (devpriv->b_TimerSelectMode==ADDIDATA_TIMER)
+
+ else if ((devpriv->b_TimerSelectMode != ADDIDATA_TIMER)
+ && (devpriv->b_TimerSelectMode != ADDIDATA_WATCHDOG)) {
+ printk("\nIn ReadTimerCounterWatchdog :: Invalid Subdevice \n");
+ }
+ return insn->n;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI3501_Reset(struct comedi_device *dev) |
+| |
++----------------------------------------------------------------------------+
+| Task :Resets the registers of the card |
++----------------------------------------------------------------------------+
+| Input Parameters : |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : |
+| |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3501_Reset(struct comedi_device * dev)
+{
+ int i_Count = 0, i_temp = 0;
+ ULONG ul_Command1 = 0, ul_Polarity, ul_DAC_Ready = 0;
+ outl(0x0, devpriv->iobase + APCI3501_DIGITAL_OP);
+ outl(1, devpriv->iobase + APCI3501_ANALOG_OUTPUT +
+ APCI3501_AO_VOLT_MODE);
+
+ ul_Polarity = 0x80000000;
+
+ for (i_Count = 0; i_Count <= 7; i_Count++) {
+ ul_DAC_Ready = inl(devpriv->iobase + APCI3501_ANALOG_OUTPUT);
+
+ while (ul_DAC_Ready == 0) {
+ ul_DAC_Ready =
+ inl(devpriv->iobase + APCI3501_ANALOG_OUTPUT);
+ ul_DAC_Ready = (ul_DAC_Ready >> 8) & 1;
+ }
+
+ if (ul_DAC_Ready) {
+ // Output the Value on the output channels.
+ ul_Command1 =
+ (ULONG) ((ULONG) (i_Count & 0xFF) |
+ (ULONG) ((i_temp << 0x8) & 0x7FFFFF00L) |
+ (ULONG) (ul_Polarity));
+ outl(ul_Command1,
+ devpriv->iobase + APCI3501_ANALOG_OUTPUT +
+ APCI3501_AO_PROG);
+ }
+ }
+
+ return 0;
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : static void v_APCI3501_Interrupt |
+| (int irq , void *d) |
++----------------------------------------------------------------------------+
+| Task : Interrupt processing Routine |
++----------------------------------------------------------------------------+
+| Input Parameters : int irq : irq number |
+| void *d : void pointer |
++----------------------------------------------------------------------------+
+| Output Parameters : -- |
++----------------------------------------------------------------------------+
+| Return Value : TRUE : No error occur |
+| : FALSE : Error occur. Return the error |
+| |
++----------------------------------------------------------------------------+
+*/
+void v_APCI3501_Interrupt(int irq, void *d)
+{
+ int i_temp;
+ struct comedi_device *dev = d;
+ unsigned int ui_Timer_AOWatchdog;
+ unsigned long ul_Command1;
+ // Disable Interrupt
+ ul_Command1 =
+ inl(devpriv->iobase + APCI3501_WATCHDOG + APCI3501_TCW_PROG);
+
+ ul_Command1 = (ul_Command1 & 0xFFFFF9FDul);
+ outl(ul_Command1,
+ devpriv->iobase + APCI3501_WATCHDOG + APCI3501_TCW_PROG);
+
+ ui_Timer_AOWatchdog =
+ inl(devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_IRQ) & 0x1;
+
+ if ((!ui_Timer_AOWatchdog)) {
+ comedi_error(dev, "IRQ from unknow source");
+ return;
+ }
+
+ // Enable Interrupt
+ //Send a signal to from kernel to user space
+ send_sig(SIGIO, devpriv->tsk_Current, 0);
+ ul_Command1 =
+ inl(devpriv->iobase + APCI3501_WATCHDOG + APCI3501_TCW_PROG);
+ ul_Command1 = ((ul_Command1 & 0xFFFFF9FDul) | 1 << 1);
+ outl(ul_Command1,
+ devpriv->iobase + APCI3501_WATCHDOG + APCI3501_TCW_PROG);
+ i_temp = inl(devpriv->iobase + APCI3501_WATCHDOG +
+ APCI3501_TCW_TRIG_STATUS) & 0x1;
+ return;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3501.h b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3501.h
new file mode 100644
index 000000000000..51e7b66544ac
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3501.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+// Card Specific information
+#define APCI3501_BOARD_VENDOR_ID 0x15B8
+#define APCI3501_ADDRESS_RANGE 255
+
+#define APCI3501_DIGITAL_IP 0x50
+#define APCI3501_DIGITAL_OP 0x40
+#define APCI3501_ANALOG_OUTPUT 0x00
+
+//Analog Output related Defines
+#define APCI3501_AO_VOLT_MODE 0
+#define APCI3501_AO_PROG 4
+#define APCI3501_AO_TRIG_SCS 8
+#define UNIPOLAR 0
+#define BIPOLAR 1
+#define MODE0 0
+#define MODE1 1
+// ANALOG OUTPUT RANGE
+struct comedi_lrange range_apci3501_ao = { 2, {
+ BIP_RANGE(10),
+ UNI_RANGE(10)
+ }
+};
+
+//Watchdog Related Defines
+
+#define APCI3501_WATCHDOG 0x20
+#define APCI3501_TCW_SYNC_ENABLEDISABLE 0
+#define APCI3501_TCW_RELOAD_VALUE 4
+#define APCI3501_TCW_TIMEBASE 8
+#define APCI3501_TCW_PROG 12
+#define APCI3501_TCW_TRIG_STATUS 16
+#define APCI3501_TCW_IRQ 20
+#define APCI3501_TCW_WARN_TIMEVAL 24
+#define APCI3501_TCW_WARN_TIMEBASE 28
+#define ADDIDATA_TIMER 0
+#define ADDIDATA_WATCHDOG 2
+
+// Hardware Layer functions for Apci3501
+
+//AO
+INT i_APCI3501_ConfigAnalogOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI3501_WriteAnalogOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+//DI
+// for di read
+//INT i_APCI3501_ReadDigitalInput(struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data);
+
+INT i_APCI3501_ReadDigitalInput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+//DO
+int i_APCI3501_ConfigDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI3501_WriteDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+INT i_APCI3501_ReadDigitalOutput(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+
+// TIMER
+// timer value is passed as u seconds
+INT i_APCI3501_ConfigTimerCounterWatchdog(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+int i_APCI3501_StartStopWriteTimerCounterWatchdog(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data);
+int i_APCI3501_ReadTimerCounterWatchdog(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+//Interrupt
+void v_APCI3501_Interrupt(int irq, void *d);
+
+//Reset functions
+int i_APCI3501_Reset(struct comedi_device *dev);
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3xxx.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3xxx.c
new file mode 100644
index 000000000000..b7268e4da64e
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3xxx.c
@@ -0,0 +1,1691 @@
+/**
+@verbatim
+
+Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+
+ ADDI-DATA GmbH
+ Dieselstrasse 3
+ D-77833 Ottersweier
+ Tel: +19(0)7223/9493-0
+ Fax: +49(0)7223/9493-92
+ http://www.addi-data-com
+ info@addi-data.com
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You shoud also find the complete GPL in the COPYING file accompanying this source code.
+
+@endverbatim
+*/
+/*
+ +-----------------------------------------------------------------------+
+ | (C) ADDI-DATA GmbH Dieselstrasse 3 D-77833 Ottersweier |
+ +-----------------------------------------------------------------------+
+ | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
+ | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
+ +-----------------------------------------------------------------------+
+ | Project : APCI-3XXX | Compiler : GCC |
+ | Module name : hwdrv_apci3xxx.c| Version : 2.96 |
+ +-------------------------------+---------------------------------------+
+ | Project manager: S. Weber | Date : 15/09/2005 |
+ +-----------------------------------------------------------------------+
+ | Description :APCI3XXX Module. Hardware abstraction Layer for APCI3XXX|
+ +-----------------------------------------------------------------------+
+ | UPDATE'S |
+ +-----------------------------------------------------------------------+
+ | Date | Author | Description of updates |
+ +----------+-----------+------------------------------------------------+
+ | | | |
+ | | | |
+ +----------+-----------+------------------------------------------------+
+*/
+
+#include "hwdrv_apci3xxx.h"
+
+/*
++----------------------------------------------------------------------------+
+| ANALOG INPUT FUNCTIONS |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI3XXX_TestConversionStarted |
+| (struct comedi_device *dev) |
++----------------------------------------------------------------------------+
+| Task Test if any conversion started |
++----------------------------------------------------------------------------+
+| Input Parameters : - |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0 : Conversion not started |
+| 1 : Conversion started |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3XXX_TestConversionStarted(struct comedi_device * dev)
+{
+ if ((readl((void *)(devpriv->dw_AiBase + 8)) & 0x80000UL) == 0x80000UL) {
+ return (1);
+ } else {
+ return (0);
+ }
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI3XXX_AnalogInputConfigOperatingMode |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task Converting mode and convert time selection |
++----------------------------------------------------------------------------+
+| Input Parameters : b_SingleDiff = (BYTE) data[1]; |
+| b_TimeBase = (BYTE) data[2]; (0: ns, 1:micros 2:ms)|
+| dw_ReloadValue = (DWORD) data[3]; |
+| ........ |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value :>0 : No error |
+| -1 : Single/Diff selection error |
+| -2 : Convert time base unity selection error |
+| -3 : Convert time value selection error |
+| -10: Any conversion started |
+| .... |
+| -100 : Config command error |
+| -101 : Data size error |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3XXX_AnalogInputConfigOperatingMode(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = insn->n;
+ BYTE b_TimeBase = 0;
+ BYTE b_SingleDiff = 0;
+ DWORD dw_ReloadValue = 0;
+ DWORD dw_TestReloadValue = 0;
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n == 4) {
+ /****************************/
+ /* Get the Singel/Diff flag */
+ /****************************/
+
+ b_SingleDiff = (BYTE) data[1];
+
+ /****************************/
+ /* Get the time base unitiy */
+ /****************************/
+
+ b_TimeBase = (BYTE) data[2];
+
+ /*************************************/
+ /* Get the convert time reload value */
+ /*************************************/
+
+ dw_ReloadValue = (DWORD) data[3];
+
+ /**********************/
+ /* Test the time base */
+ /**********************/
+
+ if ((devpriv->ps_BoardInfo->
+ b_AvailableConvertUnit & (1 << b_TimeBase)) !=
+ 0) {
+ /*******************************/
+ /* Test the convert time value */
+ /*******************************/
+
+ if ((dw_ReloadValue >= 0) && (dw_ReloadValue <= 65535)) {
+ dw_TestReloadValue = dw_ReloadValue;
+
+ if (b_TimeBase == 1) {
+ dw_TestReloadValue =
+ dw_TestReloadValue * 1000UL;
+ }
+ if (b_TimeBase == 2) {
+ dw_TestReloadValue =
+ dw_TestReloadValue * 1000000UL;
+ }
+
+ /*******************************/
+ /* Test the convert time value */
+ /*******************************/
+
+ if (dw_TestReloadValue >=
+ devpriv->ps_BoardInfo->
+ ui_MinAcquisitiontimeNs) {
+ if ((b_SingleDiff == APCI3XXX_SINGLE)
+ || (b_SingleDiff ==
+ APCI3XXX_DIFF)) {
+ if (((b_SingleDiff == APCI3XXX_SINGLE) && (devpriv->ps_BoardInfo->i_NbrAiChannel == 0)) || ((b_SingleDiff == APCI3XXX_DIFF) && (devpriv->ps_BoardInfo->i_NbrAiChannelDiff == 0))) {
+ /*******************************/
+ /* Single/Diff selection error */
+ /*******************************/
+
+ printk("Single/Diff selection error\n");
+ i_ReturnValue = -1;
+ } else {
+ /**********************************/
+ /* Test if conversion not started */
+ /**********************************/
+
+ if (i_APCI3XXX_TestConversionStarted(dev) == 0) {
+ devpriv->
+ ui_EocEosConversionTime
+ =
+ (UINT)
+ dw_ReloadValue;
+ devpriv->
+ b_EocEosConversionTimeBase
+ =
+ b_TimeBase;
+ devpriv->
+ b_SingelDiff
+ =
+ b_SingleDiff;
+ devpriv->
+ b_AiInitialisation
+ = 1;
+
+ /*******************************/
+ /* Set the convert timing unit */
+ /*******************************/
+
+ writel((DWORD)
+ b_TimeBase,
+ (void *)
+ (devpriv->
+ dw_AiBase
+ +
+ 36));
+
+ /**************************/
+ /* Set the convert timing */
+ /*************************/
+
+ writel(dw_ReloadValue, (void *)(devpriv->dw_AiBase + 32));
+ } else {
+ /**************************/
+ /* Any conversion started */
+ /**************************/
+
+ printk("Any conversion started\n");
+ i_ReturnValue =
+ -10;
+ }
+ }
+ } else {
+ /*******************************/
+ /* Single/Diff selection error */
+ /*******************************/
+
+ printk("Single/Diff selection error\n");
+ i_ReturnValue = -1;
+ }
+ } else {
+ /************************/
+ /* Time selection error */
+ /************************/
+
+ printk("Convert time value selection error\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /************************/
+ /* Time selection error */
+ /************************/
+
+ printk("Convert time value selection error\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*****************************/
+ /* Time base selection error */
+ /*****************************/
+
+ printk("Convert time base unity selection error\n");
+ i_ReturnValue = -2;
+ }
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("Buffer size error\n");
+ i_ReturnValue = -101;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI3XXX_InsnConfigAnalogInput |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task Converting mode and convert time selection |
++----------------------------------------------------------------------------+
+| Input Parameters : b_ConvertMode = (BYTE) data[0]; |
+| b_TimeBase = (BYTE) data[1]; (0: ns, 1:micros 2:ms)|
+| dw_ReloadValue = (DWORD) data[2]; |
+| ........ |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value :>0: No error |
+| .... |
+| -100 : Config command error |
+| -101 : Data size error |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3XXX_InsnConfigAnalogInput(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = insn->n;
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n >= 1) {
+ switch ((BYTE) data[0]) {
+ case APCI3XXX_CONFIGURATION:
+ i_ReturnValue =
+ i_APCI3XXX_AnalogInputConfigOperatingMode(dev,
+ s, insn, data);
+ break;
+
+ default:
+ i_ReturnValue = -100;
+ printk("Config command error %d\n", data[0]);
+ break;
+ }
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("Buffer size error\n");
+ i_ReturnValue = -101;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI3XXX_InsnReadAnalogInput |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task Read 1 analog input |
++----------------------------------------------------------------------------+
+| Input Parameters : b_Range = CR_RANGE(insn->chanspec); |
+| b_Channel = CR_CHAN(insn->chanspec); |
+| dw_NbrOfAcquisition = insn->n; |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value :>0: No error |
+| -3 : Channel selection error |
+| -4 : Configuration selelection error |
+| -10: Any conversion started |
+| .... |
+| -100 : Config command error |
+| -101 : Data size error |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3XXX_InsnReadAnalogInput(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = insn->n;
+ BYTE b_Configuration = (BYTE) CR_RANGE(insn->chanspec);
+ BYTE b_Channel = (BYTE) CR_CHAN(insn->chanspec);
+ DWORD dw_Temp = 0;
+ DWORD dw_Configuration = 0;
+ DWORD dw_AcquisitionCpt = 0;
+ BYTE b_Interrupt = 0;
+
+ /*************************************/
+ /* Test if operating mode configured */
+ /*************************************/
+
+ if (devpriv->b_AiInitialisation) {
+ /***************************/
+ /* Test the channel number */
+ /***************************/
+
+ if (((b_Channel < devpriv->ps_BoardInfo->i_NbrAiChannel)
+ && (devpriv->b_SingelDiff == APCI3XXX_SINGLE))
+ || ((b_Channel < devpriv->ps_BoardInfo->
+ i_NbrAiChannelDiff)
+ && (devpriv->b_SingelDiff == APCI3XXX_DIFF))) {
+ /**********************************/
+ /* Test the channel configuration */
+ /**********************************/
+
+ if (b_Configuration > 7) {
+ /***************************/
+ /* Channel not initialised */
+ /***************************/
+
+ i_ReturnValue = -4;
+ printk("Channel %d range %d selection error\n",
+ b_Channel, b_Configuration);
+ }
+ } else {
+ /***************************/
+ /* Channel selection error */
+ /***************************/
+
+ i_ReturnValue = -3;
+ printk("Channel %d selection error\n", b_Channel);
+ }
+
+ /**************************/
+ /* Test if no error occur */
+ /**************************/
+
+ if (i_ReturnValue >= 0) {
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if ((b_Interrupt != 0) || ((b_Interrupt == 0)
+ && (insn->n >= 1))) {
+ /**********************************/
+ /* Test if conversion not started */
+ /**********************************/
+
+ if (i_APCI3XXX_TestConversionStarted(dev) == 0) {
+ /******************/
+ /* Clear the FIFO */
+ /******************/
+
+ writel(0x10000UL,
+ (void *)(devpriv->dw_AiBase +
+ 12));
+
+ /*******************************/
+ /* Get and save the delay mode */
+ /*******************************/
+
+ dw_Temp =
+ readl((void *)(devpriv->
+ dw_AiBase + 4));
+ dw_Temp = dw_Temp & 0xFFFFFEF0UL;
+
+ /***********************************/
+ /* Channel configuration selection */
+ /***********************************/
+
+ writel(dw_Temp,
+ (void *)(devpriv->dw_AiBase +
+ 4));
+
+ /**************************/
+ /* Make the configuration */
+ /**************************/
+
+ dw_Configuration =
+ (b_Configuration & 3) |
+ ((DWORD) (b_Configuration >> 2)
+ << 6) | ((DWORD) devpriv->
+ b_SingelDiff << 7);
+
+ /***************************/
+ /* Write the configuration */
+ /***************************/
+
+ writel(dw_Configuration,
+ (void *)(devpriv->dw_AiBase +
+ 0));
+
+ /*********************/
+ /* Channel selection */
+ /*********************/
+
+ writel(dw_Temp | 0x100UL,
+ (void *)(devpriv->dw_AiBase +
+ 4));
+ writel((DWORD) b_Channel,
+ (void *)(devpriv->dw_AiBase +
+ 0));
+
+ /***********************/
+ /* Restaure delay mode */
+ /***********************/
+
+ writel(dw_Temp,
+ (void *)(devpriv->dw_AiBase +
+ 4));
+
+ /***********************************/
+ /* Set the number of sequence to 1 */
+ /***********************************/
+
+ writel(1,
+ (void *)(devpriv->dw_AiBase +
+ 48));
+
+ /***************************/
+ /* Save the interrupt flag */
+ /***************************/
+
+ devpriv->b_EocEosInterrupt =
+ b_Interrupt;
+
+ /*******************************/
+ /* Save the number of channels */
+ /*******************************/
+
+ devpriv->ui_AiNbrofChannels = 1;
+
+ /******************************/
+ /* Test if interrupt not used */
+ /******************************/
+
+ if (b_Interrupt == 0) {
+ for (dw_AcquisitionCpt = 0;
+ dw_AcquisitionCpt <
+ insn->n;
+ dw_AcquisitionCpt++) {
+ /************************/
+ /* Start the conversion */
+ /************************/
+
+ writel(0x80000UL,
+ (void *)
+ (devpriv->
+ dw_AiBase
+ + 8));
+
+ /****************/
+ /* Wait the EOS */
+ /****************/
+
+ do {
+ dw_Temp =
+ readl(
+ (void *)
+ (devpriv->
+ dw_AiBase
+ +
+ 20));
+ dw_Temp =
+ dw_Temp
+ & 1;
+ }
+ while (dw_Temp != 1);
+
+ /*************************/
+ /* Read the analog value */
+ /*************************/
+
+ data[dw_AcquisitionCpt]
+ =
+ (unsigned int)
+ readl((void
+ *)
+ (devpriv->
+ dw_AiBase
+ + 28));
+ }
+ } else {
+ /************************/
+ /* Start the conversion */
+ /************************/
+
+ writel(0x180000UL,
+ (void *)(devpriv->
+ dw_AiBase + 8));
+ }
+ } else {
+ /**************************/
+ /* Any conversion started */
+ /**************************/
+
+ printk("Any conversion started\n");
+ i_ReturnValue = -10;
+ }
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("Buffer size error\n");
+ i_ReturnValue = -101;
+ }
+ }
+ } else {
+ /***************************/
+ /* Channel selection error */
+ /***************************/
+
+ printk("Operating mode not configured\n");
+ i_ReturnValue = -1;
+ }
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name : void v_APCI3XXX_Interrupt (int irq, |
+| void *d) |
++----------------------------------------------------------------------------+
+| Task :Interrupt handler for APCI3XXX |
+| When interrupt occurs this gets called. |
+| First it finds which interrupt has been generated and |
+| handles corresponding interrupt |
++----------------------------------------------------------------------------+
+| Input Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : - |
++----------------------------------------------------------------------------+
+*/
+
+void v_APCI3XXX_Interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ BYTE b_CopyCpt = 0;
+ DWORD dw_Status = 0;
+
+ /***************************/
+ /* Test if interrupt occur */
+ /***************************/
+
+ if (((dw_Status = readl((void *)(devpriv->dw_AiBase + 16))) & 0x2UL) ==
+ 0x2UL) {
+ /***********************/
+ /* Reset the interrupt */
+ /***********************/
+
+ writel(dw_Status, (void *)(devpriv->dw_AiBase + 16));
+
+ /*****************************/
+ /* Test if interrupt enabled */
+ /*****************************/
+
+ if (devpriv->b_EocEosInterrupt == 1) {
+ /********************************/
+ /* Read all analog inputs value */
+ /********************************/
+
+ for (b_CopyCpt = 0;
+ b_CopyCpt < devpriv->ui_AiNbrofChannels;
+ b_CopyCpt++) {
+ devpriv->ui_AiReadData[b_CopyCpt] =
+ (UINT) readl((void *)(devpriv->
+ dw_AiBase + 28));
+ }
+
+ /**************************/
+ /* Set the interrupt flag */
+ /**************************/
+
+ devpriv->b_EocEosInterrupt = 2;
+
+ /**********************************************/
+ /* Send a signal to from kernel to user space */
+ /**********************************************/
+
+ send_sig(SIGIO, devpriv->tsk_Current, 0);
+ }
+ }
+}
+
+/*
++----------------------------------------------------------------------------+
+| ANALOG OUTPUT SUBDEVICE |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI3XXX_InsnWriteAnalogOutput |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task Read 1 analog input |
++----------------------------------------------------------------------------+
+| Input Parameters : b_Range = CR_RANGE(insn->chanspec); |
+| b_Channel = CR_CHAN(insn->chanspec); |
+| data[0] = analog value; |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value :>0: No error |
+| -3 : Channel selection error |
+| -4 : Configuration selelection error |
+| .... |
+| -101 : Data size error |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3XXX_InsnWriteAnalogOutput(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ BYTE b_Range = (BYTE) CR_RANGE(insn->chanspec);
+ BYTE b_Channel = (BYTE) CR_CHAN(insn->chanspec);
+ DWORD dw_Status = 0;
+ INT i_ReturnValue = insn->n;
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n >= 1) {
+ /***************************/
+ /* Test the channel number */
+ /***************************/
+
+ if (b_Channel < devpriv->ps_BoardInfo->i_NbrAoChannel) {
+ /**********************************/
+ /* Test the channel configuration */
+ /**********************************/
+
+ if (b_Range < 2) {
+ /***************************/
+ /* Set the range selection */
+ /***************************/
+
+ writel(b_Range,
+ (void *)(devpriv->dw_AiBase + 96));
+
+ /**************************************************/
+ /* Write the analog value to the selected channel */
+ /**************************************************/
+
+ writel((data[0] << 8) | b_Channel,
+ (void *)(devpriv->dw_AiBase + 100));
+
+ /****************************/
+ /* Wait the end of transfer */
+ /****************************/
+
+ do {
+ dw_Status =
+ readl((void *)(devpriv->
+ dw_AiBase + 96));
+ }
+ while ((dw_Status & 0x100) != 0x100);
+ } else {
+ /***************************/
+ /* Channel not initialised */
+ /***************************/
+
+ i_ReturnValue = -4;
+ printk("Channel %d range %d selection error\n",
+ b_Channel, b_Range);
+ }
+ } else {
+ /***************************/
+ /* Channel selection error */
+ /***************************/
+
+ i_ReturnValue = -3;
+ printk("Channel %d selection error\n", b_Channel);
+ }
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("Buffer size error\n");
+ i_ReturnValue = -101;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| TTL FUNCTIONS |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI3XXX_InsnConfigInitTTLIO |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task You must calling this function be |
+| for you call any other function witch access of TTL. |
+| APCI3XXX_TTL_INIT_DIRECTION_PORT2(user inputs for direction)|
++----------------------------------------------------------------------------+
+| Input Parameters : b_InitType = (BYTE) data[0]; |
+| b_Port2Mode = (BYTE) data[1]; |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value :>0: No error |
+| -1: Port 2 mode selection is wrong |
+| .... |
+| -100 : Config command error |
+| -101 : Data size error |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3XXX_InsnConfigInitTTLIO(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = insn->n;
+ BYTE b_Command = 0;
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n >= 1) {
+ /*******************/
+ /* Get the command */
+ /* **************** */
+
+ b_Command = (BYTE) data[0];
+
+ /********************/
+ /* Test the command */
+ /********************/
+
+ if (b_Command == APCI3XXX_TTL_INIT_DIRECTION_PORT2) {
+ /***************************************/
+ /* Test the initialisation buffer size */
+ /***************************************/
+
+ if ((b_Command == APCI3XXX_TTL_INIT_DIRECTION_PORT2)
+ && (insn->n != 2)) {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("Buffer size error\n");
+ i_ReturnValue = -101;
+ }
+ } else {
+ /************************/
+ /* Config command error */
+ /************************/
+
+ printk("Command selection error\n");
+ i_ReturnValue = -100;
+ }
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("Buffer size error\n");
+ i_ReturnValue = -101;
+ }
+
+ /*********************************************************************************/
+ /* Test if no error occur and APCI3XXX_TTL_INIT_DIRECTION_PORT2 command selected */
+ /*********************************************************************************/
+
+ if ((i_ReturnValue >= 0)
+ && (b_Command == APCI3XXX_TTL_INIT_DIRECTION_PORT2)) {
+ /**********************/
+ /* Test the direction */
+ /**********************/
+
+ if ((data[1] == 0) || (data[1] == 0xFF)) {
+ /**************************/
+ /* Save the configuration */
+ /**************************/
+
+ devpriv->ul_TTLPortConfiguration[0] =
+ devpriv->ul_TTLPortConfiguration[0] | data[1];
+ } else {
+ /************************/
+ /* Port direction error */
+ /************************/
+
+ printk("Port 2 direction selection error\n");
+ i_ReturnValue = -1;
+ }
+ }
+
+ /**************************/
+ /* Test if no error occur */
+ /**************************/
+
+ if (i_ReturnValue >= 0) {
+ /***********************************/
+ /* Test if TTL port initilaisation */
+ /***********************************/
+
+ if (b_Command == APCI3XXX_TTL_INIT_DIRECTION_PORT2) {
+ /*************************/
+ /* Set the configuration */
+ /*************************/
+
+ outl(data[1], devpriv->iobase + 224);
+ }
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| TTL INPUT FUNCTIONS |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI3XXX_InsnBitsTTLIO |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Write the selected output mask and read the status from|
+| all TTL channles |
++----------------------------------------------------------------------------+
+| Input Parameters : dw_ChannelMask = data [0]; |
+| dw_BitMask = data [1]; |
++----------------------------------------------------------------------------+
+| Output Parameters : data[1] : All TTL channles states |
++----------------------------------------------------------------------------+
+| Return Value : >0 : No error |
+| -4 : Channel mask error |
+| -101 : Data size error |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3XXX_InsnBitsTTLIO(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = insn->n;
+ BYTE b_ChannelCpt = 0;
+ DWORD dw_ChannelMask = 0;
+ DWORD dw_BitMask = 0;
+ DWORD dw_Status = 0;
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n >= 2) {
+ /*******************************/
+ /* Get the channe and bit mask */
+ /*******************************/
+
+ dw_ChannelMask = data[0];
+ dw_BitMask = data[1];
+
+ /*************************/
+ /* Test the channel mask */
+ /*************************/
+
+ if (((dw_ChannelMask & 0XFF00FF00) == 0) &&
+ (((devpriv->ul_TTLPortConfiguration[0] & 0xFF) == 0xFF)
+ || (((devpriv->ul_TTLPortConfiguration[0] &
+ 0xFF) == 0)
+ && ((dw_ChannelMask & 0XFF0000) ==
+ 0)))) {
+ /*********************************/
+ /* Test if set/reset any channel */
+ /*********************************/
+
+ if (dw_ChannelMask) {
+ /****************************************/
+ /* Test if set/rest any port 0 channels */
+ /****************************************/
+
+ if (dw_ChannelMask & 0xFF) {
+ /*******************************************/
+ /* Read port 0 (first digital output port) */
+ /*******************************************/
+
+ dw_Status = inl(devpriv->iobase + 80);
+
+ for (b_ChannelCpt = 0; b_ChannelCpt < 8;
+ b_ChannelCpt++) {
+ if ((dw_ChannelMask >>
+ b_ChannelCpt) &
+ 1) {
+ dw_Status =
+ (dw_Status &
+ (0xFF - (1 << b_ChannelCpt))) | (dw_BitMask & (1 << b_ChannelCpt));
+ }
+ }
+
+ outl(dw_Status, devpriv->iobase + 80);
+ }
+
+ /****************************************/
+ /* Test if set/rest any port 2 channels */
+ /****************************************/
+
+ if (dw_ChannelMask & 0xFF0000) {
+ dw_BitMask = dw_BitMask >> 16;
+ dw_ChannelMask = dw_ChannelMask >> 16;
+
+ /********************************************/
+ /* Read port 2 (second digital output port) */
+ /********************************************/
+
+ dw_Status = inl(devpriv->iobase + 112);
+
+ for (b_ChannelCpt = 0; b_ChannelCpt < 8;
+ b_ChannelCpt++) {
+ if ((dw_ChannelMask >>
+ b_ChannelCpt) &
+ 1) {
+ dw_Status =
+ (dw_Status &
+ (0xFF - (1 << b_ChannelCpt))) | (dw_BitMask & (1 << b_ChannelCpt));
+ }
+ }
+
+ outl(dw_Status, devpriv->iobase + 112);
+ }
+ }
+
+ /*******************************************/
+ /* Read port 0 (first digital output port) */
+ /*******************************************/
+
+ data[1] = inl(devpriv->iobase + 80);
+
+ /******************************************/
+ /* Read port 1 (first digital input port) */
+ /******************************************/
+
+ data[1] = data[1] | (inl(devpriv->iobase + 64) << 8);
+
+ /************************/
+ /* Test if port 2 input */
+ /************************/
+
+ if ((devpriv->ul_TTLPortConfiguration[0] & 0xFF) == 0) {
+ data[1] =
+ data[1] | (inl(devpriv->iobase +
+ 96) << 16);
+ } else {
+ data[1] =
+ data[1] | (inl(devpriv->iobase +
+ 112) << 16);
+ }
+ } else {
+ /************************/
+ /* Config command error */
+ /************************/
+
+ printk("Channel mask error\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("Buffer size error\n");
+ i_ReturnValue = -101;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI3XXX_InsnReadTTLIO |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read the status from selected channel |
++----------------------------------------------------------------------------+
+| Input Parameters : b_Channel = CR_CHAN(insn->chanspec) |
++----------------------------------------------------------------------------+
+| Output Parameters : data[0] : Selected TTL channel state |
++----------------------------------------------------------------------------+
+| Return Value : 0 : No error |
+| -3 : Channel selection error |
+| -101 : Data size error |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3XXX_InsnReadTTLIO(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ BYTE b_Channel = (BYTE) CR_CHAN(insn->chanspec);
+ INT i_ReturnValue = insn->n;
+ unsigned int *pls_ReadData = data;
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n >= 1) {
+ /***********************/
+ /* Test if read port 0 */
+ /***********************/
+
+ if (b_Channel < 8) {
+ /*******************************************/
+ /* Read port 0 (first digital output port) */
+ /*******************************************/
+
+ pls_ReadData[0] = inl(devpriv->iobase + 80);
+ pls_ReadData[0] = (pls_ReadData[0] >> b_Channel) & 1;
+ } else {
+ /***********************/
+ /* Test if read port 1 */
+ /***********************/
+
+ if ((b_Channel > 7) && (b_Channel < 16)) {
+ /******************************************/
+ /* Read port 1 (first digital input port) */
+ /******************************************/
+
+ pls_ReadData[0] = inl(devpriv->iobase + 64);
+ pls_ReadData[0] =
+ (pls_ReadData[0] >> (b_Channel -
+ 8)) & 1;
+ } else {
+ /***********************/
+ /* Test if read port 2 */
+ /***********************/
+
+ if ((b_Channel > 15) && (b_Channel < 24)) {
+ /************************/
+ /* Test if port 2 input */
+ /************************/
+
+ if ((devpriv->ul_TTLPortConfiguration[0]
+ & 0xFF) == 0) {
+ pls_ReadData[0] =
+ inl(devpriv->iobase +
+ 96);
+ pls_ReadData[0] =
+ (pls_ReadData[0] >>
+ (b_Channel - 16)) & 1;
+ } else {
+ pls_ReadData[0] =
+ inl(devpriv->iobase +
+ 112);
+ pls_ReadData[0] =
+ (pls_ReadData[0] >>
+ (b_Channel - 16)) & 1;
+ }
+ } else {
+ /***************************/
+ /* Channel selection error */
+ /***************************/
+
+ i_ReturnValue = -3;
+ printk("Channel %d selection error\n",
+ b_Channel);
+ }
+ }
+ }
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("Buffer size error\n");
+ i_ReturnValue = -101;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| TTL OUTPUT FUNCTIONS |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : INT i_APCI3XXX_InsnWriteTTLIO |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Set the state from TTL output channel |
++----------------------------------------------------------------------------+
+| Input Parameters : b_Channel = CR_CHAN(insn->chanspec) |
+| b_State = data [0] |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : 0 : No error |
+| -3 : Channel selection error |
+| -101 : Data size error |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3XXX_InsnWriteTTLIO(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = insn->n;
+ BYTE b_Channel = (BYTE) CR_CHAN(insn->chanspec);
+ BYTE b_State = 0;
+ DWORD dw_Status = 0;
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n >= 1) {
+ b_State = (BYTE) data[0];
+
+ /***********************/
+ /* Test if read port 0 */
+ /***********************/
+
+ if (b_Channel < 8) {
+ /*****************************************************************************/
+ /* Read port 0 (first digital output port) and set/reset the selcted channel */
+ /*****************************************************************************/
+
+ dw_Status = inl(devpriv->iobase + 80);
+ dw_Status =
+ (dw_Status & (0xFF -
+ (1 << b_Channel))) | ((b_State & 1) <<
+ b_Channel);
+ outl(dw_Status, devpriv->iobase + 80);
+ } else {
+ /***********************/
+ /* Test if read port 2 */
+ /***********************/
+
+ if ((b_Channel > 15) && (b_Channel < 24)) {
+ /*************************/
+ /* Test if port 2 output */
+ /*************************/
+
+ if ((devpriv->ul_TTLPortConfiguration[0] & 0xFF)
+ == 0xFF) {
+ /*****************************************************************************/
+ /* Read port 2 (first digital output port) and set/reset the selcted channel */
+ /*****************************************************************************/
+
+ dw_Status = inl(devpriv->iobase + 112);
+ dw_Status =
+ (dw_Status & (0xFF -
+ (1 << (b_Channel -
+ 16)))) |
+ ((b_State & 1) << (b_Channel -
+ 16));
+ outl(dw_Status, devpriv->iobase + 112);
+ } else {
+ /***************************/
+ /* Channel selection error */
+ /***************************/
+
+ i_ReturnValue = -3;
+ printk("Channel %d selection error\n",
+ b_Channel);
+ }
+ } else {
+ /***************************/
+ /* Channel selection error */
+ /***************************/
+
+ i_ReturnValue = -3;
+ printk("Channel %d selection error\n",
+ b_Channel);
+ }
+ }
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("Buffer size error\n");
+ i_ReturnValue = -101;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| DIGITAL INPUT SUBDEVICE |
++----------------------------------------------------------------------------+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3XXX_InsnReadDigitalInput |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Reads the value of the specified Digital input channel |
++----------------------------------------------------------------------------+
+| Input Parameters : b_Channel = CR_CHAN(insn->chanspec) (0 to 3) |
++----------------------------------------------------------------------------+
+| Output Parameters : data[0] : Channel value |
++----------------------------------------------------------------------------+
+| Return Value : 0 : No error |
+| -3 : Channel selection error |
+| -101 : Data size error |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3XXX_InsnReadDigitalInput(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = insn->n;
+ BYTE b_Channel = (BYTE) CR_CHAN(insn->chanspec);
+ DWORD dw_Temp = 0;
+
+ /***************************/
+ /* Test the channel number */
+ /***************************/
+
+ if (b_Channel <= devpriv->ps_BoardInfo->i_NbrDiChannel) {
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n >= 1) {
+ dw_Temp = inl(devpriv->iobase + 32);
+ *data = (dw_Temp >> b_Channel) & 1;
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("Buffer size error\n");
+ i_ReturnValue = -101;
+ }
+ } else {
+ /***************************/
+ /* Channel selection error */
+ /***************************/
+
+ printk("Channel selection error\n");
+ i_ReturnValue = -3;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3XXX_InsnBitsDigitalInput |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Reads the value of the Digital input Port i.e.4channels|
++----------------------------------------------------------------------------+
+| Input Parameters : - |
++----------------------------------------------------------------------------+
+| Output Parameters : data[0] : Port value |
++----------------------------------------------------------------------------+
+| Return Value :>0: No error |
+| .... |
+| -101 : Data size error |
++----------------------------------------------------------------------------+
+*/
+int i_APCI3XXX_InsnBitsDigitalInput(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = insn->n;
+ DWORD dw_Temp = 0;
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n >= 1) {
+ dw_Temp = inl(devpriv->iobase + 32);
+ *data = dw_Temp & 0xf;
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("Buffer size error\n");
+ i_ReturnValue = -101;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| DIGITAL OUTPUT SUBDEVICE |
++----------------------------------------------------------------------------+
+
+*/
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3XXX_InsnBitsDigitalOutput |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Write the selected output mask and read the status from|
+| all digital output channles |
++----------------------------------------------------------------------------+
+| Input Parameters : dw_ChannelMask = data [0]; |
+| dw_BitMask = data [1]; |
++----------------------------------------------------------------------------+
+| Output Parameters : data[1] : All digital output channles states |
++----------------------------------------------------------------------------+
+| Return Value : >0 : No error |
+| -4 : Channel mask error |
+| -101 : Data size error |
++----------------------------------------------------------------------------+
+*/
+int i_APCI3XXX_InsnBitsDigitalOutput(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = insn->n;
+ BYTE b_ChannelCpt = 0;
+ DWORD dw_ChannelMask = 0;
+ DWORD dw_BitMask = 0;
+ DWORD dw_Status = 0;
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n >= 2) {
+ /*******************************/
+ /* Get the channe and bit mask */
+ /*******************************/
+
+ dw_ChannelMask = data[0];
+ dw_BitMask = data[1];
+
+ /*************************/
+ /* Test the channel mask */
+ /*************************/
+
+ if ((dw_ChannelMask & 0XFFFFFFF0) == 0) {
+ /*********************************/
+ /* Test if set/reset any channel */
+ /*********************************/
+
+ if (dw_ChannelMask & 0xF) {
+ /********************************/
+ /* Read the digital output port */
+ /********************************/
+
+ dw_Status = inl(devpriv->iobase + 48);
+
+ for (b_ChannelCpt = 0; b_ChannelCpt < 4;
+ b_ChannelCpt++) {
+ if ((dw_ChannelMask >> b_ChannelCpt) &
+ 1) {
+ dw_Status =
+ (dw_Status & (0xF -
+ (1 << b_ChannelCpt))) | (dw_BitMask & (1 << b_ChannelCpt));
+ }
+ }
+
+ outl(dw_Status, devpriv->iobase + 48);
+ }
+
+ /********************************/
+ /* Read the digital output port */
+ /********************************/
+
+ data[1] = inl(devpriv->iobase + 48);
+ } else {
+ /************************/
+ /* Config command error */
+ /************************/
+
+ printk("Channel mask error\n");
+ i_ReturnValue = -4;
+ }
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("Buffer size error\n");
+ i_ReturnValue = -101;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3XXX_InsnWriteDigitalOutput |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Set the state from digital output channel |
++----------------------------------------------------------------------------+
+| Input Parameters : b_Channel = CR_CHAN(insn->chanspec) |
+| b_State = data [0] |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : >0 : No error |
+| -3 : Channel selection error |
+| -101 : Data size error |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3XXX_InsnWriteDigitalOutput(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = insn->n;
+ BYTE b_Channel = CR_CHAN(insn->chanspec);
+ BYTE b_State = 0;
+ DWORD dw_Status = 0;
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n >= 1) {
+ /***************************/
+ /* Test the channel number */
+ /***************************/
+
+ if (b_Channel < devpriv->ps_BoardInfo->i_NbrDoChannel) {
+ /*******************/
+ /* Get the command */
+ /*******************/
+
+ b_State = (BYTE) data[0];
+
+ /********************************/
+ /* Read the digital output port */
+ /********************************/
+
+ dw_Status = inl(devpriv->iobase + 48);
+
+ dw_Status =
+ (dw_Status & (0xF -
+ (1 << b_Channel))) | ((b_State & 1) <<
+ b_Channel);
+ outl(dw_Status, devpriv->iobase + 48);
+ } else {
+ /***************************/
+ /* Channel selection error */
+ /***************************/
+
+ printk("Channel selection error\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("Buffer size error\n");
+ i_ReturnValue = -101;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function name :int i_APCI3XXX_InsnReadDigitalOutput |
+| (struct comedi_device *dev, |
+| struct comedi_subdevice *s, |
+| struct comedi_insn *insn, |
+| unsigned int *data) |
++----------------------------------------------------------------------------+
+| Task : Read the state from digital output channel |
++----------------------------------------------------------------------------+
+| Input Parameters : b_Channel = CR_CHAN(insn->chanspec) |
++----------------------------------------------------------------------------+
+| Output Parameters : b_State = data [0] |
++----------------------------------------------------------------------------+
+| Return Value : >0 : No error |
+| -3 : Channel selection error |
+| -101 : Data size error |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3XXX_InsnReadDigitalOutput(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ INT i_ReturnValue = insn->n;
+ BYTE b_Channel = CR_CHAN(insn->chanspec);
+ DWORD dw_Status = 0;
+
+ /************************/
+ /* Test the buffer size */
+ /************************/
+
+ if (insn->n >= 1) {
+ /***************************/
+ /* Test the channel number */
+ /***************************/
+
+ if (b_Channel < devpriv->ps_BoardInfo->i_NbrDoChannel) {
+ /********************************/
+ /* Read the digital output port */
+ /********************************/
+
+ dw_Status = inl(devpriv->iobase + 48);
+
+ dw_Status = (dw_Status >> b_Channel) & 1;
+ *data = dw_Status;
+ } else {
+ /***************************/
+ /* Channel selection error */
+ /***************************/
+
+ printk("Channel selection error\n");
+ i_ReturnValue = -3;
+ }
+ } else {
+ /*******************/
+ /* Data size error */
+ /*******************/
+
+ printk("Buffer size error\n");
+ i_ReturnValue = -101;
+ }
+
+ return (i_ReturnValue);
+}
+
+/*
++----------------------------------------------------------------------------+
+| Function Name : int i_APCI3XXX_Reset(struct comedi_device *dev) | +----------------------------------------------------------------------------+
+| Task :resets all the registers |
++----------------------------------------------------------------------------+
+| Input Parameters : struct comedi_device *dev |
++----------------------------------------------------------------------------+
+| Output Parameters : - |
++----------------------------------------------------------------------------+
+| Return Value : - |
++----------------------------------------------------------------------------+
+*/
+
+int i_APCI3XXX_Reset(struct comedi_device * dev)
+{
+ unsigned char b_Cpt = 0;
+
+ /*************************/
+ /* Disable the interrupt */
+ /*************************/
+
+ disable_irq(dev->irq);
+
+ /****************************/
+ /* Reset the interrupt flag */
+ /****************************/
+
+ devpriv->b_EocEosInterrupt = 0;
+
+ /***************************/
+ /* Clear the start command */
+ /***************************/
+
+ writel(0, (void *)(devpriv->dw_AiBase + 8));
+
+ /*****************************/
+ /* Reset the interrupt flags */
+ /*****************************/
+
+ writel(readl((void *)(devpriv->dw_AiBase + 16)),
+ (void *)(devpriv->dw_AiBase + 16));
+
+ /*****************/
+ /* clear the EOS */
+ /*****************/
+
+ readl((void *)(devpriv->dw_AiBase + 20));
+
+ /******************/
+ /* Clear the FIFO */
+ /******************/
+
+ for (b_Cpt = 0; b_Cpt < 16; b_Cpt++) {
+ readl((void *)(devpriv->dw_AiBase + 28));
+ }
+
+ /************************/
+ /* Enable the interrupt */
+ /************************/
+
+ enable_irq(dev->irq);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3xxx.h b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3xxx.h
new file mode 100644
index 000000000000..788d7c1cf5c1
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3xxx.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data-com
+ * info@addi-data.com
+ *
+ * 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.
+ */
+
+#ifndef COMEDI_SUBD_TTLIO
+#define COMEDI_SUBD_TTLIO 11 /* Digital Input Output But TTL */
+#endif
+
+#ifndef ADDIDATA_ENABLE
+#define ADDIDATA_ENABLE 1
+#define ADDIDATA_DISABLE 0
+#endif
+
+#define APCI3XXX_SINGLE 0
+#define APCI3XXX_DIFF 1
+#define APCI3XXX_CONFIGURATION 0
+
+#define APCI3XXX_TTL_INIT_DIRECTION_PORT2 0
+
+#ifdef __KERNEL__
+
+static const struct comedi_lrange range_apci3XXX_ai = { 8, {BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1)}
+};
+
+static const struct comedi_lrange range_apci3XXX_ttl = { 12, {BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1),
+ BIP_RANGE(1)}
+};
+
+static const struct comedi_lrange range_apci3XXX_ao = { 2, {BIP_RANGE(10),
+ UNI_RANGE(10)}
+};
+#endif
diff --git a/drivers/staging/comedi/drivers/addi_apci_035.c b/drivers/staging/comedi/drivers/addi_apci_035.c
new file mode 100644
index 000000000000..bac018202fe8
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_035.c
@@ -0,0 +1,5 @@
+#define CONFIG_APCI_035 1
+
+#define ADDIDATA_WATCHDOG 2 // Or shold it be something else
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_1032.c b/drivers/staging/comedi/drivers/addi_apci_1032.c
new file mode 100644
index 000000000000..fa2056e8aa0e
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_1032.c
@@ -0,0 +1,3 @@
+#define CONFIG_APCI_1032 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_1500.c b/drivers/staging/comedi/drivers/addi_apci_1500.c
new file mode 100644
index 000000000000..7a5cae599ef8
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_1500.c
@@ -0,0 +1,3 @@
+#define CONFIG_APCI_1500 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_1516.c b/drivers/staging/comedi/drivers/addi_apci_1516.c
new file mode 100644
index 000000000000..8d414844009f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_1516.c
@@ -0,0 +1,3 @@
+#define CONFIG_APCI_1516 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_1564.c b/drivers/staging/comedi/drivers/addi_apci_1564.c
new file mode 100644
index 000000000000..0351cdde1026
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_1564.c
@@ -0,0 +1,3 @@
+#define CONFIG_APCI_1564 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_16xx.c b/drivers/staging/comedi/drivers/addi_apci_16xx.c
new file mode 100644
index 000000000000..506799041294
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_16xx.c
@@ -0,0 +1,3 @@
+#define CONFIG_APCI_16XX 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_1710.c b/drivers/staging/comedi/drivers/addi_apci_1710.c
new file mode 100644
index 000000000000..c433445913dd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_1710.c
@@ -0,0 +1,3 @@
+#define CONFIG_APCI_1710 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_2016.c b/drivers/staging/comedi/drivers/addi_apci_2016.c
new file mode 100644
index 000000000000..271c47c8cad3
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_2016.c
@@ -0,0 +1,3 @@
+#define CONFIG_APCI_2016 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_2032.c b/drivers/staging/comedi/drivers/addi_apci_2032.c
new file mode 100644
index 000000000000..5108ea2a3924
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_2032.c
@@ -0,0 +1,3 @@
+#define CONFIG_APCI_2032 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_2200.c b/drivers/staging/comedi/drivers/addi_apci_2200.c
new file mode 100644
index 000000000000..e439f835cf4f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_2200.c
@@ -0,0 +1,3 @@
+#define CONFIG_APCI_2200 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_3001.c b/drivers/staging/comedi/drivers/addi_apci_3001.c
new file mode 100644
index 000000000000..df97c305828b
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_3001.c
@@ -0,0 +1,3 @@
+#define CONFIG_APCI_3001 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_3120.c b/drivers/staging/comedi/drivers/addi_apci_3120.c
new file mode 100644
index 000000000000..9183125ddde4
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_3120.c
@@ -0,0 +1,3 @@
+#define CONFIG_APCI_3120 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_3200.c b/drivers/staging/comedi/drivers/addi_apci_3200.c
new file mode 100644
index 000000000000..f25a70b3290b
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_3200.c
@@ -0,0 +1,3 @@
+#define CONFIG_APCI_3200 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_3300.c b/drivers/staging/comedi/drivers/addi_apci_3300.c
new file mode 100644
index 000000000000..1ee4778ad45b
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_3300.c
@@ -0,0 +1,3 @@
+#define CONFIG_APCI_3300 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_3501.c b/drivers/staging/comedi/drivers/addi_apci_3501.c
new file mode 100644
index 000000000000..1049e20237e8
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_3501.c
@@ -0,0 +1,3 @@
+#define CONFIG_APCI_3501 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_3xxx.c b/drivers/staging/comedi/drivers/addi_apci_3xxx.c
new file mode 100644
index 000000000000..fb9deb7083bd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_3xxx.c
@@ -0,0 +1,3 @@
+#define CONFIG_APCI_3XXX 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/addi_apci_all.c b/drivers/staging/comedi/drivers/addi_apci_all.c
new file mode 100644
index 000000000000..aeb1b2688f3d
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_all.c
@@ -0,0 +1,18 @@
+#define CONFIG_APCI_035 1
+#define CONFIG_APCI_1032 1
+#define CONFIG_APCI_1500 1
+#define CONFIG_APCI_1516 1
+#define CONFIG_APCI_1564 1
+#define CONFIG_APCI_16XX 1
+#define CONFIG_APCI_1710 1
+#define CONFIG_APCI_2016 1
+#define CONFIG_APCI_2032 1
+#define CONFIG_APCI_2200 1
+#define CONFIG_APCI_3001 1
+#define CONFIG_APCI_3120 1
+#define CONFIG_APCI_3200 1
+#define CONFIG_APCI_3300 1
+#define CONFIG_APCI_3501 1
+#define CONFIG_APCI_3XXX 1
+
+#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/adl_pci6208.c b/drivers/staging/comedi/drivers/adl_pci6208.c
new file mode 100644
index 000000000000..f710f551820d
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adl_pci6208.c
@@ -0,0 +1,391 @@
+/*
+ comedi/drivers/adl_pci6208.c
+
+ Hardware driver for ADLink 6208 series cards:
+ card | voltage output | current output
+ -------------+-------------------+---------------
+ PCI-6208V | 8 channels | -
+ PCI-6216V | 16 channels | -
+ PCI-6208A | 8 channels | 8 channels
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+Driver: adl_pci6208
+Description: ADLink PCI-6208A
+Devices: [ADLink] PCI-6208A (adl_pci6208)
+Author: nsyeow <nsyeow@pd.jaring.my>
+Updated: Fri, 30 Jan 2004 14:44:27 +0800
+Status: untested
+
+Configuration Options:
+ none
+
+References:
+ - ni_660x.c
+ - adl_pci9111.c copied the entire pci setup section
+ - adl_pci9118.c
+*/
+/*
+ * These headers should be followed by a blank line, and any comments
+ * you wish to say about the driver. The comment area is the place
+ * to put any known bugs, limitations, unsupported features, supported
+ * command triggers, whether or not commands are supported on particular
+ * subdevices, etc.
+ *
+ * Somewhere in the comment should be information about configuration
+ * options that are used with comedi_config.
+ */
+#include "../comedidev.h"
+#include "comedi_pci.h"
+
+#define PCI6208_DRIVER_NAME "adl_pci6208"
+
+/* Board descriptions */
+struct pci6208_board {
+ const char *name;
+ unsigned short dev_id; /* `lspci` will show you this */
+ int ao_chans;
+ //int ao_bits;
+};
+
+static const struct pci6208_board pci6208_boards[] = {
+ /*{
+ name : "pci6208v",
+ dev_id : 0x6208, //not sure
+ ao_chans: 8
+ //, ao_bits : 16
+ },
+ {
+ name : "pci6216v",
+ dev_id : 0x6208, //not sure
+ ao_chans: 16
+ //, ao_bits : 16
+ }, */
+ {
+ name: "pci6208a",
+ dev_id: 0x6208,
+ ao_chans:8
+ //, ao_bits : 16
+ }
+};
+
+/* This is used by modprobe to translate PCI IDs to drivers. Should
+ * only be used for PCI and ISA-PnP devices */
+static DEFINE_PCI_DEVICE_TABLE(pci6208_pci_table) = {
+ //{ PCI_VENDOR_ID_ADLINK, 0x6208, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ //{ PCI_VENDOR_ID_ADLINK, 0x6208, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ {PCI_VENDOR_ID_ADLINK, 0x6208, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, pci6208_pci_table);
+
+/* Will be initialized in pci6208_find device(). */
+#define thisboard ((const struct pci6208_board *)dev->board_ptr)
+
+struct pci6208_private {
+ int data;
+ struct pci_dev *pci_dev; /* for a PCI device */
+ unsigned int ao_readback[2]; /* Used for AO readback */
+};
+
+#define devpriv ((struct pci6208_private *)dev->private)
+
+static int pci6208_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pci6208_detach(struct comedi_device * dev);
+
+#define pci6208_board_nbr \
+ (sizeof(pci6208_boards) / sizeof(struct pci6208_board))
+
+static struct comedi_driver driver_pci6208 = {
+ driver_name:PCI6208_DRIVER_NAME,
+ module:THIS_MODULE,
+ attach:pci6208_attach,
+ detach:pci6208_detach,
+};
+
+COMEDI_PCI_INITCLEANUP(driver_pci6208, pci6208_pci_table);
+
+static int pci6208_find_device(struct comedi_device * dev, int bus, int slot);
+static int
+pci6208_pci_setup(struct pci_dev *pci_dev, unsigned long *io_base_ptr,
+ int dev_minor);
+
+/*read/write functions*/
+static int pci6208_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int pci6208_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+//static int pci6208_dio_insn_bits(struct comedi_device *dev,struct comedi_subdevice *s,
+// struct comedi_insn *insn,unsigned int *data);
+//static int pci6208_dio_insn_config(struct comedi_device *dev,struct comedi_subdevice *s,
+// struct comedi_insn *insn,unsigned int *data);
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int pci6208_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int retval;
+ unsigned long io_base;
+
+ printk("comedi%d: pci6208: ", dev->minor);
+
+ retval = alloc_private(dev, sizeof(struct pci6208_private));
+ if (retval < 0)
+ return retval;
+
+ retval = pci6208_find_device(dev, it->options[0], it->options[1]);
+ if (retval < 0)
+ return retval;
+
+ retval = pci6208_pci_setup(devpriv->pci_dev, &io_base, dev->minor);
+ if (retval < 0)
+ return retval;
+
+ dev->iobase = io_base;
+ dev->board_name = thisboard->name;
+
+/*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_subdevices(dev, 2) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE; //anything else to add here??
+ s->n_chan = thisboard->ao_chans;
+ s->maxdata = 0xffff; //16-bit DAC
+ s->range_table = &range_bipolar10; //this needs to be checked.
+ s->insn_write = pci6208_ao_winsn;
+ s->insn_read = pci6208_ao_rinsn;
+
+ //s=dev->subdevices+1;
+ /* digital i/o subdevice */
+ //s->type=COMEDI_SUBD_DIO;
+ //s->subdev_flags=SDF_READABLE|SDF_WRITABLE;
+ //s->n_chan=16;
+ //s->maxdata=1;
+ //s->range_table=&range_digital;
+ //s->insn_bits = pci6208_dio_insn_bits;
+ //s->insn_config = pci6208_dio_insn_config;
+
+ printk("attached\n");
+
+ return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int pci6208_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: pci6208: remove\n", dev->minor);
+
+ if (devpriv && devpriv->pci_dev) {
+ if (dev->iobase) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ }
+
+ return 0;
+}
+
+static int pci6208_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i = 0, Data_Read;
+ unsigned short chan = CR_CHAN(insn->chanspec);
+ unsigned long invert = 1 << (16 - 1);
+ unsigned long out_value;
+ /* Writing a list of values to an AO channel is probably not
+ * very useful, but that's how the interface is defined. */
+ for (i = 0; i < insn->n; i++) {
+ out_value = data[i] ^ invert;
+ /* a typical programming sequence */
+ do {
+ Data_Read = (inw(dev->iobase) & 1);
+ } while (Data_Read);
+ outw(out_value, dev->iobase + (0x02 * chan));
+ devpriv->ao_readback[chan] = out_value;
+ }
+
+ /* return the number of samples read/written */
+ return i;
+}
+
+/* AO subdevices should have a read insn as well as a write insn.
+ * Usually this means copying a value stored in devpriv. */
+static int pci6208_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = devpriv->ao_readback[chan];
+
+ return i;
+}
+
+/* DIO devices are slightly special. Although it is possible to
+ * implement the insn_read/insn_write interface, it is much more
+ * useful to applications if you implement the insn_bits interface.
+ * This allows packed reading/writing of the DIO channels. The
+ * comedi core can convert between insn_bits and insn_read/write */
+//static int pci6208_dio_insn_bits(struct comedi_device *dev,struct comedi_subdevice *s,
+// struct comedi_insn *insn,unsigned int *data)
+//{
+// if(insn->n!=2)return -EINVAL;
+
+ /* The insn data is a mask in data[0] and the new data
+ * in data[1], each channel cooresponding to a bit. */
+// if(data[0]){
+// s->state &= ~data[0];
+// s->state |= data[0]&data[1];
+ /* Write out the new digital output lines */
+ //outw(s->state,dev->iobase + SKEL_DIO);
+// }
+
+ /* on return, data[1] contains the value of the digital
+ * input and output lines. */
+ //data[1]=inw(dev->iobase + SKEL_DIO);
+ /* or we could just return the software copy of the output values if
+ * it was a purely digital output subdevice */
+ //data[1]=s->state;
+
+// return 2;
+//}
+
+//static int pci6208_dio_insn_config(struct comedi_device *dev,struct comedi_subdevice *s,
+// struct comedi_insn *insn,unsigned int *data)
+//{
+// int chan=CR_CHAN(insn->chanspec);
+
+ /* The input or output configuration of each digital line is
+ * configured by a special insn_config instruction. chanspec
+ * contains the channel to be changed, and data[0] contains the
+ * value COMEDI_INPUT or COMEDI_OUTPUT. */
+
+// if(data[0]==COMEDI_OUTPUT){
+// s->io_bits |= 1<<chan;
+// }else{
+// s->io_bits &= ~(1<<chan);
+// }
+ //outw(s->io_bits,dev->iobase + SKEL_DIO_CONFIG);
+
+// return 1;
+//}
+
+static int pci6208_find_device(struct comedi_device * dev, int bus, int slot)
+{
+ struct pci_dev *pci_dev;
+ int i;
+
+ for (pci_dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pci_dev != NULL;
+ pci_dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) {
+ if (pci_dev->vendor == PCI_VENDOR_ID_ADLINK) {
+ for (i = 0; i < pci6208_board_nbr; i++) {
+ if (pci6208_boards[i].dev_id == pci_dev->device) {
+ // was a particular bus/slot requested?
+ if ((bus != 0) || (slot != 0)) {
+ // are we on the wrong bus/slot?
+ if (pci_dev->bus->number
+ != bus ||
+ PCI_SLOT(pci_dev->devfn)
+ != slot) {
+ continue;
+ }
+ }
+ dev->board_ptr = pci6208_boards + i;
+ goto found;
+ }
+ }
+ }
+ }
+
+ printk("comedi%d: no supported board found! (req. bus/slot : %d/%d)\n",
+ dev->minor, bus, slot);
+ return -EIO;
+
+ found:
+ printk("comedi%d: found %s (b:s:f=%d:%d:%d) , irq=%d\n",
+ dev->minor,
+ pci6208_boards[i].name,
+ pci_dev->bus->number,
+ PCI_SLOT(pci_dev->devfn),
+ PCI_FUNC(pci_dev->devfn), pci_dev->irq);
+
+ // TODO: Warn about non-tested boards.
+ //switch(board->device_id)
+ //{
+ //};
+
+ devpriv->pci_dev = pci_dev;
+
+ return 0;
+}
+
+static int
+pci6208_pci_setup(struct pci_dev *pci_dev, unsigned long *io_base_ptr,
+ int dev_minor)
+{
+ unsigned long io_base, io_range, lcr_io_base, lcr_io_range;
+
+ // Enable PCI device and request regions
+ if (comedi_pci_enable(pci_dev, PCI6208_DRIVER_NAME) < 0) {
+ printk("comedi%d: Failed to enable PCI device and request regions\n", dev_minor);
+ return -EIO;
+ }
+ // Read local configuration register base address [PCI_BASE_ADDRESS #1].
+ lcr_io_base = pci_resource_start(pci_dev, 1);
+ lcr_io_range = pci_resource_len(pci_dev, 1);
+
+ printk("comedi%d: local config registers at address 0x%4lx [0x%4lx]\n",
+ dev_minor, lcr_io_base, lcr_io_range);
+
+ // Read PCI6208 register base address [PCI_BASE_ADDRESS #2].
+ io_base = pci_resource_start(pci_dev, 2);
+ io_range = pci_resource_end(pci_dev, 2) - io_base + 1;
+
+ printk("comedi%d: 6208 registers at address 0x%4lx [0x%4lx]\n",
+ dev_minor, io_base, io_range);
+
+ *io_base_ptr = io_base;
+ //devpriv->io_range = io_range;
+ //devpriv->is_valid=0;
+ //devpriv->lcr_io_base=lcr_io_base;
+ //devpriv->lcr_io_range=lcr_io_range;
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/adl_pci7296.c b/drivers/staging/comedi/drivers/adl_pci7296.c
new file mode 100644
index 000000000000..bd0f9ab226ef
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adl_pci7296.c
@@ -0,0 +1,174 @@
+/*
+ comedi/drivers/adl_pci7296.c
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: adl_pci7296
+Description: Driver for the Adlink PCI-7296 96 ch. digital io board
+Devices: [ADLink] PCI-7296 (adl_pci7296)
+Author: Jon Grierson <jd@renko.co.uk>
+Updated: Mon, 14 Apr 2008 15:05:56 +0100
+Status: testing
+
+Configuration Options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first supported
+ PCI device found will be used.
+*/
+
+#include "../comedidev.h"
+#include <linux/kernel.h>
+
+#include "comedi_pci.h"
+#include "8255.h"
+// #include "8253.h"
+
+#define PORT1A 0
+#define PORT2A 4
+#define PORT3A 8
+#define PORT4A 12
+
+#define PCI_DEVICE_ID_PCI7296 0x7296
+
+static DEFINE_PCI_DEVICE_TABLE(adl_pci7296_pci_table) = {
+ {PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7296, PCI_ANY_ID, PCI_ANY_ID, 0,
+ 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, adl_pci7296_pci_table);
+
+struct adl_pci7296_private {
+ int data;
+ struct pci_dev *pci_dev;
+};
+
+
+#define devpriv ((struct adl_pci7296_private *)dev->private)
+
+static int adl_pci7296_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int adl_pci7296_detach(struct comedi_device * dev);
+static struct comedi_driver driver_adl_pci7296 = {
+ driver_name:"adl_pci7296",
+ module:THIS_MODULE,
+ attach:adl_pci7296_attach,
+ detach:adl_pci7296_detach,
+};
+
+static int adl_pci7296_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct pci_dev *pcidev;
+ struct comedi_subdevice *s;
+ int bus, slot;
+ int ret;
+
+ printk("comedi: attempt to attach...\n");
+ printk("comedi%d: adl_pci7432\n", dev->minor);
+
+ dev->board_name = "pci7432";
+ bus = it->options[0];
+ slot = it->options[1];
+
+ if (alloc_private(dev, sizeof(struct adl_pci7296_private)) < 0)
+ return -ENOMEM;
+
+ if (alloc_subdevices(dev, 4) < 0)
+ return -ENOMEM;
+
+ for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pcidev != NULL;
+ pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
+
+ if (pcidev->vendor == PCI_VENDOR_ID_ADLINK &&
+ pcidev->device == PCI_DEVICE_ID_PCI7296) {
+ if (bus || slot) {
+ /* requested particular bus/slot */
+ if (pcidev->bus->number != bus
+ || PCI_SLOT(pcidev->devfn) != slot) {
+ continue;
+ }
+ }
+ devpriv->pci_dev = pcidev;
+ if (comedi_pci_enable(pcidev, "adl_pci7296") < 0) {
+ printk("comedi%d: Failed to enable PCI device and request regions\n", dev->minor);
+ return -EIO;
+ }
+
+ dev->iobase = pci_resource_start(pcidev, 2);
+ printk("comedi: base addr %4lx\n", dev->iobase);
+
+ // four 8255 digital io subdevices
+ s = dev->subdevices + 0;
+ subdev_8255_init(dev, s, NULL,
+ (unsigned long)(dev->iobase));
+
+ s = dev->subdevices + 1;
+ ret = subdev_8255_init(dev, s, NULL,
+ (unsigned long)(dev->iobase + PORT2A));
+ if (ret < 0)
+ return ret;
+
+ s = dev->subdevices + 2;
+ ret = subdev_8255_init(dev, s, NULL,
+ (unsigned long)(dev->iobase + PORT3A));
+ if (ret < 0)
+ return ret;
+
+ s = dev->subdevices + 3;
+ ret = subdev_8255_init(dev, s, NULL,
+ (unsigned long)(dev->iobase + PORT4A));
+ if (ret < 0)
+ return ret;
+
+ printk("attached\n");
+
+ return 1;
+ }
+ }
+
+ printk("comedi%d: no supported board found! (req. bus/slot : %d/%d)\n",
+ dev->minor, bus, slot);
+ return -EIO;
+}
+
+static int adl_pci7296_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: pci7432: remove\n", dev->minor);
+
+ if (devpriv && devpriv->pci_dev) {
+ if (dev->iobase) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ }
+ // detach four 8255 digital io subdevices
+ if (dev->subdevices) {
+ subdev_8255_cleanup(dev, dev->subdevices + 0);
+ subdev_8255_cleanup(dev, dev->subdevices + 1);
+ subdev_8255_cleanup(dev, dev->subdevices + 2);
+ subdev_8255_cleanup(dev, dev->subdevices + 3);
+
+ }
+
+ return 0;
+}
+
+COMEDI_PCI_INITCLEANUP(driver_adl_pci7296, adl_pci7296_pci_table);
diff --git a/drivers/staging/comedi/drivers/adl_pci7432.c b/drivers/staging/comedi/drivers/adl_pci7432.c
new file mode 100644
index 000000000000..a8f1715608d1
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adl_pci7432.c
@@ -0,0 +1,202 @@
+/*
+ comedi/drivers/adl_pci7432.c
+
+ Hardware comedi driver fot PCI7432 Adlink card
+ Copyright (C) 2004 Michel Lachine <mike@mikelachaine.ca>
+
+ 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.
+
+*/
+/*
+Driver: adl_pci7432
+Description: Driver for the Adlink PCI-7432 64 ch. isolated digital io board
+Devices: [ADLink] PCI-7432 (adl_pci7432)
+Author: Michel Lachaine <mike@mikelachaine.ca>
+Status: experimental
+Updated: Mon, 14 Apr 2008 15:08:14 +0100
+
+Configuration Options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first supported
+ PCI device found will be used.
+*/
+
+#include "../comedidev.h"
+#include <linux/kernel.h>
+#include "comedi_pci.h"
+
+#define PCI7432_DI 0x00
+#define PCI7432_DO 0x00
+
+#define PCI_DEVICE_ID_PCI7432 0x7432
+
+static DEFINE_PCI_DEVICE_TABLE(adl_pci7432_pci_table) = {
+ {PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7432, PCI_ANY_ID, PCI_ANY_ID, 0,
+ 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, adl_pci7432_pci_table);
+
+struct adl_pci7432_private {
+ int data;
+ struct pci_dev *pci_dev;
+};
+
+#define devpriv ((struct adl_pci7432_private *)dev->private)
+
+static int adl_pci7432_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int adl_pci7432_detach(struct comedi_device * dev);
+static struct comedi_driver driver_adl_pci7432 = {
+ driver_name:"adl_pci7432",
+ module:THIS_MODULE,
+ attach:adl_pci7432_attach,
+ detach:adl_pci7432_detach,
+};
+
+/* Digital IO */
+
+static int adl_pci7432_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int adl_pci7432_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+/* */
+
+static int adl_pci7432_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct pci_dev *pcidev;
+ struct comedi_subdevice *s;
+ int bus, slot;
+
+ printk("comedi: attempt to attach...\n");
+ printk("comedi%d: adl_pci7432\n", dev->minor);
+
+ dev->board_name = "pci7432";
+ bus = it->options[0];
+ slot = it->options[1];
+
+ if (alloc_private(dev, sizeof(struct adl_pci7432_private)) < 0)
+ return -ENOMEM;
+
+ if (alloc_subdevices(dev, 2) < 0)
+ return -ENOMEM;
+
+ for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pcidev != NULL;
+ pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
+
+ if (pcidev->vendor == PCI_VENDOR_ID_ADLINK &&
+ pcidev->device == PCI_DEVICE_ID_PCI7432) {
+ if (bus || slot) {
+ /* requested particular bus/slot */
+ if (pcidev->bus->number != bus
+ || PCI_SLOT(pcidev->devfn) != slot) {
+ continue;
+ }
+ }
+ devpriv->pci_dev = pcidev;
+ if (comedi_pci_enable(pcidev, "adl_pci7432") < 0) {
+ printk("comedi%d: Failed to enable PCI device and request regions\n", dev->minor);
+ return -EIO;
+ }
+ dev->iobase = pci_resource_start(pcidev, 2);
+ printk("comedi: base addr %4lx\n", dev->iobase);
+
+ s = dev->subdevices + 0;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags =
+ SDF_READABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 32;
+ s->maxdata = 1;
+ s->len_chanlist = 32;
+ s->io_bits = 0x00000000;
+ s->range_table = &range_digital;
+ s->insn_bits = adl_pci7432_di_insn_bits;
+
+ s = dev->subdevices + 1;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags =
+ SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 32;
+ s->maxdata = 1;
+ s->len_chanlist = 32;
+ s->io_bits = 0xffffffff;
+ s->range_table = &range_digital;
+ s->insn_bits = adl_pci7432_do_insn_bits;
+
+ printk("comedi: attached\n");
+
+ return 1;
+ }
+ }
+
+ printk("comedi%d: no supported board found! (req. bus/slot : %d/%d)\n",
+ dev->minor, bus, slot);
+ return -EIO;
+}
+
+static int adl_pci7432_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: pci7432: remove\n", dev->minor);
+
+ if (devpriv && devpriv->pci_dev) {
+ if (dev->iobase) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ }
+
+ return 0;
+}
+
+static int adl_pci7432_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ printk("comedi: pci7432_do_insn_bits called\n");
+ printk("comedi: data0: %8x data1: %8x\n", data[0], data[1]);
+
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+
+ printk("comedi: out: %8x on iobase %4lx\n", s->state,
+ dev->iobase + PCI7432_DO);
+ outl(s->state & 0xffffffff, dev->iobase + PCI7432_DO);
+ }
+ return 2;
+}
+
+static int adl_pci7432_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ printk("comedi: pci7432_di_insn_bits called\n");
+ printk("comedi: data0: %8x data1: %8x\n", data[0], data[1]);
+
+ if (insn->n != 2)
+ return -EINVAL;
+
+ data[1] = inl(dev->iobase + PCI7432_DI) & 0xffffffff;
+ printk("comedi: data1 %8x\n", data[1]);
+
+ return 2;
+}
+
+COMEDI_PCI_INITCLEANUP(driver_adl_pci7432, adl_pci7432_pci_table);
diff --git a/drivers/staging/comedi/drivers/adl_pci8164.c b/drivers/staging/comedi/drivers/adl_pci8164.c
new file mode 100644
index 000000000000..adf90be79983
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adl_pci8164.c
@@ -0,0 +1,512 @@
+/*
+ comedi/drivers/adl_pci8164.c
+
+ Hardware comedi driver fot PCI-8164 Adlink card
+ Copyright (C) 2004 Michel Lachine <mike@mikelachaine.ca>
+
+ 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.
+
+*/
+/*
+Driver: adl_pci8164
+Description: Driver for the Adlink PCI-8164 4 Axes Motion Control board
+Devices: [ADLink] PCI-8164 (adl_pci8164)
+Author: Michel Lachaine <mike@mikelachaine.ca>
+Status: experimental
+Updated: Mon, 14 Apr 2008 15:10:32 +0100
+
+Configuration Options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first supported
+ PCI device found will be used.
+*/
+
+#include "../comedidev.h"
+#include <linux/delay.h>
+#include "comedi_fc.h"
+#include "comedi_pci.h"
+#include "8253.h"
+
+#define PCI8164_AXIS_X 0x00
+#define PCI8164_AXIS_Y 0x08
+#define PCI8164_AXIS_Z 0x10
+#define PCI8164_AXIS_U 0x18
+
+#define PCI8164_MSTS 0x00
+#define PCI8164_SSTS 0x02
+#define PCI8164_BUF0 0x04
+#define PCI8164_BUF1 0x06
+
+#define PCI8164_CMD 0x00
+#define PCI8164_OTP 0x02
+
+#define PCI_DEVICE_ID_PCI8164 0x8164
+
+static DEFINE_PCI_DEVICE_TABLE(adl_pci8164_pci_table) = {
+ {PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI8164, PCI_ANY_ID, PCI_ANY_ID, 0,
+ 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, adl_pci8164_pci_table);
+
+struct adl_pci8164_private {
+ int data;
+ struct pci_dev *pci_dev;
+};
+
+#define devpriv ((struct adl_pci8164_private *)dev->private)
+
+static int adl_pci8164_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int adl_pci8164_detach(struct comedi_device * dev);
+static struct comedi_driver driver_adl_pci8164 = {
+ driver_name:"adl_pci8164",
+ module:THIS_MODULE,
+ attach:adl_pci8164_attach,
+ detach:adl_pci8164_detach,
+};
+
+static int adl_pci8164_insn_read_msts(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int adl_pci8164_insn_read_ssts(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int adl_pci8164_insn_read_buf0(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int adl_pci8164_insn_read_buf1(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int adl_pci8164_insn_write_cmd(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int adl_pci8164_insn_write_otp(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int adl_pci8164_insn_write_buf0(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data);
+
+static int adl_pci8164_insn_write_buf1(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data);
+
+static int adl_pci8164_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct pci_dev *pcidev;
+ struct comedi_subdevice *s;
+ int bus, slot;
+
+ printk("comedi: attempt to attach...\n");
+ printk("comedi%d: adl_pci8164\n", dev->minor);
+
+ dev->board_name = "pci8164";
+ bus = it->options[0];
+ slot = it->options[1];
+
+ if (alloc_private(dev, sizeof(struct adl_pci8164_private)) < 0)
+ return -ENOMEM;
+
+ if (alloc_subdevices(dev, 4) < 0)
+ return -ENOMEM;
+
+ for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pcidev != NULL;
+ pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
+
+ if (pcidev->vendor == PCI_VENDOR_ID_ADLINK &&
+ pcidev->device == PCI_DEVICE_ID_PCI8164) {
+ if (bus || slot) {
+ /* requested particular bus/slot */
+ if (pcidev->bus->number != bus
+ || PCI_SLOT(pcidev->devfn) != slot) {
+ continue;
+ }
+ }
+ devpriv->pci_dev = pcidev;
+ if (comedi_pci_enable(pcidev, "adl_pci8164") < 0) {
+ printk("comedi%d: Failed to enable PCI device and request regions\n", dev->minor);
+ return -EIO;
+ }
+ dev->iobase = pci_resource_start(pcidev, 2);
+ printk("comedi: base addr %4lx\n", dev->iobase);
+
+ s = dev->subdevices + 0;
+ s->type = COMEDI_SUBD_PROC;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 0xffff;
+ s->len_chanlist = 4;
+ //s->range_table = &range_axis;
+ s->insn_read = adl_pci8164_insn_read_msts;
+ s->insn_write = adl_pci8164_insn_write_cmd;
+
+ s = dev->subdevices + 1;
+ s->type = COMEDI_SUBD_PROC;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 0xffff;
+ s->len_chanlist = 4;
+ //s->range_table = &range_axis;
+ s->insn_read = adl_pci8164_insn_read_ssts;
+ s->insn_write = adl_pci8164_insn_write_otp;
+
+ s = dev->subdevices + 2;
+ s->type = COMEDI_SUBD_PROC;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 0xffff;
+ s->len_chanlist = 4;
+ //s->range_table = &range_axis;
+ s->insn_read = adl_pci8164_insn_read_buf0;
+ s->insn_write = adl_pci8164_insn_write_buf0;
+
+ s = dev->subdevices + 3;
+ s->type = COMEDI_SUBD_PROC;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 0xffff;
+ s->len_chanlist = 4;
+ //s->range_table = &range_axis;
+ s->insn_read = adl_pci8164_insn_read_buf1;
+ s->insn_write = adl_pci8164_insn_write_buf1;
+
+ printk("comedi: attached\n");
+
+ return 1;
+ }
+ }
+
+ printk("comedi%d: no supported board found! (req. bus/slot : %d/%d)\n",
+ dev->minor, bus, slot);
+ return -EIO;
+}
+
+static int adl_pci8164_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: pci8164: remove\n", dev->minor);
+
+ if (devpriv && devpriv->pci_dev) {
+ if (dev->iobase) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ }
+
+ return 0;
+}
+
+static int adl_pci8164_insn_read_msts(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int axis, axis_reg;
+ char *axisname;
+
+ axis = CR_CHAN(insn->chanspec);
+
+ switch (axis) {
+ case 0:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ break;
+ case 1:
+ axis_reg = PCI8164_AXIS_Y;
+ axisname = "Y";
+ break;
+ case 2:
+ axis_reg = PCI8164_AXIS_Z;
+ axisname = "Z";
+ break;
+ case 3:
+ axis_reg = PCI8164_AXIS_U;
+ axisname = "U";
+ break;
+ default:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ }
+
+ data[0] = inw(dev->iobase + axis_reg + PCI8164_MSTS);
+ printk("comedi: pci8164 MSTS read -> %04X:%04X on axis %s\n", data[0],
+ data[1], axisname);
+
+ return 2;
+}
+
+static int adl_pci8164_insn_read_ssts(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int axis, axis_reg;
+ char *axisname;
+
+ axis = CR_CHAN(insn->chanspec);
+
+ switch (axis) {
+ case 0:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ break;
+ case 1:
+ axis_reg = PCI8164_AXIS_Y;
+ axisname = "Y";
+ break;
+ case 2:
+ axis_reg = PCI8164_AXIS_Z;
+ axisname = "Z";
+ break;
+ case 3:
+ axis_reg = PCI8164_AXIS_U;
+ axisname = "U";
+ break;
+ default:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ }
+
+ data[0] = inw(dev->iobase + axis_reg + PCI8164_SSTS);
+ printk("comedi: pci8164 SSTS read -> %04X:%04X on axis %s\n", data[0],
+ data[1], axisname);
+
+ return 2;
+}
+
+static int adl_pci8164_insn_read_buf0(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int axis, axis_reg;
+ char *axisname;
+
+ axis = CR_CHAN(insn->chanspec);
+
+ switch (axis) {
+ case 0:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ break;
+ case 1:
+ axis_reg = PCI8164_AXIS_Y;
+ axisname = "Y";
+ break;
+ case 2:
+ axis_reg = PCI8164_AXIS_Z;
+ axisname = "Z";
+ break;
+ case 3:
+ axis_reg = PCI8164_AXIS_U;
+ axisname = "U";
+ break;
+ default:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ }
+
+ data[0] = inw(dev->iobase + axis_reg + PCI8164_BUF0);
+ printk("comedi: pci8164 BUF0 read -> %04X:%04X on axis %s\n", data[0],
+ data[1], axisname);
+
+ return 2;
+}
+
+static int adl_pci8164_insn_read_buf1(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int axis, axis_reg;
+
+ char *axisname;
+
+ axis = CR_CHAN(insn->chanspec);
+
+ switch (axis) {
+ case 0:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ break;
+ case 1:
+ axis_reg = PCI8164_AXIS_Y;
+ axisname = "Y";
+ break;
+ case 2:
+ axis_reg = PCI8164_AXIS_Z;
+ axisname = "Z";
+ break;
+ case 3:
+ axis_reg = PCI8164_AXIS_U;
+ axisname = "U";
+ break;
+ default:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ }
+
+ data[0] = inw(dev->iobase + axis_reg + PCI8164_BUF1);
+ printk("comedi: pci8164 BUF1 read -> %04X:%04X on axis %s\n", data[0],
+ data[1], axisname);
+
+ return 2;
+}
+
+static int adl_pci8164_insn_write_cmd(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int axis, axis_reg;
+
+ char *axisname;
+
+ axis = CR_CHAN(insn->chanspec);
+
+ switch (axis) {
+ case 0:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ break;
+ case 1:
+ axis_reg = PCI8164_AXIS_Y;
+ axisname = "Y";
+ break;
+ case 2:
+ axis_reg = PCI8164_AXIS_Z;
+ axisname = "Z";
+ break;
+ case 3:
+ axis_reg = PCI8164_AXIS_U;
+ axisname = "U";
+ break;
+ default:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ }
+
+ outw(data[0], dev->iobase + axis_reg + PCI8164_CMD);
+ printk("comedi: pci8164 CMD write -> %04X:%04X on axis %s\n", data[0],
+ data[1], axisname);
+
+ return 2;
+}
+
+static int adl_pci8164_insn_write_otp(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int axis, axis_reg;
+
+ char *axisname;
+
+ axis = CR_CHAN(insn->chanspec);
+
+ switch (axis) {
+ case 0:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ break;
+ case 1:
+ axis_reg = PCI8164_AXIS_Y;
+ axisname = "Y";
+ break;
+ case 2:
+ axis_reg = PCI8164_AXIS_Z;
+ axisname = "Z";
+ break;
+ case 3:
+ axis_reg = PCI8164_AXIS_U;
+ axisname = "U";
+ break;
+ default:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ }
+
+ outw(data[0], dev->iobase + axis_reg + PCI8164_OTP);
+ printk("comedi: pci8164 OTP write -> %04X:%04X on axis %s\n", data[0],
+ data[1], axisname);
+
+ return 2;
+}
+
+static int adl_pci8164_insn_write_buf0(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ int axis, axis_reg;
+
+ char *axisname;
+
+ axis = CR_CHAN(insn->chanspec);
+
+ switch (axis) {
+ case 0:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ break;
+ case 1:
+ axis_reg = PCI8164_AXIS_Y;
+ axisname = "Y";
+ break;
+ case 2:
+ axis_reg = PCI8164_AXIS_Z;
+ axisname = "Z";
+ break;
+ case 3:
+ axis_reg = PCI8164_AXIS_U;
+ axisname = "U";
+ break;
+ default:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ }
+
+ outw(data[0], dev->iobase + axis_reg + PCI8164_BUF0);
+ printk("comedi: pci8164 BUF0 write -> %04X:%04X on axis %s\n", data[0],
+ data[1], axisname);
+
+ return 2;
+}
+
+static int adl_pci8164_insn_write_buf1(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ int axis, axis_reg;
+
+ char *axisname;
+
+ axis = CR_CHAN(insn->chanspec);
+
+ switch (axis) {
+ case 0:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ break;
+ case 1:
+ axis_reg = PCI8164_AXIS_Y;
+ axisname = "Y";
+ break;
+ case 2:
+ axis_reg = PCI8164_AXIS_Z;
+ axisname = "Z";
+ break;
+ case 3:
+ axis_reg = PCI8164_AXIS_U;
+ axisname = "U";
+ break;
+ default:
+ axis_reg = PCI8164_AXIS_X;
+ axisname = "X";
+ }
+
+ outw(data[0], dev->iobase + axis_reg + PCI8164_BUF1);
+ printk("comedi: pci8164 BUF1 write -> %04X:%04X on axis %s\n", data[0],
+ data[1], axisname);
+
+ return 2;
+}
+
+COMEDI_PCI_INITCLEANUP(driver_adl_pci8164, adl_pci8164_pci_table);
diff --git a/drivers/staging/comedi/drivers/adl_pci9111.c b/drivers/staging/comedi/drivers/adl_pci9111.c
new file mode 100644
index 000000000000..b4a61c560c5b
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adl_pci9111.c
@@ -0,0 +1,1391 @@
+/*
+
+ comedi/drivers/adl_pci9111.c
+
+ Hardware driver for PCI9111 ADLink cards:
+
+ PCI-9111HR
+
+ Copyright (C) 2002-2005 Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
+
+ 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.
+*/
+
+/*
+Driver: adl_pci9111
+Description: Adlink PCI-9111HR
+Author: Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
+Devices: [ADLink] PCI-9111HR (adl_pci9111)
+Status: experimental
+
+Supports:
+
+ - ai_insn read
+ - ao_insn read/write
+ - di_insn read
+ - do_insn read/write
+ - ai_do_cmd mode with the following sources:
+
+ - start_src TRIG_NOW
+ - scan_begin_src TRIG_FOLLOW TRIG_TIMER TRIG_EXT
+ - convert_src TRIG_TIMER TRIG_EXT
+ - scan_end_src TRIG_COUNT
+ - stop_src TRIG_COUNT TRIG_NONE
+
+ The scanned channels must be consecutive and start from 0. They must
+ all have the same range and aref.
+
+Configuration options:
+
+ [0] - PCI bus number (optional)
+ [1] - PCI slot number (optional)
+
+ If bus/slot is not specified, the first available PCI
+ device will be used.
+
+*/
+
+/*
+CHANGELOG:
+
+ 2005/02/17 Extend AI streaming capabilities. Now, scan_begin_arg can be
+ a multiple of chanlist_len*convert_arg.
+ 2002/02/19 Fixed the two's complement conversion in pci9111_(hr_)ai_get_data.
+ 2002/02/18 Added external trigger support for analog input.
+
+TODO:
+
+ - Really test implemented functionality.
+ - Add support for the PCI-9111DG with a probe routine to identify the card type
+ (perhaps with the help of the channel number readback of the A/D Data register).
+ - Add external multiplexer support.
+
+*/
+
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+
+#include "8253.h"
+#include "comedi_pci.h"
+#include "comedi_fc.h"
+
+#define PCI9111_DRIVER_NAME "adl_pci9111"
+#define PCI9111_HR_DEVICE_ID 0x9111
+
+/* TODO: Add other pci9111 board id */
+
+#define PCI9111_IO_RANGE 0x0100
+
+#define PCI9111_FIFO_HALF_SIZE 512
+
+#define PCI9111_AI_CHANNEL_NBR 16
+
+#define PCI9111_AI_RESOLUTION 12
+#define PCI9111_AI_RESOLUTION_MASK 0x0FFF
+#define PCI9111_AI_RESOLUTION_2_CMP_BIT 0x0800
+
+#define PCI9111_HR_AI_RESOLUTION 16
+#define PCI9111_HR_AI_RESOLUTION_MASK 0xFFFF
+#define PCI9111_HR_AI_RESOLUTION_2_CMP_BIT 0x8000
+
+#define PCI9111_AI_ACQUISITION_PERIOD_MIN_NS 10000
+#define PCI9111_AO_CHANNEL_NBR 1
+#define PCI9111_AO_RESOLUTION 12
+#define PCI9111_AO_RESOLUTION_MASK 0x0FFF
+#define PCI9111_DI_CHANNEL_NBR 16
+#define PCI9111_DO_CHANNEL_NBR 16
+#define PCI9111_DO_MASK 0xFFFF
+
+#define PCI9111_RANGE_SETTING_DELAY 10
+#define PCI9111_AI_INSTANT_READ_UDELAY_US 2
+#define PCI9111_AI_INSTANT_READ_TIMEOUT 100
+
+#define PCI9111_8254_CLOCK_PERIOD_NS 500
+
+#define PCI9111_8254_COUNTER_0 0x00
+#define PCI9111_8254_COUNTER_1 0x40
+#define PCI9111_8254_COUNTER_2 0x80
+#define PCI9111_8254_COUNTER_LATCH 0x00
+#define PCI9111_8254_READ_LOAD_LSB_ONLY 0x10
+#define PCI9111_8254_READ_LOAD_MSB_ONLY 0x20
+#define PCI9111_8254_READ_LOAD_LSB_MSB 0x30
+#define PCI9111_8254_MODE_0 0x00
+#define PCI9111_8254_MODE_1 0x02
+#define PCI9111_8254_MODE_2 0x04
+#define PCI9111_8254_MODE_3 0x06
+#define PCI9111_8254_MODE_4 0x08
+#define PCI9111_8254_MODE_5 0x0A
+#define PCI9111_8254_BINARY_COUNTER 0x00
+#define PCI9111_8254_BCD_COUNTER 0x01
+
+/* IO address map */
+
+#define PCI9111_REGISTER_AD_FIFO_VALUE 0x00 /* AD Data stored in FIFO */
+#define PCI9111_REGISTER_DA_OUTPUT 0x00
+#define PCI9111_REGISTER_DIGITAL_IO 0x02
+#define PCI9111_REGISTER_EXTENDED_IO_PORTS 0x04
+#define PCI9111_REGISTER_AD_CHANNEL_CONTROL 0x06 /* Channel selection */
+#define PCI9111_REGISTER_AD_CHANNEL_READBACK 0x06
+#define PCI9111_REGISTER_INPUT_SIGNAL_RANGE 0x08
+#define PCI9111_REGISTER_RANGE_STATUS_READBACK 0x08
+#define PCI9111_REGISTER_TRIGGER_MODE_CONTROL 0x0A
+#define PCI9111_REGISTER_AD_MODE_INTERRUPT_READBACK 0x0A
+#define PCI9111_REGISTER_SOFTWARE_TRIGGER 0x0E
+#define PCI9111_REGISTER_INTERRUPT_CONTROL 0x0C
+#define PCI9111_REGISTER_8254_COUNTER_0 0x40
+#define PCI9111_REGISTER_8254_COUNTER_1 0x42
+#define PCI9111_REGISTER_8254_COUNTER_2 0X44
+#define PCI9111_REGISTER_8254_CONTROL 0x46
+#define PCI9111_REGISTER_INTERRUPT_CLEAR 0x48
+
+#define PCI9111_TRIGGER_MASK 0x0F
+#define PCI9111_PTRG_OFF (0 << 3)
+#define PCI9111_PTRG_ON (1 << 3)
+#define PCI9111_EITS_EXTERNAL (1 << 2)
+#define PCI9111_EITS_INTERNAL (0 << 2)
+#define PCI9111_TPST_SOFTWARE_TRIGGER (0 << 1)
+#define PCI9111_TPST_TIMER_PACER (1 << 1)
+#define PCI9111_ASCAN_ON (1 << 0)
+#define PCI9111_ASCAN_OFF (0 << 0)
+
+#define PCI9111_ISC0_SET_IRQ_ON_ENDING_OF_AD_CONVERSION (0 << 0)
+#define PCI9111_ISC0_SET_IRQ_ON_FIFO_HALF_FULL (1 << 0)
+#define PCI9111_ISC1_SET_IRQ_ON_TIMER_TICK (0 << 1)
+#define PCI9111_ISC1_SET_IRQ_ON_EXT_TRG (1 << 1)
+#define PCI9111_FFEN_SET_FIFO_ENABLE (0 << 2)
+#define PCI9111_FFEN_SET_FIFO_DISABLE (1 << 2)
+
+#define PCI9111_CHANNEL_MASK 0x0F
+
+#define PCI9111_RANGE_MASK 0x07
+#define PCI9111_FIFO_EMPTY_MASK 0x10
+#define PCI9111_FIFO_HALF_FULL_MASK 0x20
+#define PCI9111_FIFO_FULL_MASK 0x40
+#define PCI9111_AD_BUSY_MASK 0x80
+
+#define PCI9111_IO_BASE dev->iobase
+
+/*
+ * Define inlined function
+ */
+
+#define pci9111_trigger_and_autoscan_get() \
+ (inb(PCI9111_IO_BASE+PCI9111_REGISTER_AD_MODE_INTERRUPT_READBACK)&0x0F)
+
+#define pci9111_trigger_and_autoscan_set(flags) \
+ outb(flags,PCI9111_IO_BASE+PCI9111_REGISTER_TRIGGER_MODE_CONTROL)
+
+#define pci9111_interrupt_and_fifo_get() \
+ ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_AD_MODE_INTERRUPT_READBACK) >> 4) &0x03)
+
+#define pci9111_interrupt_and_fifo_set(flags) \
+ outb(flags,PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL)
+
+#define pci9111_interrupt_clear() \
+ outb(0,PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CLEAR)
+
+#define pci9111_software_trigger() \
+ outb(0,PCI9111_IO_BASE+PCI9111_REGISTER_SOFTWARE_TRIGGER)
+
+#define pci9111_fifo_reset() \
+ outb(PCI9111_FFEN_SET_FIFO_ENABLE,PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL); \
+ outb(PCI9111_FFEN_SET_FIFO_DISABLE,PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL); \
+ outb(PCI9111_FFEN_SET_FIFO_ENABLE,PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL)
+
+#define pci9111_is_fifo_full() \
+ ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK)& \
+ PCI9111_FIFO_FULL_MASK)==0)
+
+#define pci9111_is_fifo_half_full() \
+ ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK)& \
+ PCI9111_FIFO_HALF_FULL_MASK)==0)
+
+#define pci9111_is_fifo_empty() \
+ ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK)& \
+ PCI9111_FIFO_EMPTY_MASK)==0)
+
+#define pci9111_ai_channel_set(channel) \
+ outb((channel)&PCI9111_CHANNEL_MASK,PCI9111_IO_BASE+PCI9111_REGISTER_AD_CHANNEL_CONTROL)
+
+#define pci9111_ai_channel_get() \
+ inb(PCI9111_IO_BASE+PCI9111_REGISTER_AD_CHANNEL_READBACK)&PCI9111_CHANNEL_MASK
+
+#define pci9111_ai_range_set(range) \
+ outb((range)&PCI9111_RANGE_MASK,PCI9111_IO_BASE+PCI9111_REGISTER_INPUT_SIGNAL_RANGE)
+
+#define pci9111_ai_range_get() \
+ inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK)&PCI9111_RANGE_MASK
+
+#define pci9111_ai_get_data() \
+ ((inw(PCI9111_IO_BASE+PCI9111_REGISTER_AD_FIFO_VALUE)>>4)&PCI9111_AI_RESOLUTION_MASK) \
+ ^ PCI9111_AI_RESOLUTION_2_CMP_BIT
+
+#define pci9111_hr_ai_get_data() \
+ (inw(PCI9111_IO_BASE+PCI9111_REGISTER_AD_FIFO_VALUE) & PCI9111_HR_AI_RESOLUTION_MASK) \
+ ^ PCI9111_HR_AI_RESOLUTION_2_CMP_BIT
+
+#define pci9111_ao_set_data(data) \
+ outw(data&PCI9111_AO_RESOLUTION_MASK,PCI9111_IO_BASE+PCI9111_REGISTER_DA_OUTPUT)
+
+#define pci9111_di_get_bits() \
+ inw(PCI9111_IO_BASE+PCI9111_REGISTER_DIGITAL_IO)
+
+#define pci9111_do_set_bits(bits) \
+ outw(bits,PCI9111_IO_BASE+PCI9111_REGISTER_DIGITAL_IO)
+
+#define pci9111_8254_control_set(flags) \
+ outb(flags,PCI9111_IO_BASE+PCI9111_REGISTER_8254_CONTROL)
+
+#define pci9111_8254_counter_0_set(data) \
+ outb(data & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_0); \
+ outb( (data >> 8) & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_0)
+
+#define pci9111_8254_counter_1_set(data) \
+ outb(data & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_1); \
+ outb( (data >> 8) & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_1)
+
+#define pci9111_8254_counter_2_set(data) \
+ outb(data & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_2); \
+ outb( (data >> 8) & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_2)
+
+/* Function prototypes */
+
+static int pci9111_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pci9111_detach(struct comedi_device * dev);
+static void pci9111_ai_munge(struct comedi_device * dev, struct comedi_subdevice * s,
+ void *data, unsigned int num_bytes, unsigned int start_chan_index);
+
+static const struct comedi_lrange pci9111_hr_ai_range = {
+ 5,
+ {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625)
+ }
+};
+
+static DEFINE_PCI_DEVICE_TABLE(pci9111_pci_table) = {
+ {PCI_VENDOR_ID_ADLINK, PCI9111_HR_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0,
+ 0, 0},
+ /* { PCI_VENDOR_ID_ADLINK, PCI9111_HG_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, */
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, pci9111_pci_table);
+
+/* */
+/* Board specification structure */
+/* */
+
+struct pci9111_board {
+ const char *name; /* driver name */
+ int device_id;
+ int ai_channel_nbr; /* num of A/D chans */
+ int ao_channel_nbr; /* num of D/A chans */
+ int ai_resolution; /* resolution of A/D */
+ int ai_resolution_mask;
+ int ao_resolution; /* resolution of D/A */
+ int ao_resolution_mask;
+ const struct comedi_lrange *ai_range_list; /* rangelist for A/D */
+ const struct comedi_lrange *ao_range_list; /* rangelist for D/A */
+ unsigned int ai_acquisition_period_min_ns;
+};
+
+static const struct pci9111_board pci9111_boards[] = {
+ {
+ name: "pci9111_hr",
+ device_id:PCI9111_HR_DEVICE_ID,
+ ai_channel_nbr:PCI9111_AI_CHANNEL_NBR,
+ ao_channel_nbr:PCI9111_AO_CHANNEL_NBR,
+ ai_resolution:PCI9111_HR_AI_RESOLUTION,
+ ai_resolution_mask:PCI9111_HR_AI_RESOLUTION_MASK,
+ ao_resolution:PCI9111_AO_RESOLUTION,
+ ao_resolution_mask:PCI9111_AO_RESOLUTION_MASK,
+ ai_range_list:&pci9111_hr_ai_range,
+ ao_range_list:&range_bipolar10,
+ ai_acquisition_period_min_ns:PCI9111_AI_ACQUISITION_PERIOD_MIN_NS}
+};
+
+#define pci9111_board_nbr \
+ (sizeof(pci9111_boards)/sizeof(struct pci9111_board))
+
+static struct comedi_driver pci9111_driver = {
+ driver_name:PCI9111_DRIVER_NAME,
+ module:THIS_MODULE,
+ attach:pci9111_attach,
+ detach:pci9111_detach,
+};
+
+COMEDI_PCI_INITCLEANUP(pci9111_driver, pci9111_pci_table);
+
+/* Private data structure */
+
+struct pci9111_private_data {
+ struct pci_dev *pci_device;
+ unsigned long io_range; /* PCI6503 io range */
+
+ unsigned long lcr_io_base; /* Local configuration register base address */
+ unsigned long lcr_io_range;
+
+ int stop_counter;
+ int stop_is_none;
+
+ unsigned int scan_delay;
+ unsigned int chanlist_len;
+ unsigned int chunk_counter;
+ unsigned int chunk_num_samples;
+
+ int ao_readback; /* Last written analog output data */
+
+ int timer_divisor_1; /* Divisor values for the 8254 timer pacer */
+ int timer_divisor_2;
+
+ int is_valid; /* Is device valid */
+
+ short ai_bounce_buffer[2 * PCI9111_FIFO_HALF_SIZE];
+};
+
+#define dev_private ((struct pci9111_private_data *)dev->private)
+
+/* ------------------------------------------------------------------ */
+/* PLX9050 SECTION */
+/* ------------------------------------------------------------------ */
+
+#define PLX9050_REGISTER_INTERRUPT_CONTROL 0x4c
+
+#define PLX9050_LINTI1_ENABLE (1 << 0)
+#define PLX9050_LINTI1_ACTIVE_HIGH (1 << 1)
+#define PLX9050_LINTI1_STATUS (1 << 2)
+#define PLX9050_LINTI2_ENABLE (1 << 3)
+#define PLX9050_LINTI2_ACTIVE_HIGH (1 << 4)
+#define PLX9050_LINTI2_STATUS (1 << 5)
+#define PLX9050_PCI_INTERRUPT_ENABLE (1 << 6)
+#define PLX9050_SOFTWARE_INTERRUPT (1 << 7)
+
+static void plx9050_interrupt_control(unsigned long io_base,
+ bool LINTi1_enable,
+ bool LINTi1_active_high,
+ bool LINTi2_enable, bool LINTi2_active_high, bool interrupt_enable)
+{
+ int flags = 0;
+
+ if (LINTi1_enable)
+ flags |= PLX9050_LINTI1_ENABLE;
+ if (LINTi1_active_high)
+ flags |= PLX9050_LINTI1_ACTIVE_HIGH;
+ if (LINTi2_enable)
+ flags |= PLX9050_LINTI2_ENABLE;
+ if (LINTi2_active_high)
+ flags |= PLX9050_LINTI2_ACTIVE_HIGH;
+
+ if (interrupt_enable)
+ flags |= PLX9050_PCI_INTERRUPT_ENABLE;
+
+ outb(flags, io_base + PLX9050_REGISTER_INTERRUPT_CONTROL);
+}
+
+/* ------------------------------------------------------------------ */
+/* MISCELLANEOUS SECTION */
+/* ------------------------------------------------------------------ */
+
+/* 8254 timer */
+
+static void pci9111_timer_set(struct comedi_device * dev)
+{
+ pci9111_8254_control_set(PCI9111_8254_COUNTER_0 |
+ PCI9111_8254_READ_LOAD_LSB_MSB |
+ PCI9111_8254_MODE_0 | PCI9111_8254_BINARY_COUNTER);
+
+ pci9111_8254_control_set(PCI9111_8254_COUNTER_1 |
+ PCI9111_8254_READ_LOAD_LSB_MSB |
+ PCI9111_8254_MODE_2 | PCI9111_8254_BINARY_COUNTER);
+
+ pci9111_8254_control_set(PCI9111_8254_COUNTER_2 |
+ PCI9111_8254_READ_LOAD_LSB_MSB |
+ PCI9111_8254_MODE_2 | PCI9111_8254_BINARY_COUNTER);
+
+ comedi_udelay(1);
+
+ pci9111_8254_counter_2_set(dev_private->timer_divisor_2);
+ pci9111_8254_counter_1_set(dev_private->timer_divisor_1);
+}
+
+enum pci9111_trigger_sources {
+ software,
+ timer_pacer,
+ external
+};
+
+static void pci9111_trigger_source_set(struct comedi_device * dev,
+ enum pci9111_trigger_sources source)
+{
+ int flags;
+
+ flags = pci9111_trigger_and_autoscan_get() & 0x09;
+
+ switch (source) {
+ case software:
+ flags |= PCI9111_EITS_INTERNAL | PCI9111_TPST_SOFTWARE_TRIGGER;
+ break;
+
+ case timer_pacer:
+ flags |= PCI9111_EITS_INTERNAL | PCI9111_TPST_TIMER_PACER;
+ break;
+
+ case external:
+ flags |= PCI9111_EITS_EXTERNAL;
+ break;
+ }
+
+ pci9111_trigger_and_autoscan_set(flags);
+}
+
+static void pci9111_pretrigger_set(struct comedi_device * dev, bool pretrigger)
+{
+ int flags;
+
+ flags = pci9111_trigger_and_autoscan_get() & 0x07;
+
+ if (pretrigger)
+ flags |= PCI9111_PTRG_ON;
+
+ pci9111_trigger_and_autoscan_set(flags);
+}
+
+static void pci9111_autoscan_set(struct comedi_device * dev, bool autoscan)
+{
+ int flags;
+
+ flags = pci9111_trigger_and_autoscan_get() & 0x0e;
+
+ if (autoscan)
+ flags |= PCI9111_ASCAN_ON;
+
+ pci9111_trigger_and_autoscan_set(flags);
+}
+
+enum pci9111_ISC0_sources {
+ irq_on_eoc,
+ irq_on_fifo_half_full
+};
+
+enum pci9111_ISC1_sources {
+ irq_on_timer_tick,
+ irq_on_external_trigger
+};
+
+static void pci9111_interrupt_source_set(struct comedi_device * dev,
+ enum pci9111_ISC0_sources irq_0_source, enum pci9111_ISC1_sources irq_1_source)
+{
+ int flags;
+
+ flags = pci9111_interrupt_and_fifo_get() & 0x04;
+
+ if (irq_0_source == irq_on_fifo_half_full)
+ flags |= PCI9111_ISC0_SET_IRQ_ON_FIFO_HALF_FULL;
+
+ if (irq_1_source == irq_on_external_trigger)
+ flags |= PCI9111_ISC1_SET_IRQ_ON_EXT_TRG;
+
+ pci9111_interrupt_and_fifo_set(flags);
+}
+
+/* ------------------------------------------------------------------ */
+/* HARDWARE TRIGGERED ANALOG INPUT SECTION */
+/* ------------------------------------------------------------------ */
+
+/* Cancel analog input autoscan */
+
+#undef AI_DO_CMD_DEBUG
+
+static int pci9111_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ /* Disable interrupts */
+
+ plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
+ true, false);
+
+ pci9111_trigger_source_set(dev, software);
+
+ pci9111_autoscan_set(dev, false);
+
+ pci9111_fifo_reset();
+
+#ifdef AI_DO_CMD_DEBUG
+ printk(PCI9111_DRIVER_NAME ": ai_cancel\n");
+#endif
+
+ return 0;
+}
+
+/* Test analog input command */
+
+#define pci9111_check_trigger_src(src,flags) \
+ tmp = src; \
+ src &= flags; \
+ if (!src || tmp != src) error++
+
+static int
+pci9111_ai_do_cmd_test(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_cmd * cmd)
+{
+ int tmp;
+ int error = 0;
+ int range, reference;
+ int i;
+ struct pci9111_board *board = (struct pci9111_board *) dev->board_ptr;
+
+ /* Step 1 : check if trigger are trivialy valid */
+
+ pci9111_check_trigger_src(cmd->start_src, TRIG_NOW);
+ pci9111_check_trigger_src(cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT);
+ pci9111_check_trigger_src(cmd->convert_src, TRIG_TIMER | TRIG_EXT);
+ pci9111_check_trigger_src(cmd->scan_end_src, TRIG_COUNT);
+ pci9111_check_trigger_src(cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (error)
+ return 1;
+
+ /* step 2 : make sure trigger sources are unique and mutually compatible */
+
+ if (cmd->start_src != TRIG_NOW)
+ error++;
+
+ if ((cmd->scan_begin_src != TRIG_TIMER) &&
+ (cmd->scan_begin_src != TRIG_FOLLOW) &&
+ (cmd->scan_begin_src != TRIG_EXT))
+ error++;
+
+ if ((cmd->convert_src != TRIG_TIMER) && (cmd->convert_src != TRIG_EXT)) {
+ error++;
+ }
+ if ((cmd->convert_src == TRIG_TIMER) &&
+ !((cmd->scan_begin_src == TRIG_TIMER) ||
+ (cmd->scan_begin_src == TRIG_FOLLOW))) {
+ error++;
+ }
+ if ((cmd->convert_src == TRIG_EXT) &&
+ !((cmd->scan_begin_src == TRIG_EXT) ||
+ (cmd->scan_begin_src == TRIG_FOLLOW))) {
+ error++;
+ }
+
+ if (cmd->scan_end_src != TRIG_COUNT)
+ error++;
+ if ((cmd->stop_src != TRIG_COUNT) && (cmd->stop_src != TRIG_NONE))
+ error++;
+
+ if (error)
+ return 2;
+
+ /* Step 3 : make sure arguments are trivialy compatible */
+
+ if (cmd->chanlist_len < 1) {
+ cmd->chanlist_len = 1;
+ error++;
+ }
+
+ if (cmd->chanlist_len > board->ai_channel_nbr) {
+ cmd->chanlist_len = board->ai_channel_nbr;
+ error++;
+ }
+
+ if ((cmd->start_src == TRIG_NOW) && (cmd->start_arg != 0)) {
+ cmd->start_arg = 0;
+ error++;
+ }
+
+ if ((cmd->convert_src == TRIG_TIMER) &&
+ (cmd->convert_arg < board->ai_acquisition_period_min_ns)) {
+ cmd->convert_arg = board->ai_acquisition_period_min_ns;
+ error++;
+ }
+ if ((cmd->convert_src == TRIG_EXT) && (cmd->convert_arg != 0)) {
+ cmd->convert_arg = 0;
+ error++;
+ }
+
+ if ((cmd->scan_begin_src == TRIG_TIMER) &&
+ (cmd->scan_begin_arg < board->ai_acquisition_period_min_ns)) {
+ cmd->scan_begin_arg = board->ai_acquisition_period_min_ns;
+ error++;
+ }
+ if ((cmd->scan_begin_src == TRIG_FOLLOW) && (cmd->scan_begin_arg != 0)) {
+ cmd->scan_begin_arg = 0;
+ error++;
+ }
+ if ((cmd->scan_begin_src == TRIG_EXT) && (cmd->scan_begin_arg != 0)) {
+ cmd->scan_begin_arg = 0;
+ error++;
+ }
+
+ if ((cmd->scan_end_src == TRIG_COUNT) &&
+ (cmd->scan_end_arg != cmd->chanlist_len)) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ error++;
+ }
+
+ if ((cmd->stop_src == TRIG_COUNT) && (cmd->stop_arg < 1)) {
+ cmd->stop_arg = 1;
+ error++;
+ }
+ if ((cmd->stop_src == TRIG_NONE) && (cmd->stop_arg != 0)) {
+ cmd->stop_arg = 0;
+ error++;
+ }
+
+ if (error)
+ return 3;
+
+ /* Step 4 : fix up any arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ i8253_cascade_ns_to_timer_2div(PCI9111_8254_CLOCK_PERIOD_NS,
+ &(dev_private->timer_divisor_1),
+ &(dev_private->timer_divisor_2),
+ &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->convert_arg)
+ error++;
+ }
+ /* There's only one timer on this card, so the scan_begin timer must */
+ /* be a multiple of chanlist_len*convert_arg */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+
+ unsigned int scan_begin_min;
+ unsigned int scan_begin_arg;
+ unsigned int scan_factor;
+
+ scan_begin_min = cmd->chanlist_len * cmd->convert_arg;
+
+ if (cmd->scan_begin_arg != scan_begin_min) {
+ if (scan_begin_min < cmd->scan_begin_arg) {
+ scan_factor =
+ cmd->scan_begin_arg / scan_begin_min;
+ scan_begin_arg = scan_factor * scan_begin_min;
+ if (cmd->scan_begin_arg != scan_begin_arg) {
+ cmd->scan_begin_arg = scan_begin_arg;
+ error++;
+ }
+ } else {
+ cmd->scan_begin_arg = scan_begin_min;
+ error++;
+ }
+ }
+ }
+
+ if (error)
+ return 4;
+
+ /* Step 5 : check channel list */
+
+ if (cmd->chanlist) {
+
+ range = CR_RANGE(cmd->chanlist[0]);
+ reference = CR_AREF(cmd->chanlist[0]);
+
+ if (cmd->chanlist_len > 1) {
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ if (CR_CHAN(cmd->chanlist[i]) != i) {
+ comedi_error(dev,
+ "entries in chanlist must be consecutive "
+ "channels,counting upwards from 0\n");
+ error++;
+ }
+ if (CR_RANGE(cmd->chanlist[i]) != range) {
+ comedi_error(dev,
+ "entries in chanlist must all have the same gain\n");
+ error++;
+ }
+ if (CR_AREF(cmd->chanlist[i]) != reference) {
+ comedi_error(dev,
+ "entries in chanlist must all have the same reference\n");
+ error++;
+ }
+ }
+ } else {
+ if ((CR_CHAN(cmd->chanlist[0]) >
+ (board->ai_channel_nbr - 1))
+ || (CR_CHAN(cmd->chanlist[0]) < 0)) {
+ comedi_error(dev,
+ "channel number is out of limits\n");
+ error++;
+ }
+ }
+ }
+
+ if (error)
+ return 5;
+
+ return 0;
+
+}
+
+/* Analog input command */
+
+static int pci9111_ai_do_cmd(struct comedi_device * dev, struct comedi_subdevice * subdevice)
+{
+ struct comedi_cmd *async_cmd = &subdevice->async->cmd;
+
+ if (!dev->irq) {
+ comedi_error(dev,
+ "no irq assigned for PCI9111, cannot do hardware conversion");
+ return -1;
+ }
+ /* Set channel scan limit */
+ /* PCI9111 allows only scanning from channel 0 to channel n */
+ /* TODO: handle the case of an external multiplexer */
+
+ if (async_cmd->chanlist_len > 1) {
+ pci9111_ai_channel_set((async_cmd->chanlist_len) - 1);
+ pci9111_autoscan_set(dev, true);
+ } else {
+ pci9111_ai_channel_set(CR_CHAN(async_cmd->chanlist[0]));
+ pci9111_autoscan_set(dev, false);
+ }
+
+ /* Set gain */
+ /* This is the same gain on every channel */
+
+ pci9111_ai_range_set(CR_RANGE(async_cmd->chanlist[0]));
+
+ /* Set counter */
+
+ switch (async_cmd->stop_src) {
+ case TRIG_COUNT:
+ dev_private->stop_counter =
+ async_cmd->stop_arg * async_cmd->chanlist_len;
+ dev_private->stop_is_none = 0;
+ break;
+
+ case TRIG_NONE:
+ dev_private->stop_counter = 0;
+ dev_private->stop_is_none = 1;
+ break;
+
+ default:
+ comedi_error(dev, "Invalid stop trigger");
+ return -1;
+ }
+
+ /* Set timer pacer */
+
+ dev_private->scan_delay = 0;
+ switch (async_cmd->convert_src) {
+ case TRIG_TIMER:
+ i8253_cascade_ns_to_timer_2div(PCI9111_8254_CLOCK_PERIOD_NS,
+ &(dev_private->timer_divisor_1),
+ &(dev_private->timer_divisor_2),
+ &(async_cmd->convert_arg),
+ async_cmd->flags & TRIG_ROUND_MASK);
+#ifdef AI_DO_CMD_DEBUG
+ printk(PCI9111_DRIVER_NAME ": divisors = %d, %d\n",
+ dev_private->timer_divisor_1,
+ dev_private->timer_divisor_2);
+#endif
+
+ pci9111_trigger_source_set(dev, software);
+ pci9111_timer_set(dev);
+ pci9111_fifo_reset();
+ pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
+ irq_on_timer_tick);
+ pci9111_trigger_source_set(dev, timer_pacer);
+ plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
+ false, true, true);
+
+ dev_private->scan_delay =
+ (async_cmd->scan_begin_arg / (async_cmd->convert_arg *
+ async_cmd->chanlist_len)) - 1;
+
+ break;
+
+ case TRIG_EXT:
+
+ pci9111_trigger_source_set(dev, external);
+ pci9111_fifo_reset();
+ pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
+ irq_on_timer_tick);
+ plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
+ false, true, true);
+
+ break;
+
+ default:
+ comedi_error(dev, "Invalid convert trigger");
+ return -1;
+ }
+
+ dev_private->stop_counter *= (1 + dev_private->scan_delay);
+ dev_private->chanlist_len = async_cmd->chanlist_len;
+ dev_private->chunk_counter = 0;
+ dev_private->chunk_num_samples =
+ dev_private->chanlist_len * (1 + dev_private->scan_delay);
+
+#ifdef AI_DO_CMD_DEBUG
+ printk(PCI9111_DRIVER_NAME ": start interruptions!\n");
+ printk(PCI9111_DRIVER_NAME ": trigger source = %2x\n",
+ pci9111_trigger_and_autoscan_get());
+ printk(PCI9111_DRIVER_NAME ": irq source = %2x\n",
+ pci9111_interrupt_and_fifo_get());
+ printk(PCI9111_DRIVER_NAME ": ai_do_cmd\n");
+ printk(PCI9111_DRIVER_NAME ": stop counter = %d\n",
+ dev_private->stop_counter);
+ printk(PCI9111_DRIVER_NAME ": scan delay = %d\n",
+ dev_private->scan_delay);
+ printk(PCI9111_DRIVER_NAME ": chanlist_len = %d\n",
+ dev_private->chanlist_len);
+ printk(PCI9111_DRIVER_NAME ": chunk num samples = %d\n",
+ dev_private->chunk_num_samples);
+#endif
+
+ return 0;
+}
+
+static void pci9111_ai_munge(struct comedi_device * dev, struct comedi_subdevice * s,
+ void *data, unsigned int num_bytes, unsigned int start_chan_index)
+{
+ unsigned int i, num_samples = num_bytes / sizeof(short);
+ short *array = data;
+ int resolution =
+ ((struct pci9111_board *) dev->board_ptr)->ai_resolution;
+
+ for (i = 0; i < num_samples; i++) {
+ if (resolution == PCI9111_HR_AI_RESOLUTION)
+ array[i] =
+ (array[i] & PCI9111_HR_AI_RESOLUTION_MASK) ^
+ PCI9111_HR_AI_RESOLUTION_2_CMP_BIT;
+ else
+ array[i] =
+ ((array[i] >> 4) & PCI9111_AI_RESOLUTION_MASK) ^
+ PCI9111_AI_RESOLUTION_2_CMP_BIT;
+ }
+}
+
+/* ------------------------------------------------------------------ */
+/* INTERRUPT SECTION */
+/* ------------------------------------------------------------------ */
+
+#undef INTERRUPT_DEBUG
+
+static irqreturn_t pci9111_interrupt(int irq, void *p_device PT_REGS_ARG)
+{
+ struct comedi_device *dev = p_device;
+ struct comedi_subdevice *subdevice = dev->read_subdev;
+ struct comedi_async *async;
+ unsigned long irq_flags;
+ unsigned char intcsr;
+
+ if (!dev->attached) {
+ /* Ignore interrupt before device fully attached. */
+ /* Might not even have allocated subdevices yet! */
+ return IRQ_NONE;
+ }
+
+ async = subdevice->async;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
+
+ /* Check if we are source of interrupt */
+ intcsr = inb(dev_private->lcr_io_base +
+ PLX9050_REGISTER_INTERRUPT_CONTROL);
+ if (!(((intcsr & PLX9050_PCI_INTERRUPT_ENABLE) != 0)
+ && (((intcsr & (PLX9050_LINTI1_ENABLE |
+ PLX9050_LINTI1_STATUS))
+ ==
+ (PLX9050_LINTI1_ENABLE |
+ PLX9050_LINTI1_STATUS))
+ || ((intcsr & (PLX9050_LINTI2_ENABLE |
+ PLX9050_LINTI2_STATUS))
+ ==
+ (PLX9050_LINTI2_ENABLE |
+ PLX9050_LINTI2_STATUS))))) {
+ /* Not the source of the interrupt. */
+ /* (N.B. not using PLX9050_SOFTWARE_INTERRUPT) */
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ return IRQ_NONE;
+ }
+
+ if ((intcsr & (PLX9050_LINTI1_ENABLE | PLX9050_LINTI1_STATUS)) ==
+ (PLX9050_LINTI1_ENABLE | PLX9050_LINTI1_STATUS)) {
+ /* Interrupt comes from fifo_half-full signal */
+
+ if (pci9111_is_fifo_full()) {
+ comedi_spin_unlock_irqrestore(&dev->spinlock,
+ irq_flags);
+ comedi_error(dev, PCI9111_DRIVER_NAME " fifo overflow");
+ pci9111_interrupt_clear();
+ pci9111_ai_cancel(dev, subdevice);
+ async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ comedi_event(dev, subdevice);
+
+ return IRQ_HANDLED;
+ }
+
+ if (pci9111_is_fifo_half_full()) {
+ unsigned int num_samples;
+ unsigned int bytes_written = 0;
+
+#ifdef INTERRUPT_DEBUG
+ printk(PCI9111_DRIVER_NAME ": fifo is half full\n");
+#endif
+
+ num_samples =
+ PCI9111_FIFO_HALF_SIZE >
+ dev_private->stop_counter
+ && !dev_private->stop_is_none ? dev_private->
+ stop_counter : PCI9111_FIFO_HALF_SIZE;
+ insw(PCI9111_IO_BASE + PCI9111_REGISTER_AD_FIFO_VALUE,
+ dev_private->ai_bounce_buffer, num_samples);
+
+ if (dev_private->scan_delay < 1) {
+ bytes_written =
+ cfc_write_array_to_buffer(subdevice,
+ dev_private->ai_bounce_buffer,
+ num_samples * sizeof(short));
+ } else {
+ int position = 0;
+ int to_read;
+
+ while (position < num_samples) {
+ if (dev_private->chunk_counter <
+ dev_private->chanlist_len) {
+ to_read =
+ dev_private->
+ chanlist_len -
+ dev_private->
+ chunk_counter;
+
+ if (to_read >
+ num_samples - position)
+ to_read =
+ num_samples -
+ position;
+
+ bytes_written +=
+ cfc_write_array_to_buffer
+ (subdevice,
+ dev_private->
+ ai_bounce_buffer +
+ position,
+ to_read *
+ sizeof(short));
+ } else {
+ to_read =
+ dev_private->
+ chunk_num_samples -
+ dev_private->
+ chunk_counter;
+ if (to_read >
+ num_samples - position)
+ to_read =
+ num_samples -
+ position;
+
+ bytes_written +=
+ sizeof(short) *
+ to_read;
+ }
+
+ position += to_read;
+ dev_private->chunk_counter += to_read;
+
+ if (dev_private->chunk_counter >=
+ dev_private->chunk_num_samples)
+ dev_private->chunk_counter = 0;
+ }
+ }
+
+ dev_private->stop_counter -=
+ bytes_written / sizeof(short);
+ }
+ }
+
+ if ((dev_private->stop_counter == 0) && (!dev_private->stop_is_none)) {
+ async->events |= COMEDI_CB_EOA;
+ pci9111_ai_cancel(dev, subdevice);
+ }
+
+ /* Very important, otherwise another interrupt request will be inserted
+ * and will cause driver hangs on processing interrupt event. */
+
+ pci9111_interrupt_clear();
+
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ comedi_event(dev, subdevice);
+
+ return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------------ */
+/* INSTANT ANALOG INPUT OUTPUT SECTION */
+/* ------------------------------------------------------------------ */
+
+/* analog instant input */
+
+#undef AI_INSN_DEBUG
+
+static int pci9111_ai_insn_read(struct comedi_device * dev,
+ struct comedi_subdevice * subdevice, struct comedi_insn * insn, unsigned int * data)
+{
+ int resolution =
+ ((struct pci9111_board *) dev->board_ptr)->ai_resolution;
+
+ int timeout, i;
+
+#ifdef AI_INSN_DEBUG
+ printk(PCI9111_DRIVER_NAME ": ai_insn set c/r/n = %2x/%2x/%2x\n",
+ CR_CHAN((&insn->chanspec)[0]),
+ CR_RANGE((&insn->chanspec)[0]), insn->n);
+#endif
+
+ pci9111_ai_channel_set(CR_CHAN((&insn->chanspec)[0]));
+
+ if ((pci9111_ai_range_get()) != CR_RANGE((&insn->chanspec)[0])) {
+ pci9111_ai_range_set(CR_RANGE((&insn->chanspec)[0]));
+ }
+
+ pci9111_fifo_reset();
+
+ for (i = 0; i < insn->n; i++) {
+ pci9111_software_trigger();
+
+ timeout = PCI9111_AI_INSTANT_READ_TIMEOUT;
+
+ while (timeout--) {
+ if (!pci9111_is_fifo_empty())
+ goto conversion_done;
+ }
+
+ comedi_error(dev, "A/D read timeout");
+ data[i] = 0;
+ pci9111_fifo_reset();
+ return -ETIME;
+
+ conversion_done:
+
+ if (resolution == PCI9111_HR_AI_RESOLUTION) {
+ data[i] = pci9111_hr_ai_get_data();
+ } else {
+ data[i] = pci9111_ai_get_data();
+ }
+ }
+
+#ifdef AI_INSN_DEBUG
+ printk(PCI9111_DRIVER_NAME ": ai_insn get c/r/t = %2x/%2x/%2x\n",
+ pci9111_ai_channel_get(),
+ pci9111_ai_range_get(), pci9111_trigger_and_autoscan_get());
+#endif
+
+ return i;
+}
+
+/* Analog instant output */
+
+static int
+pci9111_ao_insn_write(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ pci9111_ao_set_data(data[i]);
+ dev_private->ao_readback = data[i];
+ }
+
+ return i;
+}
+
+/* Analog output readback */
+
+static int pci9111_ao_insn_read(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ data[i] = dev_private->ao_readback & PCI9111_AO_RESOLUTION_MASK;
+ }
+
+ return i;
+}
+
+/* ------------------------------------------------------------------ */
+/* DIGITAL INPUT OUTPUT SECTION */
+/* ------------------------------------------------------------------ */
+
+/* Digital inputs */
+
+static int pci9111_di_insn_bits(struct comedi_device * dev,
+ struct comedi_subdevice * subdevice, struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int bits;
+
+ bits = pci9111_di_get_bits();
+ data[1] = bits;
+
+ return 2;
+}
+
+/* Digital outputs */
+
+static int pci9111_do_insn_bits(struct comedi_device * dev,
+ struct comedi_subdevice * subdevice, struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int bits;
+
+ /* Only set bits that have been masked */
+ /* data[0] = mask */
+ /* data[1] = bit state */
+
+ data[0] &= PCI9111_DO_MASK;
+
+ bits = subdevice->state;
+ bits &= ~data[0];
+ bits |= data[0] & data[1];
+ subdevice->state = bits;
+
+ pci9111_do_set_bits(bits);
+
+ data[1] = bits;
+
+ return 2;
+}
+
+/* ------------------------------------------------------------------ */
+/* INITIALISATION SECTION */
+/* ------------------------------------------------------------------ */
+
+/* Reset device */
+
+static int pci9111_reset(struct comedi_device * dev)
+{
+ /* Set trigger source to software */
+
+ plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
+ true, false);
+
+ pci9111_trigger_source_set(dev, software);
+ pci9111_pretrigger_set(dev, false);
+ pci9111_autoscan_set(dev, false);
+
+ /* Reset 8254 chip */
+
+ dev_private->timer_divisor_1 = 0;
+ dev_private->timer_divisor_2 = 0;
+
+ pci9111_timer_set(dev);
+
+ return 0;
+}
+
+/* Attach */
+/* - Register PCI device */
+/* - Declare device driver capability */
+
+static int pci9111_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *subdevice;
+ unsigned long io_base, io_range, lcr_io_base, lcr_io_range;
+ struct pci_dev *pci_device;
+ int error, i;
+ const struct pci9111_board *board;
+
+ if (alloc_private(dev, sizeof(struct pci9111_private_data)) < 0) {
+ return -ENOMEM;
+ }
+ /* Probe the device to determine what device in the series it is. */
+
+ printk("comedi%d: " PCI9111_DRIVER_NAME " driver\n", dev->minor);
+
+ for (pci_device = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pci_device != NULL;
+ pci_device =
+ pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_device)) {
+ if (pci_device->vendor == PCI_VENDOR_ID_ADLINK) {
+ for (i = 0; i < pci9111_board_nbr; i++) {
+ if (pci9111_boards[i].device_id ==
+ pci_device->device) {
+ /* was a particular bus/slot requested? */
+ if ((it->options[0] != 0)
+ || (it->options[1] != 0)) {
+ /* are we on the wrong bus/slot? */
+ if (pci_device->bus->number !=
+ it->options[0]
+ || PCI_SLOT(pci_device->
+ devfn) !=
+ it->options[1]) {
+ continue;
+ }
+ }
+
+ dev->board_ptr = pci9111_boards + i;
+ board = (struct pci9111_board *) dev->
+ board_ptr;
+ dev_private->pci_device = pci_device;
+ goto found;
+ }
+ }
+ }
+ }
+
+ printk("comedi%d: no supported board found! (req. bus/slot : %d/%d)\n",
+ dev->minor, it->options[0], it->options[1]);
+ return -EIO;
+
+ found:
+
+ printk("comedi%d: found %s (b:s:f=%d:%d:%d) , irq=%d\n",
+ dev->minor,
+ pci9111_boards[i].name,
+ pci_device->bus->number,
+ PCI_SLOT(pci_device->devfn),
+ PCI_FUNC(pci_device->devfn), pci_device->irq);
+
+ /* TODO: Warn about non-tested boards. */
+
+ switch (board->device_id) {
+ };
+
+ /* Read local configuration register base address [PCI_BASE_ADDRESS #1]. */
+
+ lcr_io_base = pci_resource_start(pci_device, 1);
+ lcr_io_range = pci_resource_len(pci_device, 1);
+
+ printk("comedi%d: local configuration registers at address 0x%4lx [0x%4lx]\n", dev->minor, lcr_io_base, lcr_io_range);
+
+ /* Enable PCI device and request regions */
+ if (comedi_pci_enable(pci_device, PCI9111_DRIVER_NAME) < 0) {
+ printk("comedi%d: Failed to enable PCI device and request regions\n", dev->minor);
+ return -EIO;
+ }
+ /* Read PCI6308 register base address [PCI_BASE_ADDRESS #2]. */
+
+ io_base = pci_resource_start(pci_device, 2);
+ io_range = pci_resource_len(pci_device, 2);
+
+ printk("comedi%d: 6503 registers at address 0x%4lx [0x%4lx]\n",
+ dev->minor, io_base, io_range);
+
+ dev->iobase = io_base;
+ dev->board_name = board->name;
+ dev_private->io_range = io_range;
+ dev_private->is_valid = 0;
+ dev_private->lcr_io_base = lcr_io_base;
+ dev_private->lcr_io_range = lcr_io_range;
+
+ pci9111_reset(dev);
+
+ /* Irq setup */
+
+ dev->irq = 0;
+ if (pci_device->irq > 0) {
+ if (comedi_request_irq(pci_device->irq,
+ pci9111_interrupt,
+ IRQF_SHARED, PCI9111_DRIVER_NAME, dev) != 0) {
+ printk("comedi%d: unable to allocate irq %u\n",
+ dev->minor, pci_device->irq);
+ return -EINVAL;
+ }
+ }
+ dev->irq = pci_device->irq;
+
+ /* TODO: Add external multiplexer setup (according to option[2]). */
+
+ if ((error = alloc_subdevices(dev, 4)) < 0)
+ return error;
+
+ subdevice = dev->subdevices + 0;
+ dev->read_subdev = subdevice;
+
+ subdevice->type = COMEDI_SUBD_AI;
+ subdevice->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_CMD_READ;
+
+ /* TODO: Add external multiplexer data */
+ /* if (devpriv->usemux) { subdevice->n_chan = devpriv->usemux; } */
+ /* else { subdevice->n_chan = this_board->n_aichan; } */
+
+ subdevice->n_chan = board->ai_channel_nbr;
+ subdevice->maxdata = board->ai_resolution_mask;
+ subdevice->len_chanlist = board->ai_channel_nbr;
+ subdevice->range_table = board->ai_range_list;
+ subdevice->cancel = pci9111_ai_cancel;
+ subdevice->insn_read = pci9111_ai_insn_read;
+ subdevice->do_cmdtest = pci9111_ai_do_cmd_test;
+ subdevice->do_cmd = pci9111_ai_do_cmd;
+ subdevice->munge = pci9111_ai_munge;
+
+ subdevice = dev->subdevices + 1;
+ subdevice->type = COMEDI_SUBD_AO;
+ subdevice->subdev_flags = SDF_WRITABLE | SDF_COMMON;
+ subdevice->n_chan = board->ao_channel_nbr;
+ subdevice->maxdata = board->ao_resolution_mask;
+ subdevice->len_chanlist = board->ao_channel_nbr;
+ subdevice->range_table = board->ao_range_list;
+ subdevice->insn_write = pci9111_ao_insn_write;
+ subdevice->insn_read = pci9111_ao_insn_read;
+
+ subdevice = dev->subdevices + 2;
+ subdevice->type = COMEDI_SUBD_DI;
+ subdevice->subdev_flags = SDF_READABLE;
+ subdevice->n_chan = PCI9111_DI_CHANNEL_NBR;
+ subdevice->maxdata = 1;
+ subdevice->range_table = &range_digital;
+ subdevice->insn_bits = pci9111_di_insn_bits;
+
+ subdevice = dev->subdevices + 3;
+ subdevice->type = COMEDI_SUBD_DO;
+ subdevice->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ subdevice->n_chan = PCI9111_DO_CHANNEL_NBR;
+ subdevice->maxdata = 1;
+ subdevice->range_table = &range_digital;
+ subdevice->insn_bits = pci9111_do_insn_bits;
+
+ dev_private->is_valid = 1;
+
+ return 0;
+}
+
+/* Detach */
+
+static int pci9111_detach(struct comedi_device * dev)
+{
+ /* Reset device */
+
+ if (dev->private != 0) {
+ if (dev_private->is_valid)
+ pci9111_reset(dev);
+
+ }
+ /* Release previously allocated irq */
+
+ if (dev->irq != 0) {
+ comedi_free_irq(dev->irq, dev);
+ }
+
+ if (dev_private != 0 && dev_private->pci_device != 0) {
+ if (dev->iobase) {
+ comedi_pci_disable(dev_private->pci_device);
+ }
+ pci_dev_put(dev_private->pci_device);
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/adl_pci9118.c b/drivers/staging/comedi/drivers/adl_pci9118.c
new file mode 100644
index 000000000000..278cf30cc4a3
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adl_pci9118.c
@@ -0,0 +1,2100 @@
+/*
+ * comedi/drivers/adl_pci9118.c
+ *
+ * hardware driver for ADLink cards:
+ * card: PCI-9118DG, PCI-9118HG, PCI-9118HR
+ * driver: pci9118dg, pci9118hg, pci9118hr
+ *
+ * Author: Michal Dobes <dobes@tesnet.cz>
+ *
+*/
+/*
+Driver: adl_pci9118
+Description: Adlink PCI-9118DG, PCI-9118HG, PCI-9118HR
+Author: Michal Dobes <dobes@tesnet.cz>
+Devices: [ADLink] PCI-9118DG (pci9118dg), PCI-9118HG (pci9118hg),
+ PCI-9118HR (pci9118hr)
+Status: works
+
+This driver supports AI, AO, DI and DO subdevices.
+AI subdevice supports cmd and insn interface,
+other subdevices support only insn interface.
+For AI:
+- If cmd->scan_begin_src=TRIG_EXT then trigger input is TGIN (pin 46).
+- If cmd->convert_src=TRIG_EXT then trigger input is EXTTRG (pin 44).
+- If cmd->start_src/stop_src=TRIG_EXT then trigger input is TGIN (pin 46).
+- It is not neccessary to have cmd.scan_end_arg=cmd.chanlist_len but
+ cmd.scan_end_arg modulo cmd.chanlist_len must by 0.
+- If return value of cmdtest is 5 then you've bad channel list
+ (it isn't possible mixture S.E. and DIFF inputs or bipolar and unipolar
+ ranges).
+
+There are some hardware limitations:
+a) You cann't use mixture of unipolar/bipoar ranges or differencial/single
+ ended inputs.
+b) DMA transfers must have the length aligned to two samples (32 bit),
+ so there is some problems if cmd->chanlist_len is odd. This driver tries
+ bypass this with adding one sample to the end of the every scan and discard
+ it on output but this cann't be used if cmd->scan_begin_src=TRIG_FOLLOW
+ and is used flag TRIG_WAKE_EOS, then driver switch to interrupt driven mode
+ with interrupt after every sample.
+c) If isn't used DMA then you can use only mode where
+ cmd->scan_begin_src=TRIG_FOLLOW.
+
+Configuration options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, then first available PCI
+ card will be used.
+ [2] - 0= standard 8 DIFF/16 SE channels configuration
+ n= external multiplexer connected, 1<=n<=256
+ [3] - 0=autoselect DMA or EOC interrupts operation
+ 1=disable DMA mode
+ 3=disable DMA and INT, only insn interface will work
+ [4] - sample&hold signal - card can generate signal for external S&H board
+ 0=use SSHO (pin 45) signal is generated in onboard hardware S&H logic
+ 0!=use ADCHN7 (pin 23) signal is generated from driver, number
+ say how long delay is requested in ns and sign polarity of the hold
+ (in this case external multiplexor can serve only 128 channels)
+ [5] - 0=stop measure on all hardware errors
+ 2|=ignore ADOR - A/D Overrun status
+ 8|=ignore Bover - A/D Burst Mode Overrun status
+ 256|=ignore nFull - A/D FIFO Full status
+
+*/
+#include "../comedidev.h"
+#include "../pci_ids.h"
+
+#include <linux/delay.h>
+
+#include "amcc_s5933.h"
+#include "8253.h"
+#include "comedi_pci.h"
+#include "comedi_fc.h"
+
+/* paranoid checks are broken */
+#undef PCI9118_PARANOIDCHECK /* if defined, then is used code which control correct channel number on every 12 bit sample */
+
+#undef PCI9118_EXTDEBUG /* if defined then driver prints a lot of messages */
+
+#undef DPRINTK
+#ifdef PCI9118_EXTDEBUG
+#define DPRINTK(fmt, args...) rt_printk(fmt, ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#define IORANGE_9118 64 /* I hope */
+#define PCI9118_CHANLEN 255 /* len of chanlist, some source say 256, but reality looks like 255 :-( */
+
+#define PCI9118_CNT0 0x00 /* R/W: 8254 couter 0 */
+#define PCI9118_CNT1 0x04 /* R/W: 8254 couter 0 */
+#define PCI9118_CNT2 0x08 /* R/W: 8254 couter 0 */
+#define PCI9118_CNTCTRL 0x0c /* W: 8254 counter control */
+#define PCI9118_AD_DATA 0x10 /* R: A/D data */
+#define PCI9118_DA1 0x10 /* W: D/A registers */
+#define PCI9118_DA2 0x14
+#define PCI9118_ADSTAT 0x18 /* R: A/D status register */
+#define PCI9118_ADCNTRL 0x18 /* W: A/D control register */
+#define PCI9118_DI 0x1c /* R: digi input register */
+#define PCI9118_DO 0x1c /* W: digi output register */
+#define PCI9118_SOFTTRG 0x20 /* W: soft trigger for A/D */
+#define PCI9118_GAIN 0x24 /* W: A/D gain/channel register */
+#define PCI9118_BURST 0x28 /* W: A/D burst number register */
+#define PCI9118_SCANMOD 0x2c /* W: A/D auto scan mode */
+#define PCI9118_ADFUNC 0x30 /* W: A/D function register */
+#define PCI9118_DELFIFO 0x34 /* W: A/D data FIFO reset */
+#define PCI9118_INTSRC 0x38 /* R: interrupt reason register */
+#define PCI9118_INTCTRL 0x38 /* W: interrupt control register */
+
+// bits from A/D control register (PCI9118_ADCNTRL)
+#define AdControl_UniP 0x80 /* 1=bipolar, 0=unipolar */
+#define AdControl_Diff 0x40 /* 1=differential, 0= single end inputs */
+#define AdControl_SoftG 0x20 /* 1=8254 counter works, 0=counter stops */
+#define AdControl_ExtG 0x10 /* 1=8254 countrol controlled by TGIN(pin 46), 0=controled by SoftG */
+#define AdControl_ExtM 0x08 /* 1=external hardware trigger (pin 44), 0=internal trigger */
+#define AdControl_TmrTr 0x04 /* 1=8254 is iternal trigger source, 0=software trigger is source (register PCI9118_SOFTTRG) */
+#define AdControl_Int 0x02 /* 1=enable INT, 0=disable */
+#define AdControl_Dma 0x01 /* 1=enable DMA, 0=disable */
+
+// bits from A/D function register (PCI9118_ADFUNC)
+#define AdFunction_PDTrg 0x80 /* 1=positive, 0=negative digital trigger (only positive is correct) */
+#define AdFunction_PETrg 0x40 /* 1=positive, 0=negative external trigger (only positive is correct) */
+#define AdFunction_BSSH 0x20 /* 1=with sample&hold, 0=without */
+#define AdFunction_BM 0x10 /* 1=burst mode, 0=normal mode */
+#define AdFunction_BS 0x08 /* 1=burst mode start, 0=burst mode stop */
+#define AdFunction_PM 0x04 /* 1=post trigger mode, 0=not post trigger */
+#define AdFunction_AM 0x02 /* 1=about trigger mode, 0=not about trigger */
+#define AdFunction_Start 0x01 /* 1=trigger start, 0=trigger stop */
+
+// bits from A/D status register (PCI9118_ADSTAT)
+#define AdStatus_nFull 0x100 /* 0=FIFO full (fatal), 1=not full */
+#define AdStatus_nHfull 0x080 /* 0=FIFO half full, 1=FIFO not half full */
+#define AdStatus_nEpty 0x040 /* 0=FIFO empty, 1=FIFO not empty */
+#define AdStatus_Acmp 0x020 /* */
+#define AdStatus_DTH 0x010 /* 1=external digital trigger */
+#define AdStatus_Bover 0x008 /* 1=burst mode overrun (fatal) */
+#define AdStatus_ADOS 0x004 /* 1=A/D over speed (warning) */
+#define AdStatus_ADOR 0x002 /* 1=A/D overrun (fatal) */
+#define AdStatus_ADrdy 0x001 /* 1=A/D already ready, 0=not ready */
+
+// bits for interrupt reason and control (PCI9118_INTSRC, PCI9118_INTCTRL)
+// 1=interrupt occur, enable source, 0=interrupt not occur, disable source
+#define Int_Timer 0x08 /* timer interrupt */
+#define Int_About 0x04 /* about trigger complete */
+#define Int_Hfull 0x02 /* A/D FIFO hlaf full */
+#define Int_DTrg 0x01 /* external digital trigger */
+
+#define START_AI_EXT 0x01 /* start measure on external trigger */
+#define STOP_AI_EXT 0x02 /* stop measure on external trigger */
+#define START_AI_INT 0x04 /* start measure on internal trigger */
+#define STOP_AI_INT 0x08 /* stop measure on internal trigger */
+
+#define EXTTRG_AI 0 /* ext trg is used by AI */
+
+static const struct comedi_lrange range_pci9118dg_hr = { 8, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range_pci9118hg = { 8, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.005),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01)
+ }
+};
+
+#define PCI9118_BIPOLAR_RANGES 4 /* used for test on mixture of BIP/UNI ranges */
+
+static int pci9118_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pci9118_detach(struct comedi_device * dev);
+
+struct boardtype {
+ const char *name; // board name
+ int vendor_id; // PCI vendor a device ID of card
+ int device_id;
+ int iorange_amcc; // iorange for own S5933 region
+ int iorange_9118; // pass thru card region size
+ int n_aichan; // num of A/D chans
+ int n_aichand; // num of A/D chans in diff mode
+ int mux_aichan; // num of A/D chans with external multiplexor
+ int n_aichanlist; // len of chanlist
+ int n_aochan; // num of D/A chans
+ int ai_maxdata; // resolution of A/D
+ int ao_maxdata; // resolution of D/A
+ const struct comedi_lrange *rangelist_ai; // rangelist for A/D
+ const struct comedi_lrange *rangelist_ao; // rangelist for D/A
+ unsigned int ai_ns_min; // max sample speed of card v ns
+ unsigned int ai_pacer_min; // minimal pacer value (c1*c2 or c1 in burst)
+ int half_fifo_size; // size of FIFO/2
+
+};
+
+static DEFINE_PCI_DEVICE_TABLE(pci9118_pci_table) = {
+ {PCI_VENDOR_ID_AMCC, 0x80d9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, pci9118_pci_table);
+
+static const struct boardtype boardtypes[] = {
+ {"pci9118dg", PCI_VENDOR_ID_AMCC, 0x80d9,
+ AMCC_OP_REG_SIZE, IORANGE_9118,
+ 16, 8, 256, PCI9118_CHANLEN, 2, 0x0fff, 0x0fff,
+ &range_pci9118dg_hr, &range_bipolar10,
+ 3000, 12, 512},
+ {"pci9118hg", PCI_VENDOR_ID_AMCC, 0x80d9,
+ AMCC_OP_REG_SIZE, IORANGE_9118,
+ 16, 8, 256, PCI9118_CHANLEN, 2, 0x0fff, 0x0fff,
+ &range_pci9118hg, &range_bipolar10,
+ 3000, 12, 512},
+ {"pci9118hr", PCI_VENDOR_ID_AMCC, 0x80d9,
+ AMCC_OP_REG_SIZE, IORANGE_9118,
+ 16, 8, 256, PCI9118_CHANLEN, 2, 0xffff, 0x0fff,
+ &range_pci9118dg_hr, &range_bipolar10,
+ 10000, 40, 512},
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype))
+
+static struct comedi_driver driver_pci9118 = {
+ driver_name:"adl_pci9118",
+ module:THIS_MODULE,
+ attach:pci9118_attach,
+ detach:pci9118_detach,
+ num_names:n_boardtypes,
+ board_name:&boardtypes[0].name,
+ offset:sizeof(struct boardtype),
+};
+
+COMEDI_PCI_INITCLEANUP(driver_pci9118, pci9118_pci_table);
+
+struct pci9118_private {
+ unsigned long iobase_a; // base+size for AMCC chip
+ unsigned int master; // master capable
+ struct pci_dev *pcidev; // ptr to actual pcidev
+ unsigned int usemux; // we want to use external multiplexor!
+#ifdef PCI9118_PARANOIDCHECK
+ unsigned short chanlist[PCI9118_CHANLEN + 1]; // list of scaned channel
+ unsigned char chanlistlen; // number of scanlist
+#endif
+ unsigned char AdControlReg; // A/D control register
+ unsigned char IntControlReg; // Interrupt control register
+ unsigned char AdFunctionReg; // A/D function register
+ char valid; // driver is ok
+ char ai_neverending; // we do unlimited AI
+ unsigned int i8254_osc_base; // frequence of onboard oscilator
+ unsigned int ai_do; // what do AI? 0=nothing, 1 to 4 mode
+ unsigned int ai_act_scan; // how many scans we finished
+ unsigned int ai_buf_ptr; // data buffer ptr in samples
+ unsigned int ai_n_chan; // how many channels is measured
+ unsigned int ai_n_scanlen; // len of actual scanlist
+ unsigned int ai_n_realscanlen; // what we must transfer for one outgoing scan include front/back adds
+ unsigned int ai_act_dmapos; // position in actual real stream
+ unsigned int ai_add_front; // how many channels we must add before scan to satisfy S&H?
+ unsigned int ai_add_back; // how many channels we must add before scan to satisfy DMA?
+ unsigned int *ai_chanlist; // actaul chanlist
+ unsigned int ai_timer1;
+ unsigned int ai_timer2;
+ unsigned int ai_flags;
+ char ai12_startstop; // measure can start/stop on external trigger
+ unsigned int ai_divisor1, ai_divisor2; // divisors for start of measure on external start
+ unsigned int ai_data_len;
+ short *ai_data;
+ short ao_data[2]; // data output buffer
+ unsigned int ai_scans; // number of scans to do
+ char dma_doublebuf; // we can use double buffring
+ unsigned int dma_actbuf; // which buffer is used now
+ short *dmabuf_virt[2]; // pointers to begin of DMA buffer
+ unsigned long dmabuf_hw[2]; // hw address of DMA buff
+ unsigned int dmabuf_size[2]; // size of dma buffer in bytes
+ unsigned int dmabuf_use_size[2]; // which size we may now used for transfer
+ unsigned int dmabuf_used_size[2]; // which size was trully used
+ unsigned int dmabuf_panic_size[2];
+ unsigned int dmabuf_samples[2]; // size in samples
+ int dmabuf_pages[2]; // number of pages in buffer
+ unsigned char cnt0_users; // bit field of 8254 CNT0 users (0-unused, 1-AO, 2-DI, 3-DO)
+ unsigned char exttrg_users; // bit field of external trigger users (0-AI, 1-AO, 2-DI, 3-DO)
+ unsigned int cnt0_divisor; // actual CNT0 divisor
+ void (*int_ai_func) (struct comedi_device *, struct comedi_subdevice *, unsigned short, unsigned int, unsigned short); // ptr to actual interrupt AI function
+ unsigned char ai16bits; // =1 16 bit card
+ unsigned char usedma; // =1 use DMA transfer and not INT
+ unsigned char useeoshandle; // =1 change WAKE_EOS DMA transfer to fit on every second
+ unsigned char usessh; // =1 turn on S&H support
+ int softsshdelay; // >0 use software S&H, numer is requested delay in ns
+ unsigned char softsshsample; // polarity of S&H signal in sample state
+ unsigned char softsshhold; // polarity of S&H signal in hold state
+ unsigned int ai_maskerr; // which warning was printed
+ unsigned int ai_maskharderr; // on which error bits stops
+ unsigned int ai_inttrig_start; // TRIG_INT for start
+};
+
+#define devpriv ((struct pci9118_private *)dev->private)
+#define this_board ((struct boardtype *)dev->board_ptr)
+
+/*
+==============================================================================
+*/
+
+static int check_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
+ int n_chan, unsigned int *chanlist, int frontadd, int backadd);
+static int setup_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
+ int n_chan, unsigned int *chanlist, int rot, int frontadd, int backadd,
+ int usedma, char eoshandle);
+static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
+ unsigned int divisor2);
+static int pci9118_reset(struct comedi_device * dev);
+static int pci9118_exttrg_add(struct comedi_device * dev, unsigned char source);
+static int pci9118_exttrg_del(struct comedi_device * dev, unsigned char source);
+static int pci9118_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static void pci9118_calc_divisors(char mode, struct comedi_device * dev,
+ struct comedi_subdevice * s, unsigned int *tim1, unsigned int *tim2,
+ unsigned int flags, int chans, unsigned int *div1, unsigned int *div2,
+ char usessh, unsigned int chnsshfront);
+
+/*
+==============================================================================
+*/
+static int pci9118_insn_read_ai(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ int n, timeout;
+
+ devpriv->AdControlReg = AdControl_Int & 0xff;
+ devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg;
+ outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); // positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop
+
+ if (!setup_channel_list(dev, s, 1, &insn->chanspec, 0, 0, 0, 0, 0))
+ return -EINVAL;
+
+ outl(0, dev->iobase + PCI9118_DELFIFO); // flush FIFO
+
+ for (n = 0; n < insn->n; n++) {
+ outw(0, dev->iobase + PCI9118_SOFTTRG); /* start conversion */
+ comedi_udelay(2);
+ timeout = 100;
+ while (timeout--) {
+ if (inl(dev->iobase + PCI9118_ADSTAT) & AdStatus_ADrdy)
+ goto conv_finish;
+ comedi_udelay(1);
+ }
+
+ comedi_error(dev, "A/D insn timeout");
+ data[n] = 0;
+ outl(0, dev->iobase + PCI9118_DELFIFO); // flush FIFO
+ return -ETIME;
+
+ conv_finish:
+ if (devpriv->ai16bits) {
+ data[n] =
+ (inl(dev->iobase +
+ PCI9118_AD_DATA) & 0xffff) ^ 0x8000;
+ } else {
+ data[n] =
+ (inw(dev->iobase +
+ PCI9118_AD_DATA) >> 4) & 0xfff;
+ }
+ }
+
+ outl(0, dev->iobase + PCI9118_DELFIFO); // flush FIFO
+ return n;
+
+}
+
+/*
+==============================================================================
+*/
+static int pci9118_insn_write_ao(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, chanreg, ch;
+
+ ch = CR_CHAN(insn->chanspec);
+ if (ch) {
+ chanreg = PCI9118_DA2;
+ } else {
+ chanreg = PCI9118_DA1;
+ }
+
+ for (n = 0; n < insn->n; n++) {
+ outl(data[n], dev->iobase + chanreg);
+ devpriv->ao_data[ch] = data[n];
+ }
+
+ return n;
+}
+
+/*
+==============================================================================
+*/
+static int pci9118_insn_read_ao(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, chan;
+
+ chan = CR_CHAN(insn->chanspec);
+ for (n = 0; n < insn->n; n++)
+ data[n] = devpriv->ao_data[chan];
+
+ return n;
+}
+
+/*
+==============================================================================
+*/
+static int pci9118_insn_bits_di(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[1] = inl(dev->iobase + PCI9118_DI) & 0xf;
+
+ return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci9118_insn_bits_do(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ outl(s->state & 0x0f, dev->iobase + PCI9118_DO);
+ }
+ data[1] = s->state;
+
+ return 2;
+}
+
+/*
+==============================================================================
+*/
+static void interrupt_pci9118_ai_mode4_switch(struct comedi_device * dev)
+{
+ devpriv->AdFunctionReg =
+ AdFunction_PDTrg | AdFunction_PETrg | AdFunction_AM;
+ outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
+ outl(0x30, dev->iobase + PCI9118_CNTCTRL);
+ outl((devpriv->dmabuf_hw[1 - devpriv->dma_actbuf] >> 1) & 0xff,
+ dev->iobase + PCI9118_CNT0);
+ outl((devpriv->dmabuf_hw[1 - devpriv->dma_actbuf] >> 9) & 0xff,
+ dev->iobase + PCI9118_CNT0);
+ devpriv->AdFunctionReg |= AdFunction_Start;
+ outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
+}
+
+static unsigned int defragment_dma_buffer(struct comedi_device * dev,
+ struct comedi_subdevice * s, short * dma_buffer, unsigned int num_samples)
+{
+ unsigned int i = 0, j = 0;
+ unsigned int start_pos = devpriv->ai_add_front,
+ stop_pos = devpriv->ai_add_front + devpriv->ai_n_chan;
+ unsigned int raw_scanlen = devpriv->ai_add_front + devpriv->ai_n_chan +
+ devpriv->ai_add_back;
+
+ for (i = 0; i < num_samples; i++) {
+ if (devpriv->ai_act_dmapos >= start_pos &&
+ devpriv->ai_act_dmapos < stop_pos) {
+ dma_buffer[j++] = dma_buffer[i];
+ }
+ devpriv->ai_act_dmapos++;
+ devpriv->ai_act_dmapos %= raw_scanlen;
+ }
+
+ return j;
+}
+
+/*
+==============================================================================
+*/
+static unsigned int move_block_from_dma(struct comedi_device * dev,
+ struct comedi_subdevice * s, short * dma_buffer, unsigned int num_samples)
+{
+ unsigned int num_bytes;
+
+ num_samples = defragment_dma_buffer(dev, s, dma_buffer, num_samples);
+ devpriv->ai_act_scan +=
+ (s->async->cur_chan + num_samples) / devpriv->ai_n_scanlen;
+ s->async->cur_chan += num_samples;
+ s->async->cur_chan %= devpriv->ai_n_scanlen;
+ num_bytes =
+ cfc_write_array_to_buffer(s, dma_buffer,
+ num_samples * sizeof(short));
+ if (num_bytes < num_samples * sizeof(short))
+ return -1;
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static char pci9118_decode_error_status(struct comedi_device * dev,
+ struct comedi_subdevice * s, unsigned char m)
+{
+ if (m & 0x100) {
+ comedi_error(dev, "A/D FIFO Full status (Fatal Error!)");
+ devpriv->ai_maskerr &= ~0x100L;
+ }
+ if (m & 0x008) {
+ comedi_error(dev,
+ "A/D Burst Mode Overrun Status (Fatal Error!)");
+ devpriv->ai_maskerr &= ~0x008L;
+ }
+ if (m & 0x004) {
+ comedi_error(dev, "A/D Over Speed Status (Warning!)");
+ devpriv->ai_maskerr &= ~0x004L;
+ }
+ if (m & 0x002) {
+ comedi_error(dev, "A/D Overrun Status (Fatal Error!)");
+ devpriv->ai_maskerr &= ~0x002L;
+ }
+ if (m & devpriv->ai_maskharderr) {
+ s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ pci9118_ai_cancel(dev, s);
+ comedi_event(dev, s);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void pci9118_ai_munge(struct comedi_device * dev, struct comedi_subdevice * s,
+ void *data, unsigned int num_bytes, unsigned int start_chan_index)
+{
+ unsigned int i, num_samples = num_bytes / sizeof(short);
+ short *array = data;
+
+ for (i = 0; i < num_samples; i++) {
+ if (devpriv->usedma)
+ array[i] = be16_to_cpu(array[i]);
+ if (devpriv->ai16bits) {
+ array[i] ^= 0x8000;
+ } else {
+ array[i] = (array[i] >> 4) & 0x0fff;
+ }
+ }
+}
+
+/*
+==============================================================================
+*/
+static void interrupt_pci9118_ai_onesample(struct comedi_device * dev,
+ struct comedi_subdevice * s, unsigned short int_adstat, unsigned int int_amcc,
+ unsigned short int_daq)
+{
+ register short sampl;
+
+ s->async->events = 0;
+
+ if (int_adstat & devpriv->ai_maskerr)
+ if (pci9118_decode_error_status(dev, s, int_adstat))
+ return;
+
+ sampl = inw(dev->iobase + PCI9118_AD_DATA);
+
+#ifdef PCI9118_PARANOIDCHECK
+ if (devpriv->ai16bits == 0) {
+ if ((sampl & 0x000f) != devpriv->chanlist[s->async->cur_chan]) { // data dropout!
+ rt_printk
+ ("comedi: A/D SAMPL - data dropout: received channel %d, expected %d!\n",
+ sampl & 0x000f,
+ devpriv->chanlist[s->async->cur_chan]);
+ s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ pci9118_ai_cancel(dev, s);
+ comedi_event(dev, s);
+ return;
+ }
+ }
+#endif
+ cfc_write_to_buffer(s, sampl);
+ s->async->cur_chan++;
+ if (s->async->cur_chan >= devpriv->ai_n_scanlen) { /* one scan done */
+ s->async->cur_chan %= devpriv->ai_n_scanlen;
+ devpriv->ai_act_scan++;
+ if (!(devpriv->ai_neverending))
+ if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
+ pci9118_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ }
+ }
+
+ if (s->async->events)
+ comedi_event(dev, s);
+}
+
+/*
+==============================================================================
+*/
+static void interrupt_pci9118_ai_dma(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned short int_adstat, unsigned int int_amcc,
+ unsigned short int_daq)
+{
+ unsigned int next_dma_buf, samplesinbuf, sampls, m;
+
+ if (int_amcc & MASTER_ABORT_INT) {
+ comedi_error(dev, "AMCC IRQ - MASTER DMA ABORT!");
+ s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ pci9118_ai_cancel(dev, s);
+ comedi_event(dev, s);
+ return;
+ }
+
+ if (int_amcc & TARGET_ABORT_INT) {
+ comedi_error(dev, "AMCC IRQ - TARGET DMA ABORT!");
+ s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ pci9118_ai_cancel(dev, s);
+ comedi_event(dev, s);
+ return;
+ }
+
+ if (int_adstat & devpriv->ai_maskerr)
+// if (int_adstat & 0x106)
+ if (pci9118_decode_error_status(dev, s, int_adstat))
+ return;
+
+ samplesinbuf = devpriv->dmabuf_use_size[devpriv->dma_actbuf] >> 1; // number of received real samples
+// DPRINTK("dma_actbuf=%d\n",devpriv->dma_actbuf);
+
+ if (devpriv->dma_doublebuf) { // switch DMA buffers if is used double buffering
+ next_dma_buf = 1 - devpriv->dma_actbuf;
+ outl(devpriv->dmabuf_hw[next_dma_buf],
+ devpriv->iobase_a + AMCC_OP_REG_MWAR);
+ outl(devpriv->dmabuf_use_size[next_dma_buf],
+ devpriv->iobase_a + AMCC_OP_REG_MWTC);
+ devpriv->dmabuf_used_size[next_dma_buf] =
+ devpriv->dmabuf_use_size[next_dma_buf];
+ if (devpriv->ai_do == 4)
+ interrupt_pci9118_ai_mode4_switch(dev);
+ }
+
+ if (samplesinbuf) {
+ m = devpriv->ai_data_len >> 1; // how many samples is to end of buffer
+// DPRINTK("samps=%d m=%d %d %d\n",samplesinbuf,m,s->async->buf_int_count,s->async->buf_int_ptr);
+ sampls = m;
+ move_block_from_dma(dev, s,
+ devpriv->dmabuf_virt[devpriv->dma_actbuf],
+ samplesinbuf);
+ m = m - sampls; // m= how many samples was transfered
+ }
+// DPRINTK("YYY\n");
+
+ if (!devpriv->ai_neverending)
+ if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
+ pci9118_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ }
+
+ if (devpriv->dma_doublebuf) { // switch dma buffers
+ devpriv->dma_actbuf = 1 - devpriv->dma_actbuf;
+ } else { // restart DMA if is not used double buffering
+ outl(devpriv->dmabuf_hw[0],
+ devpriv->iobase_a + AMCC_OP_REG_MWAR);
+ outl(devpriv->dmabuf_use_size[0],
+ devpriv->iobase_a + AMCC_OP_REG_MWTC);
+ if (devpriv->ai_do == 4)
+ interrupt_pci9118_ai_mode4_switch(dev);
+ }
+
+ comedi_event(dev, s);
+}
+
+/*
+==============================================================================
+*/
+static irqreturn_t interrupt_pci9118(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ unsigned int int_daq = 0, int_amcc, int_adstat;
+
+ if (!dev->attached)
+ return IRQ_NONE; // not fully initialized
+
+ int_daq = inl(dev->iobase + PCI9118_INTSRC) & 0xf; // get IRQ reasons from card
+ int_amcc = inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR); // get INT register from AMCC chip
+
+// DPRINTK("INT daq=0x%01x amcc=0x%08x MWAR=0x%08x MWTC=0x%08x ADSTAT=0x%02x ai_do=%d\n", int_daq, int_amcc, inl(devpriv->iobase_a+AMCC_OP_REG_MWAR), inl(devpriv->iobase_a+AMCC_OP_REG_MWTC), inw(dev->iobase+PCI9118_ADSTAT)&0x1ff,devpriv->ai_do);
+
+ if ((!int_daq) && (!(int_amcc & ANY_S593X_INT)))
+ return IRQ_NONE; // interrupt from other source
+
+ outl(int_amcc | 0x00ff0000, devpriv->iobase_a + AMCC_OP_REG_INTCSR); // shutdown IRQ reasons in AMCC
+
+ int_adstat = inw(dev->iobase + PCI9118_ADSTAT) & 0x1ff; // get STATUS register
+
+ if (devpriv->ai_do) {
+ if (devpriv->ai12_startstop)
+ if ((int_adstat & AdStatus_DTH) && (int_daq & Int_DTrg)) { // start stop of measure
+ if (devpriv->ai12_startstop & START_AI_EXT) {
+ devpriv->ai12_startstop &=
+ ~START_AI_EXT;
+ if (!(devpriv->ai12_startstop &
+ STOP_AI_EXT))
+ pci9118_exttrg_del(dev, EXTTRG_AI); // deactivate EXT trigger
+ start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1, devpriv->ai_divisor2); // start pacer
+ outl(devpriv->AdControlReg,
+ dev->iobase + PCI9118_ADCNTRL);
+ } else {
+ if (devpriv->
+ ai12_startstop & STOP_AI_EXT) {
+ devpriv->ai12_startstop &=
+ ~STOP_AI_EXT;
+ pci9118_exttrg_del(dev, EXTTRG_AI); // deactivate EXT trigger
+ devpriv->ai_neverending = 0; //well, on next interrupt from DMA/EOC measure will stop
+ }
+ }
+ }
+
+ (devpriv->int_ai_func) (dev, dev->subdevices + 0, int_adstat,
+ int_amcc, int_daq);
+
+ }
+ return IRQ_HANDLED;
+}
+
+/*
+==============================================================================
+*/
+static int pci9118_ai_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trignum)
+{
+ if (trignum != devpriv->ai_inttrig_start)
+ return -EINVAL;
+
+ devpriv->ai12_startstop &= ~START_AI_INT;
+ s->async->inttrig = NULL;
+
+ outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
+ outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
+ if (devpriv->ai_do != 3) {
+ start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1,
+ devpriv->ai_divisor2);
+ devpriv->AdControlReg |= AdControl_SoftG;
+ }
+ outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL);
+
+ return 1;
+}
+
+/*
+==============================================================================
+*/
+static int pci9118_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp, divisor1, divisor2;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_EXT | TRIG_INT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ if (devpriv->master) {
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT | TRIG_FOLLOW;
+ } else {
+ cmd->scan_begin_src &= TRIG_FOLLOW;
+ }
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ if (devpriv->master) {
+ cmd->convert_src &= TRIG_TIMER | TRIG_EXT | TRIG_NOW;
+ } else {
+ cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+ }
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE | TRIG_EXT;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (cmd->start_src != TRIG_NOW &&
+ cmd->start_src != TRIG_INT && cmd->start_src != TRIG_EXT) {
+ cmd->start_src = TRIG_NOW;
+ err++;
+ }
+
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_EXT &&
+ cmd->scan_begin_src != TRIG_INT &&
+ cmd->scan_begin_src != TRIG_FOLLOW) {
+ cmd->scan_begin_src = TRIG_FOLLOW;
+ err++;
+ }
+
+ if (cmd->convert_src != TRIG_TIMER &&
+ cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW) {
+ cmd->convert_src = TRIG_TIMER;
+ err++;
+ }
+
+ if (cmd->scan_end_src != TRIG_COUNT) {
+ cmd->scan_end_src = TRIG_COUNT;
+ err++;
+ }
+
+ if (cmd->stop_src != TRIG_NONE &&
+ cmd->stop_src != TRIG_COUNT &&
+ cmd->stop_src != TRIG_INT && cmd->stop_src != TRIG_EXT) {
+ cmd->stop_src = TRIG_COUNT;
+ err++;
+ }
+
+ if (cmd->start_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) {
+ cmd->start_src = TRIG_NOW;
+ err++;
+ }
+
+ if (cmd->start_src == TRIG_INT && cmd->scan_begin_src == TRIG_INT) {
+ cmd->start_src = TRIG_NOW;
+ err++;
+ }
+
+ if ((cmd->scan_begin_src & (TRIG_TIMER | TRIG_EXT)) &&
+ (!(cmd->convert_src & (TRIG_TIMER | TRIG_NOW)))) {
+ cmd->convert_src = TRIG_TIMER;
+ err++;
+ }
+
+ if ((cmd->scan_begin_src == TRIG_FOLLOW) &&
+ (!(cmd->convert_src & (TRIG_TIMER | TRIG_EXT)))) {
+ cmd->convert_src = TRIG_TIMER;
+ err++;
+ }
+
+ if (cmd->stop_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) {
+ cmd->stop_src = TRIG_COUNT;
+ err++;
+ }
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_src & (TRIG_NOW | TRIG_EXT))
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_begin_src & (TRIG_FOLLOW | TRIG_EXT))
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+
+ if ((cmd->scan_begin_src == TRIG_TIMER) &&
+ (cmd->convert_src == TRIG_TIMER) && (cmd->scan_end_arg == 1)) {
+ cmd->scan_begin_src = TRIG_FOLLOW;
+ cmd->convert_arg = cmd->scan_begin_arg;
+ cmd->scan_begin_arg = 0;
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER)
+ if (cmd->scan_begin_arg < this_board->ai_ns_min) {
+ cmd->scan_begin_arg = this_board->ai_ns_min;
+ err++;
+ }
+
+ if (cmd->scan_begin_src == TRIG_EXT)
+ if (cmd->scan_begin_arg) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ if (cmd->scan_end_arg > 65535) {
+ cmd->scan_end_arg = 65535;
+ err++;
+ }
+ }
+
+ if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW))
+ if (cmd->convert_arg < this_board->ai_ns_min) {
+ cmd->convert_arg = this_board->ai_ns_min;
+ err++;
+ }
+
+ if (cmd->convert_src == TRIG_EXT)
+ if (cmd->convert_arg) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (!cmd->stop_arg) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ } else { /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (!cmd->chanlist_len) {
+ cmd->chanlist_len = 1;
+ err++;
+ }
+
+ if (cmd->chanlist_len > this_board->n_aichanlist) {
+ cmd->chanlist_len = this_board->n_aichanlist;
+ err++;
+ }
+
+ if (cmd->scan_end_arg < cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ if ((cmd->scan_end_arg % cmd->chanlist_len)) {
+ cmd->scan_end_arg =
+ cmd->chanlist_len * (cmd->scan_end_arg /
+ cmd->chanlist_len);
+ err++;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+// rt_printk("S1 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg);
+ i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
+ &divisor2, &cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+// rt_printk("S2 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg);
+ if (cmd->scan_begin_arg < this_board->ai_ns_min)
+ cmd->scan_begin_arg = this_board->ai_ns_min;
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+
+ if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) {
+ tmp = cmd->convert_arg;
+ i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
+ &divisor2, &cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+// rt_printk("s1 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg);
+ if (cmd->convert_arg < this_board->ai_ns_min)
+ cmd->convert_arg = this_board->ai_ns_min;
+ if (tmp != cmd->convert_arg)
+ err++;
+ if (cmd->scan_begin_src == TRIG_TIMER
+ && cmd->convert_src == TRIG_NOW) {
+ if (cmd->convert_arg == 0) {
+ if (cmd->scan_begin_arg <
+ this_board->ai_ns_min *
+ (cmd->scan_end_arg + 2)) {
+ cmd->scan_begin_arg =
+ this_board->ai_ns_min *
+ (cmd->scan_end_arg + 2);
+// rt_printk("s2 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg);
+ err++;
+ }
+ } else {
+ if (cmd->scan_begin_arg <
+ cmd->convert_arg * cmd->chanlist_len) {
+ cmd->scan_begin_arg =
+ cmd->convert_arg *
+ cmd->chanlist_len;
+// rt_printk("s3 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg);
+ err++;
+ }
+ }
+ }
+ }
+
+ if (err)
+ return 4;
+
+ if (cmd->chanlist)
+ if (!check_channel_list(dev, s, cmd->chanlist_len,
+ cmd->chanlist, 0, 0))
+ return 5; // incorrect channels list
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int Compute_and_setup_dma(struct comedi_device * dev)
+{
+ unsigned int dmalen0, dmalen1, i;
+
+ DPRINTK("adl_pci9118 EDBG: BGN: Compute_and_setup_dma()\n");
+ dmalen0 = devpriv->dmabuf_size[0];
+ dmalen1 = devpriv->dmabuf_size[1];
+ DPRINTK("1 dmalen0=%d dmalen1=%d ai_data_len=%d\n", dmalen0, dmalen1,
+ devpriv->ai_data_len);
+ // isn't output buff smaller that our DMA buff?
+ if (dmalen0 > (devpriv->ai_data_len)) {
+ dmalen0 = devpriv->ai_data_len & ~3L; // allign to 32bit down
+ }
+ if (dmalen1 > (devpriv->ai_data_len)) {
+ dmalen1 = devpriv->ai_data_len & ~3L; // allign to 32bit down
+ }
+ DPRINTK("2 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1);
+
+ // we want wake up every scan?
+ if (devpriv->ai_flags & TRIG_WAKE_EOS) {
+ if (dmalen0 < (devpriv->ai_n_realscanlen << 1)) {
+ // uff, too short DMA buffer, disable EOS support!
+ devpriv->ai_flags &= (~TRIG_WAKE_EOS);
+ rt_printk
+ ("comedi%d: WAR: DMA0 buf too short, cann't support TRIG_WAKE_EOS (%d<%d)\n",
+ dev->minor, dmalen0,
+ devpriv->ai_n_realscanlen << 1);
+ } else {
+ // short first DMA buffer to one scan
+ dmalen0 = devpriv->ai_n_realscanlen << 1;
+ DPRINTK("21 dmalen0=%d ai_n_realscanlen=%d useeoshandle=%d\n", dmalen0, devpriv->ai_n_realscanlen, devpriv->useeoshandle);
+ if (devpriv->useeoshandle)
+ dmalen0 += 2;
+ if (dmalen0 < 4) {
+ rt_printk
+ ("comedi%d: ERR: DMA0 buf len bug? (%d<4)\n",
+ dev->minor, dmalen0);
+ dmalen0 = 4;
+ }
+ }
+ }
+ if (devpriv->ai_flags & TRIG_WAKE_EOS) {
+ if (dmalen1 < (devpriv->ai_n_realscanlen << 1)) {
+ // uff, too short DMA buffer, disable EOS support!
+ devpriv->ai_flags &= (~TRIG_WAKE_EOS);
+ rt_printk
+ ("comedi%d: WAR: DMA1 buf too short, cann't support TRIG_WAKE_EOS (%d<%d)\n",
+ dev->minor, dmalen1,
+ devpriv->ai_n_realscanlen << 1);
+ } else {
+ // short second DMA buffer to one scan
+ dmalen1 = devpriv->ai_n_realscanlen << 1;
+ DPRINTK("22 dmalen1=%d ai_n_realscanlen=%d useeoshandle=%d\n", dmalen1, devpriv->ai_n_realscanlen, devpriv->useeoshandle);
+ if (devpriv->useeoshandle)
+ dmalen1 -= 2;
+ if (dmalen1 < 4) {
+ rt_printk
+ ("comedi%d: ERR: DMA1 buf len bug? (%d<4)\n",
+ dev->minor, dmalen1);
+ dmalen1 = 4;
+ }
+ }
+ }
+
+ DPRINTK("3 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1);
+ // transfer without TRIG_WAKE_EOS
+ if (!(devpriv->ai_flags & TRIG_WAKE_EOS)) {
+ // if it's possible then allign DMA buffers to length of scan
+ i = dmalen0;
+ dmalen0 =
+ (dmalen0 / (devpriv->ai_n_realscanlen << 1)) *
+ (devpriv->ai_n_realscanlen << 1);
+ dmalen0 &= ~3L;
+ if (!dmalen0)
+ dmalen0 = i; // uff. very long scan?
+ i = dmalen1;
+ dmalen1 =
+ (dmalen1 / (devpriv->ai_n_realscanlen << 1)) *
+ (devpriv->ai_n_realscanlen << 1);
+ dmalen1 &= ~3L;
+ if (!dmalen1)
+ dmalen1 = i; // uff. very long scan?
+ // if measure isn't neverending then test, if it whole fits into one or two DMA buffers
+ if (!devpriv->ai_neverending) {
+ // fits whole measure into one DMA buffer?
+ if (dmalen0 >
+ ((devpriv->ai_n_realscanlen << 1) *
+ devpriv->ai_scans)) {
+ DPRINTK("3.0 ai_n_realscanlen=%d ai_scans=%d \n", devpriv->ai_n_realscanlen, devpriv->ai_scans);
+ dmalen0 =
+ (devpriv->ai_n_realscanlen << 1) *
+ devpriv->ai_scans;
+ DPRINTK("3.1 dmalen0=%d dmalen1=%d \n", dmalen0,
+ dmalen1);
+ dmalen0 &= ~3L;
+ } else { // fits whole measure into two DMA buffer?
+ if (dmalen1 >
+ ((devpriv->ai_n_realscanlen << 1) *
+ devpriv->ai_scans - dmalen0))
+ dmalen1 =
+ (devpriv->
+ ai_n_realscanlen << 1) *
+ devpriv->ai_scans - dmalen0;
+ DPRINTK("3.2 dmalen0=%d dmalen1=%d \n", dmalen0,
+ dmalen1);
+ dmalen1 &= ~3L;
+ }
+ }
+ }
+
+ DPRINTK("4 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1);
+
+ // these DMA buffer size we'll be used
+ devpriv->dma_actbuf = 0;
+ devpriv->dmabuf_use_size[0] = dmalen0;
+ devpriv->dmabuf_use_size[1] = dmalen1;
+
+ DPRINTK("5 dmalen0=%d dmalen1=%d \n", dmalen0, dmalen1);
+#if 0
+ if (devpriv->ai_n_scanlen < this_board->half_fifo_size) {
+ devpriv->dmabuf_panic_size[0] =
+ (this_board->half_fifo_size / devpriv->ai_n_scanlen +
+ 1) * devpriv->ai_n_scanlen * sizeof(short);
+ devpriv->dmabuf_panic_size[1] =
+ (this_board->half_fifo_size / devpriv->ai_n_scanlen +
+ 1) * devpriv->ai_n_scanlen * sizeof(short);
+ } else {
+ devpriv->dmabuf_panic_size[0] =
+ (devpriv->ai_n_scanlen << 1) % devpriv->dmabuf_size[0];
+ devpriv->dmabuf_panic_size[1] =
+ (devpriv->ai_n_scanlen << 1) % devpriv->dmabuf_size[1];
+ }
+#endif
+
+ outl(inl(devpriv->iobase_a + AMCC_OP_REG_MCSR) & (~EN_A2P_TRANSFERS), devpriv->iobase_a + AMCC_OP_REG_MCSR); // stop DMA
+ outl(devpriv->dmabuf_hw[0], devpriv->iobase_a + AMCC_OP_REG_MWAR);
+ outl(devpriv->dmabuf_use_size[0], devpriv->iobase_a + AMCC_OP_REG_MWTC);
+ // init DMA transfer
+ outl(0x00000000 | AINT_WRITE_COMPL,
+ devpriv->iobase_a + AMCC_OP_REG_INTCSR);
+// outl(0x02000000|AINT_WRITE_COMPL, devpriv->iobase_a+AMCC_OP_REG_INTCSR);
+
+ outl(inl(devpriv->iobase_a +
+ AMCC_OP_REG_MCSR) | RESET_A2P_FLAGS | A2P_HI_PRIORITY |
+ EN_A2P_TRANSFERS, devpriv->iobase_a + AMCC_OP_REG_MCSR);
+ outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | EN_A2P_TRANSFERS, devpriv->iobase_a + AMCC_OP_REG_INTCSR); // allow bus mastering
+
+ DPRINTK("adl_pci9118 EDBG: END: Compute_and_setup_dma()\n");
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci9118_ai_docmd_sampl(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_docmd_sampl(%d,) [%d]\n",
+ dev->minor, devpriv->ai_do);
+ switch (devpriv->ai_do) {
+ case 1:
+ devpriv->AdControlReg |= AdControl_TmrTr;
+ break;
+ case 2:
+ comedi_error(dev, "pci9118_ai_docmd_sampl() mode 2 bug!\n");
+ return -EIO;
+ case 3:
+ devpriv->AdControlReg |= AdControl_ExtM;
+ break;
+ case 4:
+ comedi_error(dev, "pci9118_ai_docmd_sampl() mode 4 bug!\n");
+ return -EIO;
+ default:
+ comedi_error(dev,
+ "pci9118_ai_docmd_sampl() mode number bug!\n");
+ return -EIO;
+ };
+
+ devpriv->int_ai_func = interrupt_pci9118_ai_onesample; //transfer function
+
+ if (devpriv->ai12_startstop)
+ pci9118_exttrg_add(dev, EXTTRG_AI); // activate EXT trigger
+
+ if ((devpriv->ai_do == 1) || (devpriv->ai_do == 2))
+ devpriv->IntControlReg |= Int_Timer;
+
+ devpriv->AdControlReg |= AdControl_Int;
+
+ outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | 0x1f00, devpriv->iobase_a + AMCC_OP_REG_INTCSR); // allow INT in AMCC
+
+ if (!(devpriv->ai12_startstop & (START_AI_EXT | START_AI_INT))) {
+ outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
+ outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
+ if (devpriv->ai_do != 3) {
+ start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1,
+ devpriv->ai_divisor2);
+ devpriv->AdControlReg |= AdControl_SoftG;
+ }
+ outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
+ }
+
+ DPRINTK("adl_pci9118 EDBG: END: pci9118_ai_docmd_sampl()\n");
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci9118_ai_docmd_dma(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_docmd_dma(%d,) [%d,%d]\n",
+ dev->minor, devpriv->ai_do, devpriv->usedma);
+ Compute_and_setup_dma(dev);
+
+ switch (devpriv->ai_do) {
+ case 1:
+ devpriv->AdControlReg |=
+ ((AdControl_TmrTr | AdControl_Dma) & 0xff);
+ break;
+ case 2:
+ devpriv->AdControlReg |=
+ ((AdControl_TmrTr | AdControl_Dma) & 0xff);
+ devpriv->AdFunctionReg =
+ AdFunction_PDTrg | AdFunction_PETrg | AdFunction_BM |
+ AdFunction_BS;
+ if (devpriv->usessh && (!devpriv->softsshdelay))
+ devpriv->AdFunctionReg |= AdFunction_BSSH;
+ outl(devpriv->ai_n_realscanlen, dev->iobase + PCI9118_BURST);
+ break;
+ case 3:
+ devpriv->AdControlReg |=
+ ((AdControl_ExtM | AdControl_Dma) & 0xff);
+ devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg;
+ break;
+ case 4:
+ devpriv->AdControlReg |=
+ ((AdControl_TmrTr | AdControl_Dma) & 0xff);
+ devpriv->AdFunctionReg =
+ AdFunction_PDTrg | AdFunction_PETrg | AdFunction_AM;
+ outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
+ outl(0x30, dev->iobase + PCI9118_CNTCTRL);
+ outl((devpriv->dmabuf_hw[0] >> 1) & 0xff,
+ dev->iobase + PCI9118_CNT0);
+ outl((devpriv->dmabuf_hw[0] >> 9) & 0xff,
+ dev->iobase + PCI9118_CNT0);
+ devpriv->AdFunctionReg |= AdFunction_Start;
+ break;
+ default:
+ comedi_error(dev, "pci9118_ai_docmd_dma() mode number bug!\n");
+ return -EIO;
+ };
+
+ if (devpriv->ai12_startstop) {
+ pci9118_exttrg_add(dev, EXTTRG_AI); // activate EXT trigger
+ }
+
+ devpriv->int_ai_func = interrupt_pci9118_ai_dma; //transfer function
+
+ outl(0x02000000 | AINT_WRITE_COMPL,
+ devpriv->iobase_a + AMCC_OP_REG_INTCSR);
+
+ if (!(devpriv->ai12_startstop & (START_AI_EXT | START_AI_INT))) {
+ outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
+ outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
+ if (devpriv->ai_do != 3) {
+ start_pacer(dev, devpriv->ai_do, devpriv->ai_divisor1,
+ devpriv->ai_divisor2);
+ devpriv->AdControlReg |= AdControl_SoftG;
+ }
+ outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL);
+ }
+
+ DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_docmd_dma()\n");
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci9118_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int addchans = 0;
+ int ret = 0;
+
+ DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_cmd(%d,)\n", dev->minor);
+ devpriv->ai12_startstop = 0;
+ devpriv->ai_flags = cmd->flags;
+ devpriv->ai_n_chan = cmd->chanlist_len;
+ devpriv->ai_n_scanlen = cmd->scan_end_arg;
+ devpriv->ai_chanlist = cmd->chanlist;
+ devpriv->ai_data = s->async->prealloc_buf;
+ devpriv->ai_data_len = s->async->prealloc_bufsz;
+ devpriv->ai_timer1 = 0;
+ devpriv->ai_timer2 = 0;
+ devpriv->ai_add_front = 0;
+ devpriv->ai_add_back = 0;
+ devpriv->ai_maskerr = 0x10e;
+
+ // prepare for start/stop conditions
+ if (cmd->start_src == TRIG_EXT)
+ devpriv->ai12_startstop |= START_AI_EXT;
+ if (cmd->stop_src == TRIG_EXT) {
+ devpriv->ai_neverending = 1;
+ devpriv->ai12_startstop |= STOP_AI_EXT;
+ }
+ if (cmd->start_src == TRIG_INT) {
+ devpriv->ai12_startstop |= START_AI_INT;
+ devpriv->ai_inttrig_start = cmd->start_arg;
+ s->async->inttrig = pci9118_ai_inttrig;
+ }
+#if 0
+ if (cmd->stop_src == TRIG_INT) {
+ devpriv->ai_neverending = 1;
+ devpriv->ai12_startstop |= STOP_AI_INT;
+ }
+#endif
+ if (cmd->stop_src == TRIG_NONE)
+ devpriv->ai_neverending = 1;
+ if (cmd->stop_src == TRIG_COUNT) {
+ devpriv->ai_scans = cmd->stop_arg;
+ devpriv->ai_neverending = 0;
+ } else {
+ devpriv->ai_scans = 0;
+ }
+
+ // use sample&hold signal?
+ if (cmd->convert_src == TRIG_NOW) {
+ devpriv->usessh = 1;
+ } // yes
+ else {
+ devpriv->usessh = 0;
+ } // no
+
+ DPRINTK("1 neverending=%d scans=%u usessh=%d ai_startstop=0x%2x\n",
+ devpriv->ai_neverending, devpriv->ai_scans, devpriv->usessh,
+ devpriv->ai12_startstop);
+
+ // use additional sample at end of every scan to satisty DMA 32 bit transfer?
+ devpriv->ai_add_front = 0;
+ devpriv->ai_add_back = 0;
+ devpriv->useeoshandle = 0;
+ if (devpriv->master) {
+ devpriv->usedma = 1;
+ if ((cmd->flags & TRIG_WAKE_EOS) &&
+ (devpriv->ai_n_scanlen == 1)) {
+ if (cmd->convert_src == TRIG_NOW) {
+ devpriv->ai_add_back = 1;
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ devpriv->usedma = 0; // use INT transfer if scanlist have only one channel
+ }
+ }
+ if ((cmd->flags & TRIG_WAKE_EOS) &&
+ (devpriv->ai_n_scanlen & 1) &&
+ (devpriv->ai_n_scanlen > 1)) {
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ //vpriv->useeoshandle=1; // change DMA transfer block to fit EOS on every second call
+ devpriv->usedma = 0; // XXX maybe can be corrected to use 16 bit DMA
+ } else { // well, we must insert one sample to end of EOS to meet 32 bit transfer
+ devpriv->ai_add_back = 1;
+ }
+ }
+ } else { // interrupt transfer don't need any correction
+ devpriv->usedma = 0;
+ }
+
+ // we need software S&H signal? It add two samples before every scan as minimum
+ if (devpriv->usessh && devpriv->softsshdelay) {
+ devpriv->ai_add_front = 2;
+ if ((devpriv->usedma == 1) && (devpriv->ai_add_back == 1)) { // move it to front
+ devpriv->ai_add_front++;
+ devpriv->ai_add_back = 0;
+ }
+ if (cmd->convert_arg < this_board->ai_ns_min)
+ cmd->convert_arg = this_board->ai_ns_min;
+ addchans = devpriv->softsshdelay / cmd->convert_arg;
+ if (devpriv->softsshdelay % cmd->convert_arg)
+ addchans++;
+ if (addchans > (devpriv->ai_add_front - 1)) { // uff, still short :-(
+ devpriv->ai_add_front = addchans + 1;
+ if (devpriv->usedma == 1)
+ if ((devpriv->ai_add_front +
+ devpriv->ai_n_chan +
+ devpriv->ai_add_back) & 1)
+ devpriv->ai_add_front++; // round up to 32 bit
+ }
+ } // well, we now know what must be all added
+
+ devpriv->ai_n_realscanlen = // what we must take from card in real to have ai_n_scanlen on output?
+ (devpriv->ai_add_front + devpriv->ai_n_chan +
+ devpriv->ai_add_back) * (devpriv->ai_n_scanlen /
+ devpriv->ai_n_chan);
+
+ DPRINTK("2 usedma=%d realscan=%d af=%u n_chan=%d ab=%d n_scanlen=%d\n",
+ devpriv->usedma,
+ devpriv->ai_n_realscanlen, devpriv->ai_add_front,
+ devpriv->ai_n_chan, devpriv->ai_add_back,
+ devpriv->ai_n_scanlen);
+
+ // check and setup channel list
+ if (!check_channel_list(dev, s, devpriv->ai_n_chan,
+ devpriv->ai_chanlist, devpriv->ai_add_front,
+ devpriv->ai_add_back))
+ return -EINVAL;
+ if (!setup_channel_list(dev, s, devpriv->ai_n_chan,
+ devpriv->ai_chanlist, 0, devpriv->ai_add_front,
+ devpriv->ai_add_back, devpriv->usedma,
+ devpriv->useeoshandle))
+ return -EINVAL;
+
+ // compute timers settings
+ // simplest way, fr=4Mhz/(tim1*tim2), channel manipulation without timers effect
+ if (((cmd->scan_begin_src == TRIG_FOLLOW) || (cmd->scan_begin_src == TRIG_EXT) || (cmd->scan_begin_src == TRIG_INT)) && (cmd->convert_src == TRIG_TIMER)) { // both timer is used for one time
+ if (cmd->scan_begin_src == TRIG_EXT) {
+ devpriv->ai_do = 4;
+ } else {
+ devpriv->ai_do = 1;
+ }
+ pci9118_calc_divisors(devpriv->ai_do, dev, s,
+ &cmd->scan_begin_arg, &cmd->convert_arg,
+ devpriv->ai_flags, devpriv->ai_n_realscanlen,
+ &devpriv->ai_divisor1, &devpriv->ai_divisor2,
+ devpriv->usessh, devpriv->ai_add_front);
+ devpriv->ai_timer2 = cmd->convert_arg;
+ }
+
+ if ((cmd->scan_begin_src == TRIG_TIMER) && ((cmd->convert_src == TRIG_TIMER) || (cmd->convert_src == TRIG_NOW))) { // double timed action
+ if (!devpriv->usedma) {
+ comedi_error(dev,
+ "cmd->scan_begin_src=TRIG_TIMER works only with bus mastering!");
+ return -EIO;
+ }
+
+ devpriv->ai_do = 2;
+ pci9118_calc_divisors(devpriv->ai_do, dev, s,
+ &cmd->scan_begin_arg, &cmd->convert_arg,
+ devpriv->ai_flags, devpriv->ai_n_realscanlen,
+ &devpriv->ai_divisor1, &devpriv->ai_divisor2,
+ devpriv->usessh, devpriv->ai_add_front);
+ devpriv->ai_timer1 = cmd->scan_begin_arg;
+ devpriv->ai_timer2 = cmd->convert_arg;
+ }
+
+ if ((cmd->scan_begin_src == TRIG_FOLLOW)
+ && (cmd->convert_src == TRIG_EXT)) {
+ devpriv->ai_do = 3;
+ }
+
+ start_pacer(dev, -1, 0, 0); // stop pacer
+
+ devpriv->AdControlReg = 0; // bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable DMA
+ outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL);
+ devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg; // positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop
+ outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
+ comedi_udelay(1);
+ outl(0, dev->iobase + PCI9118_DELFIFO); // flush FIFO
+ inl(dev->iobase + PCI9118_ADSTAT); // flush A/D and INT status register
+ inl(dev->iobase + PCI9118_INTSRC);
+
+ devpriv->ai_act_scan = 0;
+ devpriv->ai_act_dmapos = 0;
+ s->async->cur_chan = 0;
+ devpriv->ai_buf_ptr = 0;
+
+ if (devpriv->usedma) {
+ ret = pci9118_ai_docmd_dma(dev, s);
+ } else {
+ ret = pci9118_ai_docmd_sampl(dev, s);
+ }
+
+ DPRINTK("adl_pci9118 EDBG: END: pci9118_ai_cmd()\n");
+ return ret;
+}
+
+/*
+==============================================================================
+*/
+static int check_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
+ int n_chan, unsigned int *chanlist, int frontadd, int backadd)
+{
+ unsigned int i, differencial = 0, bipolar = 0;
+
+ /* correct channel and range number check itself comedi/range.c */
+ if (n_chan < 1) {
+ comedi_error(dev, "range/channel list is empty!");
+ return 0;
+ }
+ if ((frontadd + n_chan + backadd) > s->len_chanlist) {
+ rt_printk
+ ("comedi%d: range/channel list is too long for actual configuration (%d>%d)!",
+ dev->minor, n_chan,
+ s->len_chanlist - frontadd - backadd);
+ return 0;
+ }
+
+ if (CR_AREF(chanlist[0]) == AREF_DIFF)
+ differencial = 1; // all input must be diff
+ if (CR_RANGE(chanlist[0]) < PCI9118_BIPOLAR_RANGES)
+ bipolar = 1; // all input must be bipolar
+ if (n_chan > 1)
+ for (i = 1; i < n_chan; i++) { // check S.E/diff
+ if ((CR_AREF(chanlist[i]) == AREF_DIFF) !=
+ (differencial)) {
+ comedi_error(dev,
+ "Differencial and single ended inputs cann't be mixtured!");
+ return 0;
+ }
+ if ((CR_RANGE(chanlist[i]) < PCI9118_BIPOLAR_RANGES) !=
+ (bipolar)) {
+ comedi_error(dev,
+ "Bipolar and unipolar ranges cann't be mixtured!");
+ return 0;
+ }
+ if ((!devpriv->usemux) & (differencial) &
+ (CR_CHAN(chanlist[i]) >=
+ this_board->n_aichand)) {
+ comedi_error(dev,
+ "If AREF_DIFF is used then is available only first 8 channels!");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+==============================================================================
+*/
+static int setup_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
+ int n_chan, unsigned int *chanlist, int rot, int frontadd, int backadd,
+ int usedma, char useeos)
+{
+ unsigned int i, differencial = 0, bipolar = 0;
+ unsigned int scanquad, gain, ssh = 0x00;
+
+ DPRINTK("adl_pci9118 EDBG: BGN: setup_channel_list(%d,.,%d,.,%d,%d,%d,%d)\n", dev->minor, n_chan, rot, frontadd, backadd, usedma);
+
+ if (usedma == 1) {
+ rot = 8;
+ usedma = 0;
+ }
+
+ if (CR_AREF(chanlist[0]) == AREF_DIFF)
+ differencial = 1; // all input must be diff
+ if (CR_RANGE(chanlist[0]) < PCI9118_BIPOLAR_RANGES)
+ bipolar = 1; // all input must be bipolar
+
+ // All is ok, so we can setup channel/range list
+
+ if (!bipolar) {
+ devpriv->AdControlReg |= AdControl_UniP; // set unibipolar
+ } else {
+ devpriv->AdControlReg &= ((~AdControl_UniP) & 0xff); // enable bipolar
+ }
+
+ if (differencial) {
+ devpriv->AdControlReg |= AdControl_Diff; // enable diff inputs
+ } else {
+ devpriv->AdControlReg &= ((~AdControl_Diff) & 0xff); // set single ended inputs
+ }
+
+ outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); // setup mode
+
+ outl(2, dev->iobase + PCI9118_SCANMOD); // gods know why this sequence!
+ outl(0, dev->iobase + PCI9118_SCANMOD);
+ outl(1, dev->iobase + PCI9118_SCANMOD);
+
+#ifdef PCI9118_PARANOIDCHECK
+ devpriv->chanlistlen = n_chan;
+ for (i = 0; i < (PCI9118_CHANLEN + 1); i++)
+ devpriv->chanlist[i] = 0x55aa;
+#endif
+
+ if (frontadd) { // insert channels for S&H
+ ssh = devpriv->softsshsample;
+ DPRINTK("FA: %04x: ", ssh);
+ for (i = 0; i < frontadd; i++) { // store range list to card
+ scanquad = CR_CHAN(chanlist[0]); // get channel number;
+ gain = CR_RANGE(chanlist[0]); // get gain number
+ scanquad |= ((gain & 0x03) << 8);
+ outl(scanquad | ssh, dev->iobase + PCI9118_GAIN);
+ DPRINTK("%02x ", scanquad | ssh);
+ ssh = devpriv->softsshhold;
+ }
+ DPRINTK("\n ");
+ }
+
+ DPRINTK("SL: ", ssh);
+ for (i = 0; i < n_chan; i++) { // store range list to card
+ scanquad = CR_CHAN(chanlist[i]); // get channel number;
+#ifdef PCI9118_PARANOIDCHECK
+ devpriv->chanlist[i ^ usedma] = (scanquad & 0xf) << rot;
+#endif
+ gain = CR_RANGE(chanlist[i]); // get gain number
+ scanquad |= ((gain & 0x03) << 8);
+ outl(scanquad | ssh, dev->iobase + PCI9118_GAIN);
+ DPRINTK("%02x ", scanquad | ssh);
+ }
+ DPRINTK("\n ");
+
+ if (backadd) { // insert channels for fit onto 32bit DMA
+ DPRINTK("BA: %04x: ", ssh);
+ for (i = 0; i < backadd; i++) { // store range list to card
+ scanquad = CR_CHAN(chanlist[0]); // get channel number;
+ gain = CR_RANGE(chanlist[0]); // get gain number
+ scanquad |= ((gain & 0x03) << 8);
+ outl(scanquad | ssh, dev->iobase + PCI9118_GAIN);
+ DPRINTK("%02x ", scanquad | ssh);
+ }
+ DPRINTK("\n ");
+ }
+#ifdef PCI9118_PARANOIDCHECK
+ devpriv->chanlist[n_chan ^ usedma] = devpriv->chanlist[0 ^ usedma]; // for 32bit oerations
+ if (useeos) {
+ for (i = 1; i < n_chan; i++) { // store range list to card
+ devpriv->chanlist[(n_chan + i) ^ usedma] =
+ (CR_CHAN(chanlist[i]) & 0xf) << rot;
+ }
+ devpriv->chanlist[(2 * n_chan) ^ usedma] = devpriv->chanlist[0 ^ usedma]; // for 32bit oerations
+ useeos = 2;
+ } else {
+ useeos = 1;
+ }
+#ifdef PCI9118_EXTDEBUG
+ DPRINTK("CHL: ");
+ for (i = 0; i <= (useeos * n_chan); i++) {
+ DPRINTK("%04x ", devpriv->chanlist[i]);
+ }
+ DPRINTK("\n ");
+#endif
+#endif
+ outl(0, dev->iobase + PCI9118_SCANMOD); // close scan queue
+// comedi_udelay(100); // important delay, or first sample will be cripled
+
+ DPRINTK("adl_pci9118 EDBG: END: setup_channel_list()\n");
+ return 1; // we can serve this with scan logic
+}
+
+/*
+==============================================================================
+ calculate 8254 divisors if they are used for dual timing
+*/
+static void pci9118_calc_divisors(char mode, struct comedi_device * dev,
+ struct comedi_subdevice * s, unsigned int *tim1, unsigned int *tim2,
+ unsigned int flags, int chans, unsigned int *div1, unsigned int *div2,
+ char usessh, unsigned int chnsshfront)
+{
+ DPRINTK("adl_pci9118 EDBG: BGN: pci9118_calc_divisors(%d,%d,.,%u,%u,%u,%d,.,.,,%u,%u)\n", mode, dev->minor, *tim1, *tim2, flags, chans, usessh, chnsshfront);
+ switch (mode) {
+ case 1:
+ case 4:
+ if (*tim2 < this_board->ai_ns_min)
+ *tim2 = this_board->ai_ns_min;
+ i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, div1, div2,
+ tim2, flags & TRIG_ROUND_NEAREST);
+ DPRINTK("OSC base=%u div1=%u div2=%u timer1=%u\n",
+ devpriv->i8254_osc_base, *div1, *div2, *tim1);
+ break;
+ case 2:
+ if (*tim2 < this_board->ai_ns_min)
+ *tim2 = this_board->ai_ns_min;
+ DPRINTK("1 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
+ *tim1, *tim2);
+ *div1 = *tim2 / devpriv->i8254_osc_base; // convert timer (burst)
+ DPRINTK("2 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
+ *tim1, *tim2);
+ if (*div1 < this_board->ai_pacer_min)
+ *div1 = this_board->ai_pacer_min;
+ DPRINTK("3 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
+ *tim1, *tim2);
+ *div2 = *tim1 / devpriv->i8254_osc_base; // scan timer
+ DPRINTK("4 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
+ *tim1, *tim2);
+ *div2 = *div2 / *div1; // major timer is c1*c2
+ DPRINTK("5 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
+ *tim1, *tim2);
+ if (*div2 < chans)
+ *div2 = chans;
+ DPRINTK("6 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
+ *tim1, *tim2);
+
+ *tim2 = *div1 * devpriv->i8254_osc_base; // real convert timer
+
+ if (usessh & (chnsshfront == 0)) // use BSSH signal
+ if (*div2 < (chans + 2))
+ *div2 = chans + 2;
+
+ DPRINTK("7 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
+ *tim1, *tim2);
+ *tim1 = *div1 * *div2 * devpriv->i8254_osc_base;
+ DPRINTK("OSC base=%u div1=%u div2=%u timer1=%u timer2=%u\n",
+ devpriv->i8254_osc_base, *div1, *div2, *tim1, *tim2);
+ break;
+ }
+ DPRINTK("adl_pci9118 EDBG: END: pci9118_calc_divisors(%u,%u)\n",
+ *div1, *div2);
+}
+
+/*
+==============================================================================
+*/
+static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
+ unsigned int divisor2)
+{
+ outl(0x74, dev->iobase + PCI9118_CNTCTRL);
+ outl(0xb4, dev->iobase + PCI9118_CNTCTRL);
+// outl(0x30, dev->iobase + PCI9118_CNTCTRL);
+ comedi_udelay(1);
+
+ if ((mode == 1) || (mode == 2) || (mode == 4)) {
+ outl(divisor2 & 0xff, dev->iobase + PCI9118_CNT2);
+ outl((divisor2 >> 8) & 0xff, dev->iobase + PCI9118_CNT2);
+ outl(divisor1 & 0xff, dev->iobase + PCI9118_CNT1);
+ outl((divisor1 >> 8) & 0xff, dev->iobase + PCI9118_CNT1);
+ }
+}
+
+/*
+==============================================================================
+*/
+static int pci9118_exttrg_add(struct comedi_device * dev, unsigned char source)
+{
+ if (source > 3)
+ return -1; // incorrect source
+ devpriv->exttrg_users |= (1 << source);
+ devpriv->IntControlReg |= Int_DTrg;
+ outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
+ outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | 0x1f00, devpriv->iobase_a + AMCC_OP_REG_INTCSR); // allow INT in AMCC
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci9118_exttrg_del(struct comedi_device * dev, unsigned char source)
+{
+ if (source > 3)
+ return -1; // incorrect source
+ devpriv->exttrg_users &= ~(1 << source);
+ if (!devpriv->exttrg_users) { // shutdown ext trg intterrupts
+ devpriv->IntControlReg &= ~Int_DTrg;
+ if (!devpriv->IntControlReg) // all IRQ disabled
+ outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) & (~0x00001f00), devpriv->iobase_a + AMCC_OP_REG_INTCSR); // disable int in AMCC
+ outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
+ }
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci9118_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ if (devpriv->usedma)
+ outl(inl(devpriv->iobase_a + AMCC_OP_REG_MCSR) & (~EN_A2P_TRANSFERS), devpriv->iobase_a + AMCC_OP_REG_MCSR); // stop DMA
+ pci9118_exttrg_del(dev, EXTTRG_AI);
+ start_pacer(dev, 0, 0, 0); // stop 8254 counters
+ devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg;
+ outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); // positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop
+ devpriv->AdControlReg = 0x00;
+ outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); // bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable INT and DMA
+ outl(0, dev->iobase + PCI9118_BURST);
+ outl(1, dev->iobase + PCI9118_SCANMOD);
+ outl(2, dev->iobase + PCI9118_SCANMOD); // reset scan queue
+ outl(0, dev->iobase + PCI9118_DELFIFO); // flush FIFO
+
+ devpriv->ai_do = 0;
+ devpriv->usedma = 0;
+
+ devpriv->ai_act_scan = 0;
+ devpriv->ai_act_dmapos = 0;
+ s->async->cur_chan = 0;
+ s->async->inttrig = NULL;
+ devpriv->ai_buf_ptr = 0;
+ devpriv->ai_neverending = 0;
+ devpriv->dma_actbuf = 0;
+
+ if (!devpriv->IntControlReg)
+ outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | 0x1f00, devpriv->iobase_a + AMCC_OP_REG_INTCSR); // allow INT in AMCC
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci9118_reset(struct comedi_device * dev)
+{
+ devpriv->IntControlReg = 0;
+ devpriv->exttrg_users = 0;
+ inl(dev->iobase + PCI9118_INTCTRL);
+ outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL); // disable interrupts source
+ outl(0x30, dev->iobase + PCI9118_CNTCTRL);
+// outl(0xb4, dev->iobase + PCI9118_CNTCTRL);
+ start_pacer(dev, 0, 0, 0); // stop 8254 counters
+ devpriv->AdControlReg = 0;
+ outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); // bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable INT and DMA
+ outl(0, dev->iobase + PCI9118_BURST);
+ outl(1, dev->iobase + PCI9118_SCANMOD);
+ outl(2, dev->iobase + PCI9118_SCANMOD); // reset scan queue
+ devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg;
+ outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC); // positive triggers, no S&H, no burst, burst stop, no post trigger, no about trigger, trigger stop
+
+ devpriv->ao_data[0] = 2047;
+ devpriv->ao_data[1] = 2047;
+ outl(devpriv->ao_data[0], dev->iobase + PCI9118_DA1); // reset A/D outs to 0V
+ outl(devpriv->ao_data[1], dev->iobase + PCI9118_DA2);
+ outl(0, dev->iobase + PCI9118_DO); // reset digi outs to L
+ comedi_udelay(10);
+ inl(dev->iobase + PCI9118_AD_DATA);
+ outl(0, dev->iobase + PCI9118_DELFIFO); // flush FIFO
+ outl(0, dev->iobase + PCI9118_INTSRC); // remove INT requests
+ inl(dev->iobase + PCI9118_ADSTAT); // flush A/D status register
+ inl(dev->iobase + PCI9118_INTSRC); // flush INT requests
+ devpriv->AdControlReg = 0;
+ outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL); // bipolar, S.E., use 8254, stop 8354, internal trigger, soft trigger, disable INT and DMA
+
+ devpriv->cnt0_users = 0;
+ devpriv->exttrg_users = 0;
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci9118_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int ret, pages, i;
+ unsigned short master;
+ unsigned int irq;
+ unsigned long iobase_a, iobase_9;
+ struct pci_dev *pcidev;
+ int opt_bus, opt_slot;
+ const char *errstr;
+ unsigned char pci_bus, pci_slot, pci_func;
+ u16 u16w;
+
+ rt_printk("comedi%d: adl_pci9118: board=%s", dev->minor,
+ this_board->name);
+
+ opt_bus = it->options[0];
+ opt_slot = it->options[1];
+ if (it->options[3] & 1) {
+ master = 0; // user don't want use bus master
+ } else {
+ master = 1;
+ }
+
+ if ((ret = alloc_private(dev, sizeof(struct pci9118_private))) < 0) {
+ rt_printk(" - Allocation failed!\n");
+ return -ENOMEM;
+ }
+
+ /* Look for matching PCI device */
+ errstr = "not found!";
+ pcidev = NULL;
+ while (NULL != (pcidev = pci_get_device(PCI_VENDOR_ID_AMCC,
+ this_board->device_id, pcidev))) {
+ /* Found matching vendor/device. */
+ if (opt_bus || opt_slot) {
+ /* Check bus/slot. */
+ if (opt_bus != pcidev->bus->number
+ || opt_slot != PCI_SLOT(pcidev->devfn))
+ continue; /* no match */
+ }
+ /*
+ * Look for device that isn't in use.
+ * Enable PCI device and request regions.
+ */
+ if (comedi_pci_enable(pcidev, "adl_pci9118")) {
+ errstr = "failed to enable PCI device and request regions!";
+ continue;
+ }
+ break;
+ }
+
+ if (!pcidev) {
+ if (opt_bus || opt_slot) {
+ rt_printk(" - Card at b:s %d:%d %s\n",
+ opt_bus, opt_slot, errstr);
+ } else {
+ rt_printk(" - Card %s\n", errstr);
+ }
+ return -EIO;
+ }
+
+ if (master) {
+ pci_set_master(pcidev);
+ }
+
+ pci_bus = pcidev->bus->number;
+ pci_slot = PCI_SLOT(pcidev->devfn);
+ pci_func = PCI_FUNC(pcidev->devfn);
+ irq = pcidev->irq;
+ iobase_a = pci_resource_start(pcidev, 0);
+ iobase_9 = pci_resource_start(pcidev, 2);
+
+ rt_printk(", b:s:f=%d:%d:%d, io=0x%4lx, 0x%4lx", pci_bus, pci_slot,
+ pci_func, iobase_9, iobase_a);
+
+ dev->iobase = iobase_9;
+ dev->board_name = this_board->name;
+
+ devpriv->pcidev = pcidev;
+ devpriv->iobase_a = iobase_a;
+
+ pci9118_reset(dev);
+
+ if (it->options[3] & 2)
+ irq = 0; // user don't want use IRQ
+ if (irq > 0) {
+ if (comedi_request_irq(irq, interrupt_pci9118, IRQF_SHARED,
+ "ADLink PCI-9118", dev)) {
+ rt_printk(", unable to allocate IRQ %d, DISABLING IT",
+ irq);
+ irq = 0; /* Can't use IRQ */
+ } else {
+ rt_printk(", irq=%u", irq);
+ }
+ } else {
+ rt_printk(", IRQ disabled");
+ }
+
+ dev->irq = irq;
+
+ if (master) { // alloc DMA buffers
+ devpriv->dma_doublebuf = 0;
+ for (i = 0; i < 2; i++) {
+ for (pages = 4; pages >= 0; pages--)
+ if ((devpriv->dmabuf_virt[i] = (short *)
+ __get_free_pages(GFP_KERNEL,
+ pages)))
+ break;
+ if (devpriv->dmabuf_virt[i]) {
+ devpriv->dmabuf_pages[i] = pages;
+ devpriv->dmabuf_size[i] = PAGE_SIZE * pages;
+ devpriv->dmabuf_samples[i] =
+ devpriv->dmabuf_size[i] >> 1;
+ devpriv->dmabuf_hw[i] =
+ virt_to_bus((void *)devpriv->
+ dmabuf_virt[i]);
+ }
+ }
+ if (!devpriv->dmabuf_virt[0]) {
+ rt_printk(", Can't allocate DMA buffer, DMA disabled!");
+ master = 0;
+ }
+
+ if (devpriv->dmabuf_virt[1])
+ devpriv->dma_doublebuf = 1;
+
+ }
+
+ if ((devpriv->master = master)) {
+ rt_printk(", bus master");
+ } else {
+ rt_printk(", no bus master");
+ }
+
+ devpriv->usemux = 0;
+ if (it->options[2] > 0) {
+ devpriv->usemux = it->options[2];
+ if (devpriv->usemux > 256)
+ devpriv->usemux = 256; // max 256 channels!
+ if (it->options[4] > 0)
+ if (devpriv->usemux > 128) {
+ devpriv->usemux = 128; // max 128 channels with softare S&H!
+ }
+ rt_printk(", ext. mux %d channels", devpriv->usemux);
+ }
+
+ devpriv->softsshdelay = it->options[4];
+ if (devpriv->softsshdelay < 0) { // select sample&hold signal polarity
+ devpriv->softsshdelay = -devpriv->softsshdelay;
+ devpriv->softsshsample = 0x80;
+ devpriv->softsshhold = 0x00;
+ } else {
+ devpriv->softsshsample = 0x00;
+ devpriv->softsshhold = 0x80;
+ }
+
+ rt_printk(".\n");
+
+ pci_read_config_word(devpriv->pcidev, PCI_COMMAND, &u16w);
+ pci_write_config_word(devpriv->pcidev, PCI_COMMAND, u16w | 64); // Enable parity check for parity error
+
+ if ((ret = alloc_subdevices(dev, 4)) < 0)
+ return ret;
+
+ s = dev->subdevices + 0;
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
+ if (devpriv->usemux) {
+ s->n_chan = devpriv->usemux;
+ } else {
+ s->n_chan = this_board->n_aichan;
+ }
+ s->maxdata = this_board->ai_maxdata;
+ s->len_chanlist = this_board->n_aichanlist;
+ s->range_table = this_board->rangelist_ai;
+ s->cancel = pci9118_ai_cancel;
+ s->insn_read = pci9118_insn_read_ai;
+ if (dev->irq) {
+ s->subdev_flags |= SDF_CMD_READ;
+ s->do_cmdtest = pci9118_ai_cmdtest;
+ s->do_cmd = pci9118_ai_cmd;
+ s->munge = pci9118_ai_munge;
+ }
+
+ s = dev->subdevices + 1;
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = this_board->n_aochan;
+ s->maxdata = this_board->ao_maxdata;
+ s->len_chanlist = this_board->n_aochan;
+ s->range_table = this_board->rangelist_ao;
+ s->insn_write = pci9118_insn_write_ao;
+ s->insn_read = pci9118_insn_read_ao;
+
+ s = dev->subdevices + 2;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->len_chanlist = 4;
+ s->range_table = &range_digital;
+ s->io_bits = 0; /* all bits input */
+ s->insn_bits = pci9118_insn_bits_di;
+
+ s = dev->subdevices + 3;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->len_chanlist = 4;
+ s->range_table = &range_digital;
+ s->io_bits = 0xf; /* all bits output */
+ s->insn_bits = pci9118_insn_bits_do;
+
+ devpriv->valid = 1;
+ devpriv->i8254_osc_base = 250; // 250ns=4MHz
+ devpriv->ai_maskharderr = 0x10a; // default measure crash condition
+ if (it->options[5]) // disable some requested
+ devpriv->ai_maskharderr &= ~it->options[5];
+
+ switch (this_board->ai_maxdata) {
+ case 0xffff:
+ devpriv->ai16bits = 1;
+ break;
+ default:
+ devpriv->ai16bits = 0;
+ break;
+ }
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci9118_detach(struct comedi_device * dev)
+{
+ if (dev->private) {
+ if (devpriv->valid)
+ pci9118_reset(dev);
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+ if (devpriv->pcidev) {
+ if (dev->iobase) {
+ comedi_pci_disable(devpriv->pcidev);
+ }
+ pci_dev_put(devpriv->pcidev);
+ }
+ if (devpriv->dmabuf_virt[0])
+ free_pages((unsigned long)devpriv->dmabuf_virt[0],
+ devpriv->dmabuf_pages[0]);
+ if (devpriv->dmabuf_virt[1])
+ free_pages((unsigned long)devpriv->dmabuf_virt[1],
+ devpriv->dmabuf_pages[1]);
+ }
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
diff --git a/drivers/staging/comedi/drivers/adq12b.c b/drivers/staging/comedi/drivers/adq12b.c
new file mode 100644
index 000000000000..92f62854d6dc
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adq12b.c
@@ -0,0 +1,394 @@
+/*
+ comedi/drivers/adq12b.c
+ driver for MicroAxial ADQ12-B data acquisition and control card
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: adq12b
+Description: driver for MicroAxial ADQ12-B data acquisition and control card
+Devices: [MicroAxial] ADQ12-B (adq12b)
+Author: jeremy theler <thelerg@ib.cnea.gov.ar>
+Updated: Thu, 21 Feb 2008 02:56:27 -0300
+Status: works
+
+Driver for the acquisition card ADQ12-B (without any add-on).
+
+ - Analog input is subdevice 0 (16 channels single-ended or 8 differential)
+ - Digital input is subdevice 1 (5 channels)
+ - Digital output is subdevice 1 (8 channels)
+ - The PACER is not supported in this version
+
+If you do not specify any options, they will default to
+
+ # comedi_config /dev/comedi0 adq12b 0x300,0,0
+
+ option 1: I/O base address. The following table is provided as a help
+ of the hardware jumpers.
+
+ address jumper JADR
+ 0x300 1 (factory default)
+ 0x320 2
+ 0x340 3
+ 0x360 4
+ 0x380 5
+ 0x3A0 6
+
+ option 2: unipolar/bipolar ADC selection: 0 -> bipolar, 1 -> unipolar
+
+ selection comedi_config option JUB
+ bipolar 0 2-3 (factory default)
+ unipolar 1 1-2
+
+ option 3: single-ended/differential AI selection: 0 -> SE, 1 -> differential
+
+ selection comedi_config option JCHA JCHB
+ single-ended 0 1-2 1-2 (factory default)
+ differential 1 2-3 2-3
+
+
+ written by jeremy theler <thelerg@ib.cnea.gov.ar>
+
+ instituto balseiro
+ comision nacional de energia atomica
+ universidad nacional de cuyo
+ argentina
+
+ 21-feb-2008
+ + changed supported devices string (missused the [] and ())
+
+ 13-oct-2007
+ + first try
+
+
+*/
+
+#include "../comedidev.h"
+
+// address scheme (page 2.17 of the manual)
+#define ADQ12B_SIZE 16
+
+#define ADQ12B_CTREG 0x00
+#define ADQ12B_STINR 0x00
+#define ADQ12B_OUTBR 0x04
+#define ADQ12B_ADLOW 0x08
+#define ADQ12B_ADHIG 0x09
+#define ADQ12B_CONT0 0x0c
+#define ADQ12B_CONT1 0x0d
+#define ADQ12B_CONT2 0x0e
+#define ADQ12B_COWORD 0x0f
+
+// mask of the bit at STINR to check end of conversion
+#define ADQ12B_EOC 0x20
+
+#define TIMEOUT 20
+
+// available ranges through the PGA gains
+static const struct comedi_lrange range_adq12b_ai_bipolar = { 4, {
+ BIP_RANGE( 5 ),
+ BIP_RANGE( 2 ),
+ BIP_RANGE( 1 ),
+ BIP_RANGE( 0.5 )
+}};
+
+static const struct comedi_lrange range_adq12b_ai_unipolar = { 4, {
+ UNI_RANGE( 5 ),
+ UNI_RANGE( 2 ),
+ UNI_RANGE( 1 ),
+ UNI_RANGE( 0.5 )
+}};
+
+
+
+struct adq12b_board {
+ const char *name;
+ int ai_se_chans;
+ int ai_diff_chans;
+ int ai_bits;
+ int di_chans;
+ int do_chans;
+};
+
+static const struct adq12b_board adq12b_boards[] = {
+ {
+ name: "adq12b",
+ ai_se_chans: 16,
+ ai_diff_chans: 8,
+ ai_bits: 12,
+ di_chans: 5,
+ do_chans: 8
+ }
+// potentially, more adq-based deviced will be added
+/*,
+ name: "adq12b",
+ ai_chans: 16, // this is just for reference, hardcoded again later
+ ai_bits: 12,
+ di_chans: 8,
+ do_chans: 5
+ }*/
+};
+
+#define thisboard ((const struct adq12b_board *)dev->board_ptr)
+
+struct adq12b_private {
+ int unipolar; /* option 2 of comedi_config (1 is iobase) */
+ int differential; /* option 3 of comedi_config */
+ int last_channel;
+ int last_range;
+ unsigned int digital_state;
+};
+
+#define devpriv ((struct adq12b_private *)dev->private)
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int adq12b_attach(struct comedi_device *dev,struct comedi_devconfig *it);
+static int adq12b_detach(struct comedi_device *dev);
+static struct comedi_driver driver_adq12b={
+ driver_name: "adq12b",
+ module: THIS_MODULE,
+ attach: adq12b_attach,
+ detach: adq12b_detach,
+ board_name: &adq12b_boards[0].name,
+ offset: sizeof(struct adq12b_board),
+ num_names: sizeof(adq12b_boards) / sizeof(struct adq12b_board),
+};
+
+static int adq12b_ai_rinsn(struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data);
+static int adq12b_di_insn_bits(struct comedi_device *dev,struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data);
+static int adq12b_do_insn_bits(struct comedi_device *dev,struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data);
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int adq12b_attach(struct comedi_device *dev,struct comedi_devconfig *it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+ int unipolar, differential;
+
+ iobase = it->options[0];
+ unipolar = it->options[1];
+ differential = it->options[2];
+
+ printk("comedi%d: adq12b called with options base=0x%03lx, %s and %s\n",dev->minor, iobase, (unipolar==1)?"unipolar":"bipolar", (differential==1)?"differential":"single-ended");
+
+ /* if no address was specified, try the default 0x300 */
+ if (iobase == 0) {
+ printk("comedi%d: adq12b warning: I/O base address not specified. Trying the default 0x300.\n", dev->minor);
+ iobase = 0x300;
+ }
+
+ printk("comedi%d: adq12b: 0x%04lx ", dev->minor, iobase);
+ if (!request_region(iobase, ADQ12B_SIZE, "adq12b")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+/*
+ * Initialize dev->board_name. Note that we can use the "thisboard"
+ * macro now, since we just initialized it in the last line.
+ */
+ dev->board_name = thisboard->name;
+
+/*
+ * Allocate the private structure area. alloc_private() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if(alloc_private(dev, sizeof(struct adq12b_private)) < 0)
+ return -ENOMEM;
+
+/* fill in devpriv structure */
+ devpriv->unipolar = unipolar;
+ devpriv->differential = differential;
+ devpriv->digital_state = 0;
+/* initialize channel and range to -1 so we make sure we always write
+ at least once to the CTREG in the instruction */
+ devpriv->last_channel = -1;
+ devpriv->last_range = -1;
+
+
+/*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if(alloc_subdevices(dev, 3)<0)
+ return -ENOMEM;
+
+ s = dev->subdevices+0;
+ /* analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ if (differential) {
+ s->subdev_flags = SDF_READABLE|SDF_GROUND|SDF_DIFF;
+ s->n_chan = thisboard->ai_diff_chans;
+ } else {
+ s->subdev_flags = SDF_READABLE|SDF_GROUND;
+ s->n_chan = thisboard->ai_se_chans;
+ }
+
+ if (unipolar) {
+ s->range_table = &range_adq12b_ai_unipolar;
+ } else {
+ s->range_table = &range_adq12b_ai_bipolar;
+ }
+
+ s->maxdata = (1 << thisboard->ai_bits)-1;
+
+
+ s->len_chanlist = 4; /* This is the maximum chanlist length that
+ the board can handle */
+ s->insn_read = adq12b_ai_rinsn;
+
+
+ s = dev->subdevices+1;
+ /* digital input subdevice */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan=thisboard->di_chans;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = adq12b_di_insn_bits;
+
+ s = dev->subdevices+2;
+ /* digital output subdevice */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = thisboard->do_chans;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = adq12b_do_insn_bits;
+
+
+ printk("attached\n");
+
+ return 0;
+}
+
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int adq12b_detach(struct comedi_device *dev)
+{
+ if (dev->iobase)
+ release_region(dev->iobase, ADQ12B_SIZE);
+
+ kfree(devpriv);
+
+ printk("comedi%d: adq12b: removed\n",dev->minor);
+
+ return 0;
+}
+
+/*
+ * "instructions" read/write data in "one-shot" or "software-triggered"
+ * mode.
+ */
+
+static int adq12b_ai_rinsn(struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data)
+{
+ int n, i;
+ int range, channel;
+ unsigned char hi, lo, status;
+
+ /* change channel and range only if it is different from the previous */
+ range = CR_RANGE(insn->chanspec);
+ channel = CR_CHAN(insn->chanspec);
+ if (channel != devpriv->last_channel || range != devpriv->last_range) {
+ outb((range << 4) | channel, dev->iobase + ADQ12B_CTREG);
+ comedi_udelay(50); /* wait for the mux to settle */
+ }
+
+ /* trigger conversion */
+ status = inb(dev->iobase + ADQ12B_ADLOW);
+
+ /* convert n samples */
+ for(n=0; n < insn->n; n++){
+
+ /* wait for end of convertion */
+ i = 0;
+ do {
+// comedi_udelay(1);
+ status = inb(dev->iobase + ADQ12B_STINR);
+ status = status & ADQ12B_EOC;
+ } while (status == 0 && ++i < TIMEOUT);
+// } while (++i < 10);
+
+ /* read data */
+ hi = inb(dev->iobase + ADQ12B_ADHIG);
+ lo = inb(dev->iobase + ADQ12B_ADLOW);
+
+ //rt_printk("debug: chan=%d range=%d status=%d hi=%d lo=%d\n", channel, range, status, hi, lo);
+ data[n] = (hi << 8) | lo;
+
+ }
+
+ /* return the number of samples read/written */
+ return n;
+}
+
+
+static int adq12b_di_insn_bits(struct comedi_device *dev,struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data)
+{
+
+ /* only bits 0-4 have information about digital inputs */
+ data[1] = (inb(dev->iobase+ADQ12B_STINR) & (0x1f));
+
+ return 2;
+}
+
+
+static int adq12b_do_insn_bits(struct comedi_device *dev,struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data)
+{
+ int channel;
+
+ for (channel = 0; channel < 8; channel++)
+ if (((data[0]>>channel) & 0x01) != 0)
+ outb((((data[1]>>channel)&0x01)<<3) | channel, dev->iobase + ADQ12B_OUTBR);
+
+ /* store information to retrieve when asked for reading */
+ if (data[0]) {
+ devpriv->digital_state &= ~data[0];
+ devpriv->digital_state |= (data[0]&data[1]);
+ }
+
+ data[1] = devpriv->digital_state;
+
+ return 2;
+}
+
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_INITCLEANUP(driver_adq12b);
diff --git a/drivers/staging/comedi/drivers/adv_pci1710.c b/drivers/staging/comedi/drivers/adv_pci1710.c
new file mode 100644
index 000000000000..29eac743c8e0
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adv_pci1710.c
@@ -0,0 +1,1568 @@
+/*
+ * comedi/drivers/adv_pci1710.c
+ *
+ * Author: Michal Dobes <dobes@tesnet.cz>
+ *
+ * Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn>
+ * for testing and informations.
+ *
+ * hardware driver for Advantech cards:
+ * card: PCI-1710, PCI-1710HG, PCI-1711, PCI-1713, PCI-1720, PCI-1731
+ * driver: pci1710, pci1710hg, pci1711, pci1713, pci1720, pci1731
+ *
+ * Options:
+ * [0] - PCI bus number - if bus number and slot number are 0,
+ * then driver search for first unused card
+ * [1] - PCI slot number
+ *
+*/
+/*
+Driver: adv_pci1710
+Description: Advantech PCI-1710, PCI-1710HG, PCI-1711, PCI-1713,
+ Advantech PCI-1720, PCI-1731
+Author: Michal Dobes <dobes@tesnet.cz>
+Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG (pci1710hg),
+ PCI-1711 (adv_pci1710), PCI-1713, PCI-1720,
+ PCI-1731
+Status: works
+
+This driver supports AI, AO, DI and DO subdevices.
+AI subdevice supports cmd and insn interface,
+other subdevices support only insn interface.
+
+The PCI-1710 and PCI-1710HG have the same PCI device ID, so the
+driver cannot distinguish between them, as would be normal for a
+PCI driver.
+
+Configuration options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first available PCI
+ device will be used.
+*/
+
+#include "../comedidev.h"
+
+#include "comedi_pci.h"
+
+#include "8253.h"
+#include "amcc_s5933.h"
+
+#define PCI171x_PARANOIDCHECK /* if defined, then is used code which control correct channel number on every 12 bit sample */
+
+#undef PCI171X_EXTDEBUG
+
+#define DRV_NAME "adv_pci1710"
+
+#undef DPRINTK
+#ifdef PCI171X_EXTDEBUG
+#define DPRINTK(fmt, args...) rt_printk(fmt, ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+// hardware types of the cards
+#define TYPE_PCI171X 0
+#define TYPE_PCI1713 2
+#define TYPE_PCI1720 3
+
+#define IORANGE_171x 32
+#define IORANGE_1720 16
+
+#define PCI171x_AD_DATA 0 /* R: A/D data */
+#define PCI171x_SOFTTRG 0 /* W: soft trigger for A/D */
+#define PCI171x_RANGE 2 /* W: A/D gain/range register */
+#define PCI171x_MUX 4 /* W: A/D multiplexor control */
+#define PCI171x_STATUS 6 /* R: status register */
+#define PCI171x_CONTROL 6 /* W: control register */
+#define PCI171x_CLRINT 8 /* W: clear interrupts request */
+#define PCI171x_CLRFIFO 9 /* W: clear FIFO */
+#define PCI171x_DA1 10 /* W: D/A register */
+#define PCI171x_DA2 12 /* W: D/A register */
+#define PCI171x_DAREF 14 /* W: D/A reference control */
+#define PCI171x_DI 16 /* R: digi inputs */
+#define PCI171x_DO 16 /* R: digi inputs */
+#define PCI171x_CNT0 24 /* R/W: 8254 couter 0 */
+#define PCI171x_CNT1 26 /* R/W: 8254 couter 1 */
+#define PCI171x_CNT2 28 /* R/W: 8254 couter 2 */
+#define PCI171x_CNTCTRL 30 /* W: 8254 counter control */
+
+// upper bits from status register (PCI171x_STATUS) (lower is same woth control reg)
+#define Status_FE 0x0100 /* 1=FIFO is empty */
+#define Status_FH 0x0200 /* 1=FIFO is half full */
+#define Status_FF 0x0400 /* 1=FIFO is full, fatal error */
+#define Status_IRQ 0x0800 /* 1=IRQ occured */
+// bits from control register (PCI171x_CONTROL)
+#define Control_CNT0 0x0040 /* 1=CNT0 have external source, 0=have internal 100kHz source */
+#define Control_ONEFH 0x0020 /* 1=IRQ on FIFO is half full, 0=every sample */
+#define Control_IRQEN 0x0010 /* 1=enable IRQ */
+#define Control_GATE 0x0008 /* 1=enable external trigger GATE (8254?) */
+#define Control_EXT 0x0004 /* 1=external trigger source */
+#define Control_PACER 0x0002 /* 1=enable internal 8254 trigger source */
+#define Control_SW 0x0001 /* 1=enable software trigger source */
+// bits from counter control register (PCI171x_CNTCTRL)
+#define Counter_BCD 0x0001 /* 0 = binary counter, 1 = BCD counter */
+#define Counter_M0 0x0002 /* M0-M2 select modes 0-5 */
+#define Counter_M1 0x0004 /* 000 = mode 0, 010 = mode 2 ... */
+#define Counter_M2 0x0008
+#define Counter_RW0 0x0010 /* RW0/RW1 select read/write mode */
+#define Counter_RW1 0x0020
+#define Counter_SC0 0x0040 /* Select Counter. Only 00 or 11 may */
+#define Counter_SC1 0x0080 /* be used, 00 for CNT0, 11 for read-back command */
+
+#define PCI1720_DA0 0 /* W: D/A register 0 */
+#define PCI1720_DA1 2 /* W: D/A register 1 */
+#define PCI1720_DA2 4 /* W: D/A register 2 */
+#define PCI1720_DA3 6 /* W: D/A register 3 */
+#define PCI1720_RANGE 8 /* R/W: D/A range register */
+#define PCI1720_SYNCOUT 9 /* W: D/A synchronized output register */
+#define PCI1720_SYNCONT 15 /* R/W: D/A synchronized control */
+
+// D/A synchronized control (PCI1720_SYNCONT)
+#define Syncont_SC0 1 /* set synchronous output mode */
+
+static const struct comedi_lrange range_pci1710_3 = { 9, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ BIP_RANGE(10),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const char range_codes_pci1710_3[] =
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x10, 0x11, 0x12, 0x13 };
+
+static const struct comedi_lrange range_pci1710hg = { 12, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.005),
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.01),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01)
+ }
+};
+
+static const char range_codes_pci1710hg[] =
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12,
+ 0x13 };
+
+static const struct comedi_lrange range_pci17x1 = { 5, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625)
+ }
+};
+
+static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
+
+static const struct comedi_lrange range_pci1720 = { 4, {
+ UNI_RANGE(5),
+ UNI_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(10)
+ }
+};
+
+static const struct comedi_lrange range_pci171x_da = { 2, {
+ UNI_RANGE(5),
+ UNI_RANGE(10),
+ }
+};
+
+static int pci1710_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pci1710_detach(struct comedi_device * dev);
+
+struct boardtype {
+ const char *name; // board name
+ int device_id;
+ int iorange; // I/O range len
+ char have_irq; // 1=card support IRQ
+ char cardtype; // 0=1710& co. 2=1713, ...
+ int n_aichan; // num of A/D chans
+ int n_aichand; // num of A/D chans in diff mode
+ int n_aochan; // num of D/A chans
+ int n_dichan; // num of DI chans
+ int n_dochan; // num of DO chans
+ int n_counter; // num of counters
+ int ai_maxdata; // resolution of A/D
+ int ao_maxdata; // resolution of D/A
+ const struct comedi_lrange *rangelist_ai; // rangelist for A/D
+ const char *rangecode_ai; // range codes for programming
+ const struct comedi_lrange *rangelist_ao; // rangelist for D/A
+ unsigned int ai_ns_min; // max sample speed of card v ns
+ unsigned int fifo_half_size; // size of FIFO/2
+};
+
+static DEFINE_PCI_DEVICE_TABLE(pci1710_pci_table) = {
+ {PCI_VENDOR_ID_ADVANTECH, 0x1710, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ADVANTECH, 0x1711, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ADVANTECH, 0x1713, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ADVANTECH, 0x1720, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ADVANTECH, 0x1731, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, pci1710_pci_table);
+
+static const struct boardtype boardtypes[] = {
+ {"pci1710", 0x1710,
+ IORANGE_171x, 1, TYPE_PCI171X,
+ 16, 8, 2, 16, 16, 1, 0x0fff, 0x0fff,
+ &range_pci1710_3, range_codes_pci1710_3,
+ &range_pci171x_da,
+ 10000, 2048},
+ {"pci1710hg", 0x1710,
+ IORANGE_171x, 1, TYPE_PCI171X,
+ 16, 8, 2, 16, 16, 1, 0x0fff, 0x0fff,
+ &range_pci1710hg, range_codes_pci1710hg,
+ &range_pci171x_da,
+ 10000, 2048},
+ {"pci1711", 0x1711,
+ IORANGE_171x, 1, TYPE_PCI171X,
+ 16, 0, 2, 16, 16, 1, 0x0fff, 0x0fff,
+ &range_pci17x1, range_codes_pci17x1, &range_pci171x_da,
+ 10000, 512},
+ {"pci1713", 0x1713,
+ IORANGE_171x, 1, TYPE_PCI1713,
+ 32, 16, 0, 0, 0, 0, 0x0fff, 0x0000,
+ &range_pci1710_3, range_codes_pci1710_3, NULL,
+ 10000, 2048},
+ {"pci1720", 0x1720,
+ IORANGE_1720, 0, TYPE_PCI1720,
+ 0, 0, 4, 0, 0, 0, 0x0000, 0x0fff,
+ NULL, NULL, &range_pci1720,
+ 0, 0},
+ {"pci1731", 0x1731,
+ IORANGE_171x, 1, TYPE_PCI171X,
+ 16, 0, 0, 16, 16, 0, 0x0fff, 0x0000,
+ &range_pci17x1, range_codes_pci17x1, NULL,
+ 10000, 512},
+ // dummy entry corresponding to driver name
+ {.name = DRV_NAME},
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype))
+
+static struct comedi_driver driver_pci1710 = {
+ .driver_name = DRV_NAME,
+ .module = THIS_MODULE,
+ .attach = pci1710_attach,
+ .detach = pci1710_detach,
+ .num_names = n_boardtypes,
+ .board_name = &boardtypes[0].name,
+ .offset = sizeof(struct boardtype),
+};
+
+struct pci1710_private {
+ struct pci_dev *pcidev; // ptr to PCI device
+ char valid; // card is usable
+ char neverending_ai; // we do unlimited AI
+ unsigned int CntrlReg; // Control register
+ unsigned int i8254_osc_base; // frequence of onboard oscilator
+ unsigned int ai_do; // what do AI? 0=nothing, 1 to 4 mode
+ unsigned int ai_act_scan; // how many scans we finished
+ unsigned int ai_act_chan; // actual position in actual scan
+ unsigned int ai_buf_ptr; // data buffer ptr in samples
+ unsigned char ai_eos; // 1=EOS wake up
+ unsigned char ai_et;
+ unsigned int ai_et_CntrlReg;
+ unsigned int ai_et_MuxVal;
+ unsigned int ai_et_div1, ai_et_div2;
+ unsigned int act_chanlist[32]; // list of scaned channel
+ unsigned char act_chanlist_len; // len of scanlist
+ unsigned char act_chanlist_pos; // actual position in MUX list
+ unsigned char da_ranges; // copy of D/A outpit range register
+ unsigned int ai_scans; // len of scanlist
+ unsigned int ai_n_chan; // how many channels is measured
+ unsigned int *ai_chanlist; // actaul chanlist
+ unsigned int ai_flags; // flaglist
+ unsigned int ai_data_len; // len of data buffer
+ short *ai_data; // data buffer
+ unsigned int ai_timer1; // timers
+ unsigned int ai_timer2;
+ short ao_data[4]; // data output buffer
+ unsigned int cnt0_write_wait; // after a write, wait for update of the internal state
+};
+
+#define devpriv ((struct pci1710_private *)dev->private)
+#define this_board ((const struct boardtype *)dev->board_ptr)
+
+/*
+==============================================================================
+*/
+
+static int check_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int *chanlist, unsigned int n_chan);
+static void setup_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int *chanlist, unsigned int n_chan, unsigned int seglen);
+static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
+ unsigned int divisor2);
+static int pci1710_reset(struct comedi_device * dev);
+static int pci171x_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+
+static const unsigned int muxonechan[] = { 0x0000, 0x0101, 0x0202, 0x0303, 0x0404, 0x0505, 0x0606, 0x0707, // used for gain list programming
+ 0x0808, 0x0909, 0x0a0a, 0x0b0b, 0x0c0c, 0x0d0d, 0x0e0e, 0x0f0f,
+ 0x1010, 0x1111, 0x1212, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717,
+ 0x1818, 0x1919, 0x1a1a, 0x1b1b, 0x1c1c, 0x1d1d, 0x1e1e, 0x1f1f
+};
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_read_ai(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, timeout;
+#ifdef PCI171x_PARANOIDCHECK
+ unsigned int idata;
+#endif
+
+ DPRINTK("adv_pci1710 EDBG: BGN: pci171x_insn_read_ai(...)\n");
+ devpriv->CntrlReg &= Control_CNT0;
+ devpriv->CntrlReg |= Control_SW; // set software trigger
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+ outb(0, dev->iobase + PCI171x_CLRFIFO);
+ outb(0, dev->iobase + PCI171x_CLRINT);
+
+ setup_channel_list(dev, s, &insn->chanspec, 1, 1);
+
+ DPRINTK("adv_pci1710 A ST=%4x IO=%x\n",
+ inw(dev->iobase + PCI171x_STATUS),
+ dev->iobase + PCI171x_STATUS);
+ for (n = 0; n < insn->n; n++) {
+ outw(0, dev->iobase + PCI171x_SOFTTRG); /* start conversion */
+ DPRINTK("adv_pci1710 B n=%d ST=%4x\n", n,
+ inw(dev->iobase + PCI171x_STATUS));
+ //comedi_udelay(1);
+ DPRINTK("adv_pci1710 C n=%d ST=%4x\n", n,
+ inw(dev->iobase + PCI171x_STATUS));
+ timeout = 100;
+ while (timeout--) {
+ if (!(inw(dev->iobase + PCI171x_STATUS) & Status_FE))
+ goto conv_finish;
+ if (!(timeout % 10))
+ DPRINTK("adv_pci1710 D n=%d tm=%d ST=%4x\n", n,
+ timeout,
+ inw(dev->iobase + PCI171x_STATUS));
+ }
+ comedi_error(dev, "A/D insn timeout");
+ outb(0, dev->iobase + PCI171x_CLRFIFO);
+ outb(0, dev->iobase + PCI171x_CLRINT);
+ data[n] = 0;
+ DPRINTK("adv_pci1710 EDBG: END: pci171x_insn_read_ai(...) n=%d\n", n);
+ return -ETIME;
+
+ conv_finish:
+#ifdef PCI171x_PARANOIDCHECK
+ idata = inw(dev->iobase + PCI171x_AD_DATA);
+ if (this_board->cardtype != TYPE_PCI1713)
+ if ((idata & 0xf000) != devpriv->act_chanlist[0]) {
+ comedi_error(dev, "A/D insn data droput!");
+ return -ETIME;
+ }
+ data[n] = idata & 0x0fff;
+#else
+ data[n] = inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff;
+#endif
+
+ }
+
+ outb(0, dev->iobase + PCI171x_CLRFIFO);
+ outb(0, dev->iobase + PCI171x_CLRINT);
+
+ DPRINTK("adv_pci1710 EDBG: END: pci171x_insn_read_ai(...) n=%d\n", n);
+ return n;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_write_ao(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, chan, range, ofs;
+
+ chan = CR_CHAN(insn->chanspec);
+ range = CR_RANGE(insn->chanspec);
+ if (chan) {
+ devpriv->da_ranges &= 0xfb;
+ devpriv->da_ranges |= (range << 2);
+ outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
+ ofs = PCI171x_DA2;
+ } else {
+ devpriv->da_ranges &= 0xfe;
+ devpriv->da_ranges |= range;
+ outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
+ ofs = PCI171x_DA1;
+ }
+
+ for (n = 0; n < insn->n; n++)
+ outw(data[n], dev->iobase + ofs);
+
+ devpriv->ao_data[chan] = data[n];
+
+ return n;
+
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_read_ao(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, chan;
+
+ chan = CR_CHAN(insn->chanspec);
+ for (n = 0; n < insn->n; n++)
+ data[n] = devpriv->ao_data[chan];
+
+ return n;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_bits_di(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[1] = inw(dev->iobase + PCI171x_DI);
+
+ return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_bits_do(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ outw(s->state, dev->iobase + PCI171x_DO);
+ }
+ data[1] = s->state;
+
+ return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_counter_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int msb, lsb, ccntrl;
+ int i;
+
+ ccntrl = 0xD2; /* count only */
+ for (i = 0; i < insn->n; i++) {
+ outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
+
+ lsb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
+ msb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
+
+ data[0] = lsb | (msb << 8);
+ }
+
+ return insn->n;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_counter_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ uint msb, lsb, ccntrl, status;
+
+ lsb = data[0] & 0x00FF;
+ msb = (data[0] & 0xFF00) >> 8;
+
+ /* write lsb, then msb */
+ outw(lsb, dev->iobase + PCI171x_CNT0);
+ outw(msb, dev->iobase + PCI171x_CNT0);
+
+ if (devpriv->cnt0_write_wait) {
+ /* wait for the new count to be loaded */
+ ccntrl = 0xE2;
+ do {
+ outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
+ status = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
+ } while (status & 0x40);
+ }
+
+ return insn->n;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_insn_counter_config(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+#ifdef unused
+ /* This doesn't work like a normal Comedi counter config */
+ uint ccntrl = 0;
+
+ devpriv->cnt0_write_wait = data[0] & 0x20;
+
+ /* internal or external clock? */
+ if (!(data[0] & 0x10)) { /* internal */
+ devpriv->CntrlReg &= ~Control_CNT0;
+ } else {
+ devpriv->CntrlReg |= Control_CNT0;
+ }
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+
+ if (data[0] & 0x01)
+ ccntrl |= Counter_M0;
+ if (data[0] & 0x02)
+ ccntrl |= Counter_M1;
+ if (data[0] & 0x04)
+ ccntrl |= Counter_M2;
+ if (data[0] & 0x08)
+ ccntrl |= Counter_BCD;
+ ccntrl |= Counter_RW0; /* set read/write mode */
+ ccntrl |= Counter_RW1;
+ outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
+#endif
+
+ return 1;
+}
+
+/*
+==============================================================================
+*/
+static int pci1720_insn_write_ao(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, rangereg, chan;
+
+ chan = CR_CHAN(insn->chanspec);
+ rangereg = devpriv->da_ranges & (~(0x03 << (chan << 1)));
+ rangereg |= (CR_RANGE(insn->chanspec) << (chan << 1));
+ if (rangereg != devpriv->da_ranges) {
+ outb(rangereg, dev->iobase + PCI1720_RANGE);
+ devpriv->da_ranges = rangereg;
+ }
+
+ for (n = 0; n < insn->n; n++) {
+ outw(data[n], dev->iobase + PCI1720_DA0 + (chan << 1));
+ outb(0, dev->iobase + PCI1720_SYNCOUT); // update outputs
+ }
+
+ devpriv->ao_data[chan] = data[n];
+
+ return n;
+}
+
+/*
+==============================================================================
+*/
+static void interrupt_pci1710_every_sample(void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+ int m;
+#ifdef PCI171x_PARANOIDCHECK
+ short sampl;
+#endif
+
+ DPRINTK("adv_pci1710 EDBG: BGN: interrupt_pci1710_every_sample(...)\n");
+ m = inw(dev->iobase + PCI171x_STATUS);
+ if (m & Status_FE) {
+ rt_printk("comedi%d: A/D FIFO empty (%4x)\n", dev->minor, m);
+ pci171x_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return;
+ }
+ if (m & Status_FF) {
+ rt_printk
+ ("comedi%d: A/D FIFO Full status (Fatal Error!) (%4x)\n",
+ dev->minor, m);
+ pci171x_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return;
+ }
+
+ outb(0, dev->iobase + PCI171x_CLRINT); // clear our INT request
+
+ DPRINTK("FOR ");
+ for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
+#ifdef PCI171x_PARANOIDCHECK
+ sampl = inw(dev->iobase + PCI171x_AD_DATA);
+ DPRINTK("%04x:", sampl);
+ if (this_board->cardtype != TYPE_PCI1713)
+ if ((sampl & 0xf000) !=
+ devpriv->act_chanlist[s->async->cur_chan]) {
+ rt_printk
+ ("comedi: A/D data dropout: received data from channel %d, expected %d!\n",
+ (sampl & 0xf000) >> 12,
+ (devpriv->act_chanlist[s->async->
+ cur_chan] & 0xf000) >>
+ 12);
+ pci171x_ai_cancel(dev, s);
+ s->async->events |=
+ COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return;
+ }
+ DPRINTK("%8d %2d %8d~", s->async->buf_int_ptr,
+ s->async->cur_chan, s->async->buf_int_count);
+ comedi_buf_put(s->async, sampl & 0x0fff);
+#else
+ comedi_buf_put(s->async,
+ inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff);
+#endif
+ ++s->async->cur_chan;
+
+ if (s->async->cur_chan >= devpriv->ai_n_chan) {
+ s->async->cur_chan = 0;
+ }
+
+ if (s->async->cur_chan == 0) { // one scan done
+ devpriv->ai_act_scan++;
+ DPRINTK("adv_pci1710 EDBG: EOS1 bic %d bip %d buc %d bup %d\n", s->async->buf_int_count, s->async->buf_int_ptr, s->async->buf_user_count, s->async->buf_user_ptr);
+ DPRINTK("adv_pci1710 EDBG: EOS2\n");
+ if ((!devpriv->neverending_ai) && (devpriv->ai_act_scan >= devpriv->ai_scans)) { // all data sampled
+ pci171x_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ return;
+ }
+ }
+ }
+
+ outb(0, dev->iobase + PCI171x_CLRINT); // clear our INT request
+ DPRINTK("adv_pci1710 EDBG: END: interrupt_pci1710_every_sample(...)\n");
+
+ comedi_event(dev, s);
+}
+
+/*
+==============================================================================
+*/
+static int move_block_from_fifo(struct comedi_device * dev, struct comedi_subdevice * s,
+ int n, int turn)
+{
+ int i, j;
+#ifdef PCI171x_PARANOIDCHECK
+ int sampl;
+#endif
+ DPRINTK("adv_pci1710 EDBG: BGN: move_block_from_fifo(...,%d,%d)\n", n,
+ turn);
+ j = s->async->cur_chan;
+ for (i = 0; i < n; i++) {
+#ifdef PCI171x_PARANOIDCHECK
+ sampl = inw(dev->iobase + PCI171x_AD_DATA);
+ if (this_board->cardtype != TYPE_PCI1713)
+ if ((sampl & 0xf000) != devpriv->act_chanlist[j]) {
+ rt_printk
+ ("comedi%d: A/D FIFO data dropout: received data from channel %d, expected %d! (%d/%d/%d/%d/%d/%4x)\n",
+ dev->minor, (sampl & 0xf000) >> 12,
+ (devpriv->
+ act_chanlist[j] & 0xf000) >> 12,
+ i, j, devpriv->ai_act_scan, n, turn,
+ sampl);
+ pci171x_ai_cancel(dev, s);
+ s->async->events |=
+ COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return 1;
+ }
+ comedi_buf_put(s->async, sampl & 0x0fff);
+#else
+ comedi_buf_put(s->async,
+ inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff);
+#endif
+ j++;
+ if (j >= devpriv->ai_n_chan) {
+ j = 0;
+ devpriv->ai_act_scan++;
+ }
+ }
+ DPRINTK("adv_pci1710 EDBG: END: move_block_from_fifo(...)\n");
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static void interrupt_pci1710_half_fifo(void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+ int m, samplesinbuf;
+
+ DPRINTK("adv_pci1710 EDBG: BGN: interrupt_pci1710_half_fifo(...)\n");
+ m = inw(dev->iobase + PCI171x_STATUS);
+ if (!(m & Status_FH)) {
+ rt_printk("comedi%d: A/D FIFO not half full! (%4x)\n",
+ dev->minor, m);
+ pci171x_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return;
+ }
+ if (m & Status_FF) {
+ rt_printk
+ ("comedi%d: A/D FIFO Full status (Fatal Error!) (%4x)\n",
+ dev->minor, m);
+ pci171x_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return;
+ }
+
+ samplesinbuf = this_board->fifo_half_size;
+ if (samplesinbuf * sizeof(short) >= devpriv->ai_data_len) {
+ m = devpriv->ai_data_len / sizeof(short);
+ if (move_block_from_fifo(dev, s, m, 0))
+ return;
+ samplesinbuf -= m;
+ }
+
+ if (samplesinbuf) {
+ if (move_block_from_fifo(dev, s, samplesinbuf, 1))
+ return;
+ }
+
+ if (!devpriv->neverending_ai)
+ if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
+ pci171x_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ return;
+ }
+ outb(0, dev->iobase + PCI171x_CLRINT); // clear our INT request
+ DPRINTK("adv_pci1710 EDBG: END: interrupt_pci1710_half_fifo(...)\n");
+
+ comedi_event(dev, s);
+}
+
+/*
+==============================================================================
+*/
+static irqreturn_t interrupt_service_pci1710(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+
+ DPRINTK("adv_pci1710 EDBG: BGN: interrupt_service_pci1710(%d,...)\n",
+ irq);
+ if (!dev->attached) // is device attached?
+ return IRQ_NONE; // no, exit
+
+ if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ)) // is this interrupt from our board?
+ return IRQ_NONE; // no, exit
+
+ DPRINTK("adv_pci1710 EDBG: interrupt_service_pci1710() ST: %4x\n",
+ inw(dev->iobase + PCI171x_STATUS));
+
+ if (devpriv->ai_et) { // Switch from initial TRIG_EXT to TRIG_xxx.
+ devpriv->ai_et = 0;
+ devpriv->CntrlReg &= Control_CNT0;
+ devpriv->CntrlReg |= Control_SW; // set software trigger
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+ devpriv->CntrlReg = devpriv->ai_et_CntrlReg;
+ outb(0, dev->iobase + PCI171x_CLRFIFO);
+ outb(0, dev->iobase + PCI171x_CLRINT);
+ outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+ // start pacer
+ start_pacer(dev, 1, devpriv->ai_et_div1, devpriv->ai_et_div2);
+ return IRQ_HANDLED;
+ }
+ if (devpriv->ai_eos) { // We use FIFO half full INT or not?
+ interrupt_pci1710_every_sample(d);
+ } else {
+ interrupt_pci1710_half_fifo(d);
+ }
+ DPRINTK("adv_pci1710 EDBG: END: interrupt_service_pci1710(...)\n");
+ return IRQ_HANDLED;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_ai_docmd_and_mode(int mode, struct comedi_device * dev,
+ struct comedi_subdevice * s)
+{
+ unsigned int divisor1, divisor2;
+ unsigned int seglen;
+
+ DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_docmd_and_mode(%d,...)\n",
+ mode);
+ start_pacer(dev, -1, 0, 0); // stop pacer
+
+ seglen = check_channel_list(dev, s, devpriv->ai_chanlist,
+ devpriv->ai_n_chan);
+ if (seglen < 1)
+ return -EINVAL;
+ setup_channel_list(dev, s, devpriv->ai_chanlist,
+ devpriv->ai_n_chan, seglen);
+
+ outb(0, dev->iobase + PCI171x_CLRFIFO);
+ outb(0, dev->iobase + PCI171x_CLRINT);
+
+ devpriv->ai_do = mode;
+
+ devpriv->ai_act_scan = 0;
+ s->async->cur_chan = 0;
+ devpriv->ai_buf_ptr = 0;
+ devpriv->neverending_ai = 0;
+
+ devpriv->CntrlReg &= Control_CNT0;
+ if ((devpriv->ai_flags & TRIG_WAKE_EOS)) { // don't we want wake up every scan? devpriv->ai_eos=1;
+ devpriv->ai_eos = 1;
+ } else {
+ devpriv->CntrlReg |= Control_ONEFH;
+ devpriv->ai_eos = 0;
+ }
+
+ if ((devpriv->ai_scans == 0) || (devpriv->ai_scans == -1)) {
+ devpriv->neverending_ai = 1;
+ } //well, user want neverending
+ else {
+ devpriv->neverending_ai = 0;
+ }
+ switch (mode) {
+ case 1:
+ case 2:
+ if (devpriv->ai_timer1 < this_board->ai_ns_min)
+ devpriv->ai_timer1 = this_board->ai_ns_min;
+ devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
+ if (mode == 2) {
+ devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
+ devpriv->CntrlReg &=
+ ~(Control_PACER | Control_ONEFH | Control_GATE);
+ devpriv->CntrlReg |= Control_EXT;
+ devpriv->ai_et = 1;
+ } else {
+ devpriv->ai_et = 0;
+ }
+ i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
+ &divisor2, &devpriv->ai_timer1,
+ devpriv->ai_flags & TRIG_ROUND_MASK);
+ DPRINTK("adv_pci1710 EDBG: OSC base=%u div1=%u div2=%u timer=%u\n", devpriv->i8254_osc_base, divisor1, divisor2, devpriv->ai_timer1);
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+ if (mode != 2) {
+ // start pacer
+ start_pacer(dev, mode, divisor1, divisor2);
+ } else {
+ devpriv->ai_et_div1 = divisor1;
+ devpriv->ai_et_div2 = divisor2;
+ }
+ break;
+ case 3:
+ devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+ break;
+ }
+
+ DPRINTK("adv_pci1710 EDBG: END: pci171x_ai_docmd_and_mode(...)\n");
+ return 0;
+}
+
+#ifdef PCI171X_EXTDEBUG
+/*
+==============================================================================
+*/
+static void pci171x_cmdtest_out(int e, struct comedi_cmd * cmd)
+{
+ rt_printk("adv_pci1710 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
+ cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
+ rt_printk("adv_pci1710 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
+ cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
+ rt_printk("adv_pci1710 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
+ cmd->scan_end_src);
+ rt_printk("adv_pci1710 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n",
+ e, cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
+}
+#endif
+
+/*
+==============================================================================
+*/
+static int pci171x_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp, divisor1, divisor2;
+
+ DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...)\n");
+#ifdef PCI171X_EXTDEBUG
+ pci171x_cmdtest_out(-1, cmd);
+#endif
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_EXT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_FOLLOW;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err) {
+#ifdef PCI171X_EXTDEBUG
+ pci171x_cmdtest_out(1, cmd);
+#endif
+ DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=1\n", err);
+ return 1;
+ }
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT) {
+ cmd->start_src = TRIG_NOW;
+ err++;
+ }
+
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ cmd->scan_begin_src = TRIG_FOLLOW;
+ err++;
+ }
+
+ if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
+ err++;
+
+ if (cmd->scan_end_src != TRIG_COUNT) {
+ cmd->scan_end_src = TRIG_COUNT;
+ err++;
+ }
+
+ if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
+ err++;
+
+ if (err) {
+#ifdef PCI171X_EXTDEBUG
+ pci171x_cmdtest_out(2, cmd);
+#endif
+ DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=2\n", err);
+ return 2;
+ }
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < this_board->ai_ns_min) {
+ cmd->convert_arg = this_board->ai_ns_min;
+ err++;
+ }
+ } else { /* TRIG_FOLLOW */
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ }
+
+ if (!cmd->chanlist_len) {
+ cmd->chanlist_len = 1;
+ err++;
+ }
+ if (cmd->chanlist_len > this_board->n_aichan) {
+ cmd->chanlist_len = this_board->n_aichan;
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (!cmd->stop_arg) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ } else { /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err) {
+#ifdef PCI171X_EXTDEBUG
+ pci171x_cmdtest_out(3, cmd);
+#endif
+ DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=3\n", err);
+ return 3;
+ }
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
+ &divisor2, &cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (cmd->convert_arg < this_board->ai_ns_min)
+ cmd->convert_arg = this_board->ai_ns_min;
+ if (tmp != cmd->convert_arg)
+ err++;
+ }
+
+ if (err) {
+ DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=4\n", err);
+ return 4;
+ }
+
+ /* step 5: complain about special chanlist considerations */
+
+ if (cmd->chanlist) {
+ if (!check_channel_list(dev, s, cmd->chanlist,
+ cmd->chanlist_len))
+ return 5; // incorrect channels list
+ }
+
+ DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) ret=0\n");
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmd(...)\n");
+ devpriv->ai_n_chan = cmd->chanlist_len;
+ devpriv->ai_chanlist = cmd->chanlist;
+ devpriv->ai_flags = cmd->flags;
+ devpriv->ai_data_len = s->async->prealloc_bufsz;
+ devpriv->ai_data = s->async->prealloc_buf;
+ devpriv->ai_timer1 = 0;
+ devpriv->ai_timer2 = 0;
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ devpriv->ai_scans = cmd->stop_arg;
+ } else {
+ devpriv->ai_scans = 0;
+ }
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) { // mode 1, 2, 3
+ if (cmd->convert_src == TRIG_TIMER) { // mode 1 and 2
+ devpriv->ai_timer1 = cmd->convert_arg;
+ return pci171x_ai_docmd_and_mode(cmd->start_src ==
+ TRIG_EXT ? 2 : 1, dev, s);
+ }
+ if (cmd->convert_src == TRIG_EXT) { // mode 3
+ return pci171x_ai_docmd_and_mode(3, dev, s);
+ }
+ }
+
+ return -1;
+}
+
+/*
+==============================================================================
+ Check if channel list from user is builded correctly
+ If it's ok, then program scan/gain logic.
+ This works for all cards.
+*/
+static int check_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int *chanlist, unsigned int n_chan)
+{
+ unsigned int chansegment[32];
+ unsigned int i, nowmustbechan, seglen, segpos;
+
+ DPRINTK("adv_pci1710 EDBG: check_channel_list(...,%d)\n", n_chan);
+ /* correct channel and range number check itself comedi/range.c */
+ if (n_chan < 1) {
+ comedi_error(dev, "range/channel list is empty!");
+ return 0;
+ }
+
+ if (n_chan > 1) {
+ chansegment[0] = chanlist[0]; // first channel is everytime ok
+ for (i = 1, seglen = 1; i < n_chan; i++, seglen++) { // build part of chanlist
+ // rt_printk("%d. %d %d\n",i,CR_CHAN(chanlist[i]),CR_RANGE(chanlist[i]));
+ if (chanlist[0] == chanlist[i])
+ break; // we detect loop, this must by finish
+ if (CR_CHAN(chanlist[i]) & 1) // odd channel cann't by differencial
+ if (CR_AREF(chanlist[i]) == AREF_DIFF) {
+ comedi_error(dev,
+ "Odd channel can't be differential input!\n");
+ return 0;
+ }
+ nowmustbechan =
+ (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
+ if (CR_AREF(chansegment[i - 1]) == AREF_DIFF)
+ nowmustbechan = (nowmustbechan + 1) % s->n_chan;
+ if (nowmustbechan != CR_CHAN(chanlist[i])) { // channel list isn't continous :-(
+ rt_printk
+ ("channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
+ i, CR_CHAN(chanlist[i]), nowmustbechan,
+ CR_CHAN(chanlist[0]));
+ return 0;
+ }
+ chansegment[i] = chanlist[i]; // well, this is next correct channel in list
+ }
+
+ for (i = 0, segpos = 0; i < n_chan; i++) { // check whole chanlist
+ //rt_printk("%d %d=%d %d\n",CR_CHAN(chansegment[i%seglen]),CR_RANGE(chansegment[i%seglen]),CR_CHAN(chanlist[i]),CR_RANGE(chanlist[i]));
+ if (chanlist[i] != chansegment[i % seglen]) {
+ rt_printk
+ ("bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
+ i, CR_CHAN(chansegment[i]),
+ CR_RANGE(chansegment[i]),
+ CR_AREF(chansegment[i]),
+ CR_CHAN(chanlist[i % seglen]),
+ CR_RANGE(chanlist[i % seglen]),
+ CR_AREF(chansegment[i % seglen]));
+ return 0; // chan/gain list is strange
+ }
+ }
+ } else {
+ seglen = 1;
+ }
+ return seglen;
+}
+
+static void setup_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int *chanlist, unsigned int n_chan, unsigned int seglen)
+{
+ unsigned int i, range, chanprog;
+
+ DPRINTK("adv_pci1710 EDBG: setup_channel_list(...,%d,%d)\n", n_chan,
+ seglen);
+ devpriv->act_chanlist_len = seglen;
+ devpriv->act_chanlist_pos = 0;
+
+ DPRINTK("SegLen: %d\n", seglen);
+ for (i = 0; i < seglen; i++) { // store range list to card
+ chanprog = muxonechan[CR_CHAN(chanlist[i])];
+ outw(chanprog, dev->iobase + PCI171x_MUX); /* select channel */
+ range = this_board->rangecode_ai[CR_RANGE(chanlist[i])];
+ if (CR_AREF(chanlist[i]) == AREF_DIFF)
+ range |= 0x0020;
+ outw(range, dev->iobase + PCI171x_RANGE); /* select gain */
+#ifdef PCI171x_PARANOIDCHECK
+ devpriv->act_chanlist[i] =
+ (CR_CHAN(chanlist[i]) << 12) & 0xf000;
+#endif
+ DPRINTK("GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
+ devpriv->act_chanlist[i]);
+ }
+
+ devpriv->ai_et_MuxVal =
+ CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8);
+ outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX); /* select channel interval to scan */
+ DPRINTK("MUX: %4x L%4x.H%4x\n",
+ CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8),
+ CR_CHAN(chanlist[0]), CR_CHAN(chanlist[seglen - 1]));
+}
+
+/*
+==============================================================================
+*/
+static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
+ unsigned int divisor2)
+{
+ DPRINTK("adv_pci1710 EDBG: BGN: start_pacer(%d,%u,%u)\n", mode,
+ divisor1, divisor2);
+ outw(0xb4, dev->iobase + PCI171x_CNTCTRL);
+ outw(0x74, dev->iobase + PCI171x_CNTCTRL);
+
+ if (mode == 1) {
+ outw(divisor2 & 0xff, dev->iobase + PCI171x_CNT2);
+ outw((divisor2 >> 8) & 0xff, dev->iobase + PCI171x_CNT2);
+ outw(divisor1 & 0xff, dev->iobase + PCI171x_CNT1);
+ outw((divisor1 >> 8) & 0xff, dev->iobase + PCI171x_CNT1);
+ }
+ DPRINTK("adv_pci1710 EDBG: END: start_pacer(...)\n");
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cancel(...)\n");
+
+ switch (this_board->cardtype) {
+ default:
+ devpriv->CntrlReg &= Control_CNT0;
+ devpriv->CntrlReg |= Control_SW;
+
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); // reset any operations
+ start_pacer(dev, -1, 0, 0);
+ outb(0, dev->iobase + PCI171x_CLRFIFO);
+ outb(0, dev->iobase + PCI171x_CLRINT);
+ break;
+ }
+
+ devpriv->ai_do = 0;
+ devpriv->ai_act_scan = 0;
+ s->async->cur_chan = 0;
+ devpriv->ai_buf_ptr = 0;
+ devpriv->neverending_ai = 0;
+
+ DPRINTK("adv_pci1710 EDBG: END: pci171x_ai_cancel(...)\n");
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci171x_reset(struct comedi_device * dev)
+{
+ DPRINTK("adv_pci1710 EDBG: BGN: pci171x_reset(...)\n");
+ outw(0x30, dev->iobase + PCI171x_CNTCTRL);
+ devpriv->CntrlReg = Control_SW | Control_CNT0; // Software trigger, CNT0=external
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); // reset any operations
+ outb(0, dev->iobase + PCI171x_CLRFIFO); // clear FIFO
+ outb(0, dev->iobase + PCI171x_CLRINT); // clear INT request
+ start_pacer(dev, -1, 0, 0); // stop 8254
+ devpriv->da_ranges = 0;
+ if (this_board->n_aochan) {
+ outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF); // set DACs to 0..5V
+ outw(0, dev->iobase + PCI171x_DA1); // set DA outputs to 0V
+ devpriv->ao_data[0] = 0x0000;
+ if (this_board->n_aochan > 1) {
+ outw(0, dev->iobase + PCI171x_DA2);
+ devpriv->ao_data[1] = 0x0000;
+ }
+ }
+ outw(0, dev->iobase + PCI171x_DO); // digital outputs to 0
+ outb(0, dev->iobase + PCI171x_CLRFIFO); // clear FIFO
+ outb(0, dev->iobase + PCI171x_CLRINT); // clear INT request
+
+ DPRINTK("adv_pci1710 EDBG: END: pci171x_reset(...)\n");
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci1720_reset(struct comedi_device * dev)
+{
+ DPRINTK("adv_pci1710 EDBG: BGN: pci1720_reset(...)\n");
+ outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT); // set synchronous output mode
+ devpriv->da_ranges = 0xAA;
+ outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE); // set all ranges to +/-5V
+ outw(0x0800, dev->iobase + PCI1720_DA0); // set outputs to 0V
+ outw(0x0800, dev->iobase + PCI1720_DA1);
+ outw(0x0800, dev->iobase + PCI1720_DA2);
+ outw(0x0800, dev->iobase + PCI1720_DA3);
+ outb(0, dev->iobase + PCI1720_SYNCOUT); // update outputs
+ devpriv->ao_data[0] = 0x0800;
+ devpriv->ao_data[1] = 0x0800;
+ devpriv->ao_data[2] = 0x0800;
+ devpriv->ao_data[3] = 0x0800;
+ DPRINTK("adv_pci1710 EDBG: END: pci1720_reset(...)\n");
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci1710_reset(struct comedi_device * dev)
+{
+ DPRINTK("adv_pci1710 EDBG: BGN: pci1710_reset(...)\n");
+ switch (this_board->cardtype) {
+ case TYPE_PCI1720:
+ return pci1720_reset(dev);
+ default:
+ return pci171x_reset(dev);
+ }
+ DPRINTK("adv_pci1710 EDBG: END: pci1710_reset(...)\n");
+}
+
+/*
+==============================================================================
+*/
+static int pci1710_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int ret, subdev, n_subdevices;
+ unsigned int irq;
+ unsigned long iobase;
+ struct pci_dev *pcidev;
+ int opt_bus, opt_slot;
+ const char *errstr;
+ unsigned char pci_bus, pci_slot, pci_func;
+ int i;
+ int board_index;
+
+ rt_printk("comedi%d: adv_pci1710: ", dev->minor);
+
+ opt_bus = it->options[0];
+ opt_slot = it->options[1];
+
+ if ((ret = alloc_private(dev, sizeof(struct pci1710_private))) < 0) {
+ rt_printk(" - Allocation failed!\n");
+ return -ENOMEM;
+ }
+
+ /* Look for matching PCI device */
+ errstr = "not found!";
+ pcidev = NULL;
+ board_index = this_board - boardtypes;
+ while (NULL != (pcidev = pci_get_device(PCI_VENDOR_ID_ADVANTECH,
+ PCI_ANY_ID, pcidev))) {
+ if(strcmp(this_board->name, DRV_NAME) == 0)
+ {
+ for(i = 0; i < n_boardtypes; ++i)
+ {
+ if(pcidev->device == boardtypes[i].device_id)
+ {
+ board_index = i;
+ break;
+ }
+ }
+ if(i == n_boardtypes) continue;
+ }else
+ {
+ if(pcidev->device != boardtypes[board_index].device_id) continue;
+ }
+
+ /* Found matching vendor/device. */
+ if (opt_bus || opt_slot) {
+ /* Check bus/slot. */
+ if (opt_bus != pcidev->bus->number
+ || opt_slot != PCI_SLOT(pcidev->devfn))
+ continue; /* no match */
+ }
+ /*
+ * Look for device that isn't in use.
+ * Enable PCI device and request regions.
+ */
+ if (comedi_pci_enable(pcidev, DRV_NAME)) {
+ errstr = "failed to enable PCI device and request regions!";
+ continue;
+ }
+ // fixup board_ptr in case we were using the dummy entry with the driver name
+ dev->board_ptr = &boardtypes[board_index];
+ break;
+ }
+
+ if (!pcidev) {
+ if (opt_bus || opt_slot) {
+ rt_printk(" - Card at b:s %d:%d %s\n",
+ opt_bus, opt_slot, errstr);
+ } else {
+ rt_printk(" - Card %s\n", errstr);
+ }
+ return -EIO;
+ }
+
+ pci_bus = pcidev->bus->number;
+ pci_slot = PCI_SLOT(pcidev->devfn);
+ pci_func = PCI_FUNC(pcidev->devfn);
+ irq = pcidev->irq;
+ iobase = pci_resource_start(pcidev, 2);
+
+ rt_printk(", b:s:f=%d:%d:%d, io=0x%4lx", pci_bus, pci_slot, pci_func,
+ iobase);
+
+ dev->iobase = iobase;
+
+ dev->board_name = this_board->name;
+ devpriv->pcidev = pcidev;
+
+ n_subdevices = 0;
+ if (this_board->n_aichan)
+ n_subdevices++;
+ if (this_board->n_aochan)
+ n_subdevices++;
+ if (this_board->n_dichan)
+ n_subdevices++;
+ if (this_board->n_dochan)
+ n_subdevices++;
+ if (this_board->n_counter)
+ n_subdevices++;
+
+ if ((ret = alloc_subdevices(dev, n_subdevices)) < 0) {
+ rt_printk(" - Allocation failed!\n");
+ return ret;
+ }
+
+ pci1710_reset(dev);
+
+ if (this_board->have_irq) {
+ if (irq) {
+ if (comedi_request_irq(irq, interrupt_service_pci1710,
+ IRQF_SHARED, "Advantech PCI-1710",
+ dev)) {
+ rt_printk
+ (", unable to allocate IRQ %d, DISABLING IT",
+ irq);
+ irq = 0; /* Can't use IRQ */
+ } else {
+ rt_printk(", irq=%u", irq);
+ }
+ } else {
+ rt_printk(", IRQ disabled");
+ }
+ } else {
+ irq = 0;
+ }
+
+ dev->irq = irq;
+
+ printk(".\n");
+
+ subdev = 0;
+
+ if (this_board->n_aichan) {
+ s = dev->subdevices + subdev;
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
+ if (this_board->n_aichand)
+ s->subdev_flags |= SDF_DIFF;
+ s->n_chan = this_board->n_aichan;
+ s->maxdata = this_board->ai_maxdata;
+ s->len_chanlist = this_board->n_aichan;
+ s->range_table = this_board->rangelist_ai;
+ s->cancel = pci171x_ai_cancel;
+ s->insn_read = pci171x_insn_read_ai;
+ if (irq) {
+ s->subdev_flags |= SDF_CMD_READ;
+ s->do_cmdtest = pci171x_ai_cmdtest;
+ s->do_cmd = pci171x_ai_cmd;
+ }
+ devpriv->i8254_osc_base = 100; // 100ns=10MHz
+ subdev++;
+ }
+
+ if (this_board->n_aochan) {
+ s = dev->subdevices + subdev;
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = this_board->n_aochan;
+ s->maxdata = this_board->ao_maxdata;
+ s->len_chanlist = this_board->n_aochan;
+ s->range_table = this_board->rangelist_ao;
+ switch (this_board->cardtype) {
+ case TYPE_PCI1720:
+ s->insn_write = pci1720_insn_write_ao;
+ break;
+ default:
+ s->insn_write = pci171x_insn_write_ao;
+ break;
+ }
+ s->insn_read = pci171x_insn_read_ao;
+ subdev++;
+ }
+
+ if (this_board->n_dichan) {
+ s = dev->subdevices + subdev;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = this_board->n_dichan;
+ s->maxdata = 1;
+ s->len_chanlist = this_board->n_dichan;
+ s->range_table = &range_digital;
+ s->io_bits = 0; /* all bits input */
+ s->insn_bits = pci171x_insn_bits_di;
+ subdev++;
+ }
+
+ if (this_board->n_dochan) {
+ s = dev->subdevices + subdev;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = this_board->n_dochan;
+ s->maxdata = 1;
+ s->len_chanlist = this_board->n_dochan;
+ s->range_table = &range_digital;
+ s->io_bits = (1 << this_board->n_dochan) - 1; /* all bits output */
+ s->state = 0;
+ s->insn_bits = pci171x_insn_bits_do;
+ subdev++;
+ }
+
+ if (this_board->n_counter) {
+ s = dev->subdevices + subdev;
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = this_board->n_counter;
+ s->len_chanlist = this_board->n_counter;
+ s->maxdata = 0xffff;
+ s->range_table = &range_unknown;
+ s->insn_read = pci171x_insn_counter_read;
+ s->insn_write = pci171x_insn_counter_write;
+ s->insn_config = pci171x_insn_counter_config;
+ subdev++;
+ }
+
+ devpriv->valid = 1;
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci1710_detach(struct comedi_device * dev)
+{
+
+ if (dev->private) {
+ if (devpriv->valid)
+ pci1710_reset(dev);
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+ if (devpriv->pcidev) {
+ if (dev->iobase) {
+ comedi_pci_disable(devpriv->pcidev);
+ }
+ pci_dev_put(devpriv->pcidev);
+ }
+ }
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+COMEDI_PCI_INITCLEANUP(driver_pci1710, pci1710_pci_table);
+/*
+==============================================================================
+*/
diff --git a/drivers/staging/comedi/drivers/adv_pci1723.c b/drivers/staging/comedi/drivers/adv_pci1723.c
new file mode 100644
index 000000000000..81f7ee18c150
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adv_pci1723.c
@@ -0,0 +1,465 @@
+/*******************************************************************************
+ comedi/drivers/pci1723.c
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*******************************************************************************/
+/*
+Driver: adv_pci1723
+Description: Advantech PCI-1723
+Author: yonggang <rsmgnu@gmail.com>, Ian Abbott <abbotti@mev.co.uk>
+Devices: [Advantech] PCI-1723 (adv_pci1723)
+Updated: Mon, 14 Apr 2008 15:12:56 +0100
+Status: works
+
+Configuration Options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+
+ If bus/slot is not specified, the first supported
+ PCI device found will be used.
+
+Subdevice 0 is 8-channel AO, 16-bit, range +/- 10 V.
+
+Subdevice 1 is 16-channel DIO. The channels are configurable as input or
+output in 2 groups (0 to 7, 8 to 15). Configuring any channel implicitly
+configures all channels in the same group.
+
+TODO:
+
+1. Add the two milliamp ranges to the AO subdevice (0 to 20 mA, 4 to 20 mA).
+2. Read the initial ranges and values of the AO subdevice at start-up instead
+ of reinitializing them.
+3. Implement calibration.
+*/
+
+#include "../comedidev.h"
+
+#include "comedi_pci.h"
+
+#define ADVANTECH_VENDOR 0x13fe /* Advantech PCI vendor ID */
+
+// hardware types of the cards
+#define TYPE_PCI1723 0
+
+#define IORANGE_1723 0x2A
+
+/* all the registers for the pci1723 board */
+#define PCI1723_DA(N) ((N)<<1) /* W: D/A register N (0 to 7) */
+
+#define PCI1723_SYN_SET 0x12 /*synchronized set register */
+#define PCI1723_ALL_CHNNELE_SYN_STROBE 0x12 /*synchronized status register */
+
+#define PCI1723_RANGE_CALIBRATION_MODE 0x14 /* range and calibration mode */
+#define PCI1723_RANGE_CALIBRATION_STATUS 0x14 /* range and calibration status */
+
+#define PCI1723_CONTROL_CMD_CALIBRATION_FUN 0x16 /* SADC control command for calibration function */
+#define PCI1723_STATUS_CMD_CALIBRATION_FUN 0x16 /* SADC control status for calibration function */
+
+#define PCI1723_CALIBRATION_PARA_STROBE 0x18 /* Calibration parameter strobe */
+
+#define PCI1723_DIGITAL_IO_PORT_SET 0x1A /* Digital I/O port setting */
+#define PCI1723_DIGITAL_IO_PORT_MODE 0x1A /* Digital I/O port mode */
+
+#define PCI1723_WRITE_DIGITAL_OUTPUT_CMD 0x1C /* Write digital output command */
+#define PCI1723_READ_DIGITAL_INPUT_DATA 0x1C /* Read digital input data */
+
+#define PCI1723_WRITE_CAL_CMD 0x1E /* Write calibration command */
+#define PCI1723_READ_CAL_STATUS 0x1E /* Read calibration status */
+
+#define PCI1723_SYN_STROBE 0x20 /* Synchronized strobe */
+
+#define PCI1723_RESET_ALL_CHN_STROBE 0x22 /* Reset all D/A channels strobe */
+
+#define PCI1723_RESET_CAL_CONTROL_STROBE 0x24 /* Reset the calibration controller strobe */
+
+#define PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE 0x26 /* Change D/A channels output type strobe */
+
+#define PCI1723_SELECT_CALIBRATION 0x28 /* Select the calibration Ref_V */
+
+//static unsigned short pci_list_builded=0; /*=1 list of card is know */
+
+static const struct comedi_lrange range_pci1723 = { 1, {
+ BIP_RANGE(10)
+ }
+};
+
+/*
+ * Board descriptions for pci1723 boards.
+ */
+struct pci1723_board {
+ const char *name;
+ int vendor_id; // PCI vendor a device ID of card
+ int device_id;
+ int iorange;
+ char cardtype;
+ int n_aochan; // num of D/A chans
+ int n_diochan; // num of DIO chans
+ int ao_maxdata; // resolution of D/A
+ const struct comedi_lrange *rangelist_ao; // rangelist for D/A
+};
+
+static const struct pci1723_board boardtypes[] = {
+ {
+ name: "pci1723",
+ vendor_id:ADVANTECH_VENDOR,
+ device_id:0x1723,
+ iorange: IORANGE_1723,
+ cardtype:TYPE_PCI1723,
+ n_aochan:8,
+ n_diochan:16,
+ ao_maxdata:0xffff,
+ rangelist_ao:&range_pci1723,
+ },
+};
+
+/* This is used by modprobe to translate PCI IDs to drivers. Should
+ * only be used for PCI and ISA-PnP devices */
+static DEFINE_PCI_DEVICE_TABLE(pci1723_pci_table) = {
+ {PCI_VENDOR_ID_ADVANTECH, 0x1723, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, pci1723_pci_table);
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int pci1723_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pci1723_detach(struct comedi_device * dev);
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pci1723_board))
+
+static struct comedi_driver driver_pci1723 = {
+ driver_name:"adv_pci1723",
+ module:THIS_MODULE,
+ attach:pci1723_attach,
+ detach:pci1723_detach,
+};
+
+/* this structure is for data unique to this hardware driver. */
+struct pci1723_private {
+ int valid; //card is usable;
+
+ struct pci_dev *pcidev;
+ unsigned char da_range[8]; // D/A output range for each channel
+
+ short ao_data[8]; // data output buffer
+};
+
+/*the following macro to make it easy to
+* access the private structure.
+*/
+#define devpriv ((struct pci1723_private *)dev->private)
+
+#define this_board boardtypes
+
+/*
+ * the pci1723 card reset;
+ */
+static int pci1723_reset(struct comedi_device * dev)
+{
+ int i;
+ DPRINTK("adv_pci1723 EDBG: BGN: pci1723_reset(...)\n");
+
+ outw(0x01, dev->iobase + PCI1723_SYN_SET); // set synchronous output mode
+
+ for (i = 0; i < 8; i++) {
+ // set all outputs to 0V
+ devpriv->ao_data[i] = 0x8000;
+ outw(devpriv->ao_data[i], dev->iobase + PCI1723_DA(i));
+ // set all ranges to +/- 10V
+ devpriv->da_range[i] = 0;
+ outw(((devpriv->da_range[i] << 4) | i),
+ PCI1723_RANGE_CALIBRATION_MODE);
+ }
+
+ outw(0, dev->iobase + PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE); // update ranges
+ outw(0, dev->iobase + PCI1723_SYN_STROBE); // update outputs
+
+ // set asynchronous output mode
+ outw(0, dev->iobase + PCI1723_SYN_SET);
+
+ DPRINTK("adv_pci1723 EDBG: END: pci1723_reset(...)\n");
+ return 0;
+}
+
+static int pci1723_insn_read_ao(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, chan;
+
+ chan = CR_CHAN(insn->chanspec);
+ DPRINTK(" adv_PCI1723 DEBUG: pci1723_insn_read_ao() ----- \n");
+ for (n = 0; n < insn->n; n++)
+ data[n] = devpriv->ao_data[chan];
+
+ return n;
+}
+
+/*
+ analog data output;
+*/
+static int pci1723_ao_write_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, chan;
+ chan = CR_CHAN(insn->chanspec);
+
+ DPRINTK("PCI1723: the pci1723_ao_write_winsn() ------\n");
+
+ for (n = 0; n < insn->n; n++) {
+
+ devpriv->ao_data[chan] = data[n];
+ outw(data[n], dev->iobase + PCI1723_DA(chan));
+ }
+
+ return n;
+}
+
+/*
+ digital i/o config/query
+*/
+static int pci1723_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int mask;
+ unsigned int bits;
+ unsigned short dio_mode;
+
+ mask = 1 << CR_CHAN(insn->chanspec);
+ if (mask & 0x00FF) {
+ bits = 0x00FF;
+ } else {
+ bits = 0xFF00;
+ }
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~bits;
+ break;
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= bits;
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+ default:
+ return -EINVAL;
+ }
+
+ // update hardware DIO mode
+ dio_mode = 0x0000; // low byte output, high byte output
+ if ((s->io_bits & 0x00FF) == 0)
+ dio_mode |= 0x0001; // low byte input
+ if ((s->io_bits & 0xFF00) == 0)
+ dio_mode |= 0x0002; // high byte input
+ outw(dio_mode, dev->iobase + PCI1723_DIGITAL_IO_PORT_SET);
+ return 1;
+}
+
+/*
+ digital i/o bits read/write
+*/
+static int pci1723_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ outw(s->state, dev->iobase + PCI1723_WRITE_DIGITAL_OUTPUT_CMD);
+ }
+ data[1] = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA);
+ return 2;
+}
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a pci1723 board.
+ */
+static int pci1723_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int ret, subdev, n_subdevices;
+ struct pci_dev *pcidev;
+ unsigned int iobase;
+ unsigned char pci_bus, pci_slot, pci_func;
+ int opt_bus, opt_slot;
+ const char *errstr;
+
+ rt_printk("comedi%d: adv_pci1723: board=%s", dev->minor,
+ this_board->name);
+
+ opt_bus = it->options[0];
+ opt_slot = it->options[1];
+
+ if ((ret = alloc_private(dev, sizeof(struct pci1723_private))) < 0) {
+ rt_printk(" - Allocation failed!\n");
+ return -ENOMEM;
+ }
+
+ /* Look for matching PCI device */
+ errstr = "not found!";
+ pcidev = NULL;
+ while (NULL != (pcidev =
+ pci_get_device(PCI_VENDOR_ID_ADVANTECH,
+ this_board->device_id, pcidev))) {
+ /* Found matching vendor/device. */
+ if (opt_bus || opt_slot) {
+ /* Check bus/slot. */
+ if (opt_bus != pcidev->bus->number
+ || opt_slot != PCI_SLOT(pcidev->devfn))
+ continue; /* no match */
+ }
+ /*
+ * Look for device that isn't in use.
+ * Enable PCI device and request regions.
+ */
+ if (comedi_pci_enable(pcidev, "adv_pci1723")) {
+ errstr = "failed to enable PCI device and request regions!";
+ continue;
+ }
+ break;
+ }
+
+ if (!pcidev) {
+ if (opt_bus || opt_slot) {
+ rt_printk(" - Card at b:s %d:%d %s\n",
+ opt_bus, opt_slot, errstr);
+ } else {
+ rt_printk(" - Card %s\n", errstr);
+ }
+ return -EIO;
+ }
+
+ pci_bus = pcidev->bus->number;
+ pci_slot = PCI_SLOT(pcidev->devfn);
+ pci_func = PCI_FUNC(pcidev->devfn);
+ iobase = pci_resource_start(pcidev, 2);
+
+ rt_printk(", b:s:f=%d:%d:%d, io=0x%4x", pci_bus, pci_slot, pci_func,
+ iobase);
+
+ dev->iobase = iobase;
+
+ dev->board_name = this_board->name;
+ devpriv->pcidev = pcidev;
+
+ n_subdevices = 0;
+
+ if (this_board->n_aochan)
+ n_subdevices++;
+ if (this_board->n_diochan)
+ n_subdevices++;
+
+ if ((ret = alloc_subdevices(dev, n_subdevices)) < 0) {
+ rt_printk(" - Allocation failed!\n");
+ return ret;
+ }
+
+ pci1723_reset(dev);
+ subdev = 0;
+ if (this_board->n_aochan) {
+ s = dev->subdevices + subdev;
+ dev->write_subdev = s;
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = this_board->n_aochan;
+ s->maxdata = this_board->ao_maxdata;
+ s->len_chanlist = this_board->n_aochan;
+ s->range_table = this_board->rangelist_ao;
+
+ s->insn_write = pci1723_ao_write_winsn;
+ s->insn_read = pci1723_insn_read_ao;
+
+ // read DIO config
+ switch (inw(dev->iobase + PCI1723_DIGITAL_IO_PORT_MODE) & 0x03) {
+ case 0x00: // low byte output, high byte output
+ s->io_bits = 0xFFFF;
+ break;
+ case 0x01: // low byte input, high byte output
+ s->io_bits = 0xFF00;
+ break;
+ case 0x02: // low byte output, high byte input
+ s->io_bits = 0x00FF;
+ break;
+ case 0x03: // low byte input, high byte input
+ s->io_bits = 0x0000;
+ break;
+ }
+ // read DIO port state
+ s->state = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA);
+
+ subdev++;
+ }
+
+ if (this_board->n_diochan) {
+ s = dev->subdevices + subdev;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags =
+ SDF_READABLE | SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = this_board->n_diochan;
+ s->maxdata = 1;
+ s->len_chanlist = this_board->n_diochan;
+ s->range_table = &range_digital;
+ s->insn_config = pci1723_dio_insn_config;
+ s->insn_bits = pci1723_dio_insn_bits;
+ subdev++;
+ }
+
+ devpriv->valid = 1;
+
+ pci1723_reset(dev);
+
+ return 0;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int pci1723_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: pci1723: remove\n", dev->minor);
+
+ if (dev->private) {
+ if (devpriv->valid)
+ pci1723_reset(dev);
+
+ if (devpriv->pcidev) {
+ if (dev->iobase) {
+ comedi_pci_disable(devpriv->pcidev);
+ }
+ pci_dev_put(devpriv->pcidev);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_PCI_INITCLEANUP(driver_pci1723, pci1723_pci_table);
diff --git a/drivers/staging/comedi/drivers/adv_pci_dio.c b/drivers/staging/comedi/drivers/adv_pci_dio.c
new file mode 100644
index 000000000000..2604425c3a0f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adv_pci_dio.c
@@ -0,0 +1,1077 @@
+/*
+ * comedi/drivers/adv_pci_dio.c
+ *
+ * Author: Michal Dobes <dobes@tesnet.cz>
+ *
+ * Hardware driver for Advantech PCI DIO cards.
+*/
+/*
+Driver: adv_pci_dio
+Description: Advantech PCI-1730, PCI-1733, PCI-1734, PCI-1736UP,
+ PCI-1750, PCI-1751, PCI-1752, PCI-1753/E, PCI-1754,
+ PCI-1756, PCI-1762
+Author: Michal Dobes <dobes@tesnet.cz>
+Devices: [Advantech] PCI-1730 (adv_pci_dio), PCI-1733,
+ PCI-1734, PCI-1736UP, PCI-1750,
+ PCI-1751, PCI-1752, PCI-1753,
+ PCI-1753+PCI-1753E, PCI-1754, PCI-1756,
+ PCI-1760, PCI-1762
+Status: untested
+Updated: Mon, 14 Apr 2008 10:43:08 +0100
+
+This driver supports now only insn interface for DI/DO/DIO.
+
+Configuration options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first available PCI
+ device will be used.
+
+*/
+
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+
+#include "comedi_pci.h"
+#include "8255.h"
+
+#undef PCI_DIO_EXTDEBUG /* if defined, enable extensive debug logging */
+
+#undef DPRINTK
+#ifdef PCI_DIO_EXTDEBUG
+#define DPRINTK(fmt, args...) rt_printk(fmt, ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+/* hardware types of the cards */
+enum hw_cards_id {
+ TYPE_PCI1730, TYPE_PCI1733, TYPE_PCI1734, TYPE_PCI1736,
+ TYPE_PCI1750,
+ TYPE_PCI1751,
+ TYPE_PCI1752,
+ TYPE_PCI1753, TYPE_PCI1753E,
+ TYPE_PCI1754, TYPE_PCI1756,
+ TYPE_PCI1760,
+ TYPE_PCI1762
+};
+
+/* which I/O instructions to use */
+enum hw_io_access {
+ IO_8b, IO_16b
+};
+
+#define MAX_DI_SUBDEVS 2 /* max number of DI subdevices per card */
+#define MAX_DO_SUBDEVS 2 /* max number of DO subdevices per card */
+#define MAX_DIO_SUBDEVG 2 /* max number of DIO subdevices group per card */
+
+#define SIZE_8255 4 /* 8255 IO space length */
+
+#define PCIDIO_MAINREG 2 /* main I/O region for all Advantech cards? */
+
+/* Register offset definitions */
+/* Advantech PCI-1730/3/4 */
+#define PCI1730_IDI 0 /* R: Isolated digital input 0-15 */
+#define PCI1730_IDO 0 /* W: Isolated digital output 0-15 */
+#define PCI1730_DI 2 /* R: Digital input 0-15 */
+#define PCI1730_DO 2 /* W: Digital output 0-15 */
+#define PCI1733_IDI 0 /* R: Isolated digital input 0-31 */
+#define PCI1730_3_INT_EN 0x08 /* R/W: enable/disable interrupts */
+#define PCI1730_3_INT_RF 0x0c /* R/W: set falling/raising edge for interrupts */
+#define PCI1730_3_INT_CLR 0x10 /* R/W: clear interrupts */
+#define PCI1734_IDO 0 /* W: Isolated digital output 0-31 */
+#define PCI173x_BOARDID 4 /* R: Board I/D switch for 1730/3/4 */
+
+/* Advantech PCI-1736UP */
+#define PCI1736_IDI 0 /* R: Isolated digital input 0-15 */
+#define PCI1736_IDO 0 /* W: Isolated digital output 0-15 */
+#define PCI1736_3_INT_EN 0x08 /* R/W: enable/disable interrupts */
+#define PCI1736_3_INT_RF 0x0c /* R/W: set falling/raising edge for interrupts */
+#define PCI1736_3_INT_CLR 0x10 /* R/W: clear interrupts */
+#define PCI1736_BOARDID 4 /* R: Board I/D switch for 1736UP */
+#define PCI1736_MAINREG 0 /* Normal register (2) doesn't work */
+
+/* Advantech PCI-1750 */
+#define PCI1750_IDI 0 /* R: Isolated digital input 0-15 */
+#define PCI1750_IDO 0 /* W: Isolated digital output 0-15 */
+#define PCI1750_ICR 32 /* W: Interrupt control register */
+#define PCI1750_ISR 32 /* R: Interrupt status register */
+
+/* Advantech PCI-1751/3/3E */
+#define PCI1751_DIO 0 /* R/W: begin of 8255 registers block */
+#define PCI1751_ICR 32 /* W: Interrupt control register */
+#define PCI1751_ISR 32 /* R: Interrupt status register */
+#define PCI1753_DIO 0 /* R/W: begin of 8255 registers block */
+#define PCI1753_ICR0 16 /* R/W: Interrupt control register group 0 */
+#define PCI1753_ICR1 17 /* R/W: Interrupt control register group 1 */
+#define PCI1753_ICR2 18 /* R/W: Interrupt control register group 2 */
+#define PCI1753_ICR3 19 /* R/W: Interrupt control register group 3 */
+#define PCI1753E_DIO 32 /* R/W: begin of 8255 registers block */
+#define PCI1753E_ICR0 48 /* R/W: Interrupt control register group 0 */
+#define PCI1753E_ICR1 49 /* R/W: Interrupt control register group 1 */
+#define PCI1753E_ICR2 50 /* R/W: Interrupt control register group 2 */
+#define PCI1753E_ICR3 51 /* R/W: Interrupt control register group 3 */
+
+/* Advantech PCI-1752/4/6 */
+#define PCI1752_IDO 0 /* R/W: Digital output 0-31 */
+#define PCI1752_IDO2 4 /* R/W: Digital output 32-63 */
+#define PCI1754_IDI 0 /* R: Digital input 0-31 */
+#define PCI1754_IDI2 4 /* R: Digital input 32-64 */
+#define PCI1756_IDI 0 /* R: Digital input 0-31 */
+#define PCI1756_IDO 4 /* R/W: Digital output 0-31 */
+#define PCI1754_6_ICR0 0x08 /* R/W: Interrupt control register group 0 */
+#define PCI1754_6_ICR1 0x0a /* R/W: Interrupt control register group 1 */
+#define PCI1754_ICR2 0x0c /* R/W: Interrupt control register group 2 */
+#define PCI1754_ICR3 0x0e /* R/W: Interrupt control register group 3 */
+#define PCI1752_6_CFC 0x12 /* R/W: set/read channel freeze function */
+#define PCI175x_BOARDID 0x10 /* R: Board I/D switch for 1752/4/6 */
+
+/* Advantech PCI-1762 registers */
+#define PCI1762_RO 0 /* R/W: Relays status/output */
+#define PCI1762_IDI 2 /* R: Isolated input status */
+#define PCI1762_BOARDID 4 /* R: Board I/D switch */
+#define PCI1762_ICR 6 /* W: Interrupt control register */
+#define PCI1762_ISR 6 /* R: Interrupt status register */
+
+/* Advantech PCI-1760 registers */
+#define OMB0 0x0c /* W: Mailbox outgoing registers */
+#define OMB1 0x0d
+#define OMB2 0x0e
+#define OMB3 0x0f
+#define IMB0 0x1c /* R: Mailbox incoming registers */
+#define IMB1 0x1d
+#define IMB2 0x1e
+#define IMB3 0x1f
+#define INTCSR0 0x38 /* R/W: Interrupt control registers */
+#define INTCSR1 0x39
+#define INTCSR2 0x3a
+#define INTCSR3 0x3b
+
+/* PCI-1760 mailbox commands */
+#define CMD_ClearIMB2 0x00 /* Clear IMB2 status and return actaul DI status in IMB3 */
+#define CMD_SetRelaysOutput 0x01 /* Set relay output from OMB0 */
+#define CMD_GetRelaysStatus 0x02 /* Get relay status to IMB0 */
+#define CMD_ReadCurrentStatus 0x07 /* Read the current status of the register in OMB0, result in IMB0 */
+#define CMD_ReadFirmwareVersion 0x0e /* Read the firmware ver., result in IMB1.IMB0 */
+#define CMD_ReadHardwareVersion 0x0f /* Read the hardware ver., result in IMB1.IMB0 */
+#define CMD_EnableIDIFilters 0x20 /* Enable IDI filters based on bits in OMB0 */
+#define CMD_EnableIDIPatternMatch 0x21 /* Enable IDI pattern match based on bits in OMB0 */
+#define CMD_SetIDIPatternMatch 0x22 /* Enable IDI pattern match based on bits in OMB0 */
+#define CMD_EnableIDICounters 0x28 /* Enable IDI counters based on bits in OMB0 */
+#define CMD_ResetIDICounters 0x29 /* Reset IDI counters based on bits in OMB0 to its reset values */
+#define CMD_OverflowIDICounters 0x2a /* Enable IDI counters overflow interrupts based on bits in OMB0 */
+#define CMD_MatchIntIDICounters 0x2b /* Enable IDI counters match value interrupts based on bits in OMB0 */
+#define CMD_EdgeIDICounters 0x2c /* Set IDI up counters count edge (bit=0 - rising, =1 - falling) */
+#define CMD_GetIDICntCurValue 0x2f /* Read IDI{OMB0} up counter current value */
+#define CMD_SetIDI0CntResetValue 0x40 /* Set IDI0 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI1CntResetValue 0x41 /* Set IDI1 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI2CntResetValue 0x42 /* Set IDI2 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI3CntResetValue 0x43 /* Set IDI3 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI4CntResetValue 0x44 /* Set IDI4 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI5CntResetValue 0x45 /* Set IDI5 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI6CntResetValue 0x46 /* Set IDI6 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI7CntResetValue 0x47 /* Set IDI7 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI0CntMatchValue 0x48 /* Set IDI0 Counter Match Value 256*OMB1+OMB0 */
+#define CMD_SetIDI1CntMatchValue 0x49 /* Set IDI1 Counter Match Value 256*OMB1+OMB0 */
+#define CMD_SetIDI2CntMatchValue 0x4a /* Set IDI2 Counter Match Value 256*OMB1+OMB0 */
+#define CMD_SetIDI3CntMatchValue 0x4b /* Set IDI3 Counter Match Value 256*OMB1+OMB0 */
+#define CMD_SetIDI4CntMatchValue 0x4c /* Set IDI4 Counter Match Value 256*OMB1+OMB0 */
+#define CMD_SetIDI5CntMatchValue 0x4d /* Set IDI5 Counter Match Value 256*OMB1+OMB0 */
+#define CMD_SetIDI6CntMatchValue 0x4e /* Set IDI6 Counter Match Value 256*OMB1+OMB0 */
+#define CMD_SetIDI7CntMatchValue 0x4f /* Set IDI7 Counter Match Value 256*OMB1+OMB0 */
+
+#define OMBCMD_RETRY 0x03 /* 3 times try request before error */
+
+static int pci_dio_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pci_dio_detach(struct comedi_device * dev);
+
+struct diosubd_data {
+ int chans; /* num of chans */
+ int addr; /* PCI address ofset */
+ int regs; /* number of registers to read or 8255 subdevices */
+ unsigned int specflags; /* addon subdevice flags */
+};
+
+struct dio_boardtype {
+ const char *name; /* board name */
+ int vendor_id; /* vendor/device PCI ID */
+ int device_id;
+ int main_pci_region; /* main I/O PCI region */
+ enum hw_cards_id cardtype;
+ struct diosubd_data sdi[MAX_DI_SUBDEVS]; /* DI chans */
+ struct diosubd_data sdo[MAX_DO_SUBDEVS]; /* DO chans */
+ struct diosubd_data sdio[MAX_DIO_SUBDEVG]; /* DIO 8255 chans */
+ struct diosubd_data boardid; /* card supports board ID switch */
+ enum hw_io_access io_access;
+};
+
+static DEFINE_PCI_DEVICE_TABLE(pci_dio_pci_table) = {
+ {PCI_VENDOR_ID_ADVANTECH, 0x1730, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ADVANTECH, 0x1733, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ADVANTECH, 0x1734, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ADVANTECH, 0x1736, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ADVANTECH, 0x1750, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ADVANTECH, 0x1751, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ADVANTECH, 0x1752, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ADVANTECH, 0x1753, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ADVANTECH, 0x1754, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ADVANTECH, 0x1756, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ADVANTECH, 0x1760, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ADVANTECH, 0x1762, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, pci_dio_pci_table);
+
+static const struct dio_boardtype boardtypes[] = {
+ {"pci1730", PCI_VENDOR_ID_ADVANTECH, 0x1730, PCIDIO_MAINREG,
+ TYPE_PCI1730,
+ {{16, PCI1730_DI, 2, 0}, {16, PCI1730_IDI, 2, 0}},
+ {{16, PCI1730_DO, 2, 0}, {16, PCI1730_IDO, 2, 0}},
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {4, PCI173x_BOARDID, 1, SDF_INTERNAL},
+ IO_8b,
+ },
+ {"pci1733", PCI_VENDOR_ID_ADVANTECH, 0x1733, PCIDIO_MAINREG,
+ TYPE_PCI1733,
+ {{0, 0, 0, 0}, {32, PCI1733_IDI, 4, 0}},
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {4, PCI173x_BOARDID, 1, SDF_INTERNAL},
+ IO_8b},
+ {"pci1734", PCI_VENDOR_ID_ADVANTECH, 0x1734, PCIDIO_MAINREG,
+ TYPE_PCI1734,
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {{0, 0, 0, 0}, {32, PCI1734_IDO, 4, 0}},
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {4, PCI173x_BOARDID, 1, SDF_INTERNAL},
+ IO_8b},
+ {"pci1736", PCI_VENDOR_ID_ADVANTECH, 0x1736, PCI1736_MAINREG,
+ TYPE_PCI1736,
+ {{0, 0, 0, 0}, {16, PCI1736_IDI, 2, 0}},
+ {{0, 0, 0, 0}, {16, PCI1736_IDO, 2, 0}},
+ {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+ { 4, PCI1736_BOARDID, 1, SDF_INTERNAL},
+ IO_8b,
+ },
+ {"pci1750", PCI_VENDOR_ID_ADVANTECH, 0x1750, PCIDIO_MAINREG,
+ TYPE_PCI1750,
+ {{0, 0, 0, 0}, {16, PCI1750_IDI, 2, 0}},
+ {{0, 0, 0, 0}, {16, PCI1750_IDO, 2, 0}},
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {0, 0, 0, 0},
+ IO_8b},
+ {"pci1751", PCI_VENDOR_ID_ADVANTECH, 0x1751, PCIDIO_MAINREG,
+ TYPE_PCI1751,
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {{48, PCI1751_DIO, 2, 0}, {0, 0, 0, 0}},
+ {0, 0, 0, 0},
+ IO_8b},
+ {"pci1752", PCI_VENDOR_ID_ADVANTECH, 0x1752, PCIDIO_MAINREG,
+ TYPE_PCI1752,
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {{32, PCI1752_IDO, 2, 0}, {32, PCI1752_IDO2, 2, 0}},
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {4, PCI175x_BOARDID, 1, SDF_INTERNAL},
+ IO_16b},
+ {"pci1753", PCI_VENDOR_ID_ADVANTECH, 0x1753, PCIDIO_MAINREG,
+ TYPE_PCI1753,
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {{96, PCI1753_DIO, 4, 0}, {0, 0, 0, 0}},
+ {0, 0, 0, 0},
+ IO_8b},
+ {"pci1753e", PCI_VENDOR_ID_ADVANTECH, 0x1753, PCIDIO_MAINREG,
+ TYPE_PCI1753E,
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {{96, PCI1753_DIO, 4, 0}, {96, PCI1753E_DIO, 4, 0}},
+ {0, 0, 0, 0},
+ IO_8b},
+ {"pci1754", PCI_VENDOR_ID_ADVANTECH, 0x1754, PCIDIO_MAINREG,
+ TYPE_PCI1754,
+ {{32, PCI1754_IDI, 2, 0}, {32, PCI1754_IDI2, 2, 0}},
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {4, PCI175x_BOARDID, 1, SDF_INTERNAL},
+ IO_16b},
+ {"pci1756", PCI_VENDOR_ID_ADVANTECH, 0x1756, PCIDIO_MAINREG,
+ TYPE_PCI1756,
+ {{0, 0, 0, 0}, {32, PCI1756_IDI, 2, 0}},
+ {{0, 0, 0, 0}, {32, PCI1756_IDO, 2, 0}},
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {4, PCI175x_BOARDID, 1, SDF_INTERNAL},
+ IO_16b},
+ {"pci1760", PCI_VENDOR_ID_ADVANTECH, 0x1760, 0,
+ TYPE_PCI1760,
+ {{0, 0, 0, 0}, {0, 0, 0, 0}}, /* This card have own setup work */
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {0, 0, 0, 0},
+ IO_8b},
+ {"pci1762", PCI_VENDOR_ID_ADVANTECH, 0x1762, PCIDIO_MAINREG,
+ TYPE_PCI1762,
+ {{0, 0, 0, 0}, {16, PCI1762_IDI, 1, 0}},
+ {{0, 0, 0, 0}, {16, PCI1762_RO, 1, 0}},
+ {{0, 0, 0, 0}, {0, 0, 0, 0}},
+ {4, PCI1762_BOARDID, 1, SDF_INTERNAL},
+ IO_16b}
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(struct dio_boardtype))
+
+static struct comedi_driver driver_pci_dio = {
+ driver_name:"adv_pci_dio",
+ module:THIS_MODULE,
+ attach:pci_dio_attach,
+ detach:pci_dio_detach
+};
+
+struct pci_dio_private {
+ struct pci_dio_private *prev; /* previous private struct */
+ struct pci_dio_private *next; /* next private struct */
+ struct pci_dev *pcidev; /* pointer to board's pci_dev */
+ char valid; /* card is usable */
+ char GlobalIrqEnabled; /* 1= any IRQ source is enabled */
+ /* PCI-1760 specific data */
+ unsigned char IDICntEnable; /* counter's counting enable status */
+ unsigned char IDICntOverEnable; /* counter's overflow interrupts enable status */
+ unsigned char IDICntMatchEnable; /* counter's match interrupts enable status */
+ unsigned char IDICntEdge; /* counter's count edge value (bit=0 - rising, =1 - falling) */
+ unsigned short CntResValue[8]; /* counters' reset value */
+ unsigned short CntMatchValue[8]; /* counters' match interrupt value */
+ unsigned char IDIFiltersEn; /* IDI's digital filters enable status */
+ unsigned char IDIPatMatchEn; /* IDI's pattern match enable status */
+ unsigned char IDIPatMatchValue; /* IDI's pattern match value */
+ unsigned short IDIFiltrLow[8]; /* IDI's filter value low signal */
+ unsigned short IDIFiltrHigh[8]; /* IDI's filter value high signal */
+};
+
+static struct pci_dio_private *pci_priv = NULL; /* list of allocated cards */
+
+#define devpriv ((struct pci_dio_private *)dev->private)
+#define this_board ((const struct dio_boardtype *)dev->board_ptr)
+
+/*
+==============================================================================
+*/
+static int pci_dio_insn_bits_di_b(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ const struct diosubd_data *d = (const struct diosubd_data *)s->private;
+ int i;
+
+ data[1] = 0;
+ for (i = 0; i < d->regs; i++) {
+ data[1] |= inb(dev->iobase + d->addr + i) << (8 * i);
+ }
+
+ return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci_dio_insn_bits_di_w(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ const struct diosubd_data *d = (const struct diosubd_data *)s->private;
+ int i;
+
+ data[1] = 0;
+ for (i = 0; i < d->regs; i++)
+ data[1] |= inw(dev->iobase + d->addr + 2 * i) << (16 * i);
+
+ return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci_dio_insn_bits_do_b(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ const struct diosubd_data *d = (const struct diosubd_data *)s->private;
+ int i;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ for (i = 0; i < d->regs; i++)
+ outb((s->state >> (8 * i)) & 0xff,
+ dev->iobase + d->addr + i);
+ }
+ data[1] = s->state;
+
+ return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci_dio_insn_bits_do_w(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ const struct diosubd_data *d = (const struct diosubd_data *)s->private;
+ int i;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ for (i = 0; i < d->regs; i++)
+ outw((s->state >> (16 * i)) & 0xffff,
+ dev->iobase + d->addr + 2 * i);
+ }
+ data[1] = s->state;
+
+ return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_unchecked_mbxrequest(struct comedi_device * dev,
+ unsigned char *omb, unsigned char *imb, int repeats)
+{
+ int cnt, tout, ok = 0;
+
+ for (cnt = 0; cnt < repeats; cnt++) {
+ outb(omb[0], dev->iobase + OMB0);
+ outb(omb[1], dev->iobase + OMB1);
+ outb(omb[2], dev->iobase + OMB2);
+ outb(omb[3], dev->iobase + OMB3);
+ for (tout = 0; tout < 251; tout++) {
+ if ((imb[2] = inb(dev->iobase + IMB2)) == omb[2]) {
+ imb[0] = inb(dev->iobase + IMB0);
+ imb[1] = inb(dev->iobase + IMB1);
+ imb[3] = inb(dev->iobase + IMB3);
+ ok = 1;
+ break;
+ }
+ comedi_udelay(1);
+ }
+ if (ok)
+ return 0;
+ }
+
+ comedi_error(dev, "PCI-1760 mailbox request timeout!");
+ return -ETIME;
+}
+
+static int pci1760_clear_imb2(struct comedi_device * dev)
+{
+ unsigned char omb[4] = { 0x0, 0x0, CMD_ClearIMB2, 0x0 };
+ unsigned char imb[4];
+ /* check if imb2 is already clear */
+ if (inb(dev->iobase + IMB2) == CMD_ClearIMB2)
+ return 0;
+ return pci1760_unchecked_mbxrequest(dev, omb, imb, OMBCMD_RETRY);
+}
+
+static int pci1760_mbxrequest(struct comedi_device * dev,
+ unsigned char *omb, unsigned char *imb)
+{
+ if (omb[2] == CMD_ClearIMB2) {
+ comedi_error(dev,
+ "bug! this function should not be used for CMD_ClearIMB2 command");
+ return -EINVAL;
+ }
+ if (inb(dev->iobase + IMB2) == omb[2]) {
+ int retval;
+ retval = pci1760_clear_imb2(dev);
+ if (retval < 0)
+ return retval;
+ }
+ return pci1760_unchecked_mbxrequest(dev, omb, imb, OMBCMD_RETRY);
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_insn_bits_di(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[1] = inb(dev->iobase + IMB3);
+
+ return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_insn_bits_do(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int ret;
+ unsigned char omb[4] = {
+ 0x00,
+ 0x00,
+ CMD_SetRelaysOutput,
+ 0x00
+ };
+ unsigned char imb[4];
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ omb[0] = s->state;
+ if (!(ret = pci1760_mbxrequest(dev, omb, imb)))
+ return ret;
+ }
+ data[1] = s->state;
+
+ return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_insn_cnt_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int ret, n;
+ unsigned char omb[4] = {
+ CR_CHAN(insn->chanspec) & 0x07,
+ 0x00,
+ CMD_GetIDICntCurValue,
+ 0x00
+ };
+ unsigned char imb[4];
+
+ for (n = 0; n < insn->n; n++) {
+ if (!(ret = pci1760_mbxrequest(dev, omb, imb)))
+ return ret;
+ data[n] = (imb[1] << 8) + imb[0];
+ }
+
+ return n;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_insn_cnt_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int ret;
+ unsigned char chan = CR_CHAN(insn->chanspec) & 0x07;
+ unsigned char bitmask = 1 << chan;
+ unsigned char omb[4] = {
+ data[0] & 0xff,
+ (data[0] >> 8) & 0xff,
+ CMD_SetIDI0CntResetValue + chan,
+ 0x00
+ };
+ unsigned char imb[4];
+
+ if (devpriv->CntResValue[chan] != (data[0] & 0xffff)) { /* Set reset value if different */
+ if (!(ret = pci1760_mbxrequest(dev, omb, imb)))
+ return ret;
+ devpriv->CntResValue[chan] = data[0] & 0xffff;
+ }
+
+ omb[0] = bitmask; /* reset counter to it reset value */
+ omb[2] = CMD_ResetIDICounters;
+ if (!(ret = pci1760_mbxrequest(dev, omb, imb)))
+ return ret;
+
+ if (!(bitmask & devpriv->IDICntEnable)) { /* start counter if it don't run */
+ omb[0] = bitmask;
+ omb[2] = CMD_EnableIDICounters;
+ if (!(ret = pci1760_mbxrequest(dev, omb, imb)))
+ return ret;
+ devpriv->IDICntEnable |= bitmask;
+ }
+ return 1;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_reset(struct comedi_device * dev)
+{
+ int i;
+ unsigned char omb[4] = { 0x00, 0x00, 0x00, 0x00 };
+ unsigned char imb[4];
+
+ outb(0, dev->iobase + INTCSR0); /* disable IRQ */
+ outb(0, dev->iobase + INTCSR1);
+ outb(0, dev->iobase + INTCSR2);
+ outb(0, dev->iobase + INTCSR3);
+ devpriv->GlobalIrqEnabled = 0;
+
+ omb[0] = 0x00;
+ omb[2] = CMD_SetRelaysOutput; /* reset relay outputs */
+ pci1760_mbxrequest(dev, omb, imb);
+
+ omb[0] = 0x00;
+ omb[2] = CMD_EnableIDICounters; /* disable IDI up counters */
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->IDICntEnable = 0;
+
+ omb[0] = 0x00;
+ omb[2] = CMD_OverflowIDICounters; /* disable counters overflow interrupts */
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->IDICntOverEnable = 0;
+
+ omb[0] = 0x00;
+ omb[2] = CMD_MatchIntIDICounters; /* disable counters match value interrupts */
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->IDICntMatchEnable = 0;
+
+ omb[0] = 0x00;
+ omb[1] = 0x80;
+ for (i = 0; i < 8; i++) { /* set IDI up counters match value */
+ omb[2] = CMD_SetIDI0CntMatchValue + i;
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->CntMatchValue[i] = 0x8000;
+ }
+
+ omb[0] = 0x00;
+ omb[1] = 0x00;
+ for (i = 0; i < 8; i++) { /* set IDI up counters reset value */
+ omb[2] = CMD_SetIDI0CntResetValue + i;
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->CntResValue[i] = 0x0000;
+ }
+
+ omb[0] = 0xff;
+ omb[2] = CMD_ResetIDICounters; /* reset IDI up counters to reset values */
+ pci1760_mbxrequest(dev, omb, imb);
+
+ omb[0] = 0x00;
+ omb[2] = CMD_EdgeIDICounters; /* set IDI up counters count edge */
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->IDICntEdge = 0x00;
+
+ omb[0] = 0x00;
+ omb[2] = CMD_EnableIDIFilters; /* disable all digital in filters */
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->IDIFiltersEn = 0x00;
+
+ omb[0] = 0x00;
+ omb[2] = CMD_EnableIDIPatternMatch; /* disable pattern matching */
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->IDIPatMatchEn = 0x00;
+
+ omb[0] = 0x00;
+ omb[2] = CMD_SetIDIPatternMatch; /* set pattern match value */
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->IDIPatMatchValue = 0x00;
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci_dio_reset(struct comedi_device * dev)
+{
+ DPRINTK("adv_pci_dio EDBG: BGN: pci171x_reset(...)\n");
+
+ switch (this_board->cardtype) {
+ case TYPE_PCI1730:
+ outb(0, dev->iobase + PCI1730_DO); /* clear outputs */
+ outb(0, dev->iobase + PCI1730_DO + 1);
+ outb(0, dev->iobase + PCI1730_IDO);
+ outb(0, dev->iobase + PCI1730_IDO + 1);
+ /* NO break there! */
+ case TYPE_PCI1733:
+ outb(0, dev->iobase + PCI1730_3_INT_EN); /* disable interrupts */
+ outb(0x0f, dev->iobase + PCI1730_3_INT_CLR); /* clear interrupts */
+ outb(0, dev->iobase + PCI1730_3_INT_RF); /* set rising edge trigger */
+ break;
+ case TYPE_PCI1734:
+ outb(0, dev->iobase + PCI1734_IDO); /* clear outputs */
+ outb(0, dev->iobase + PCI1734_IDO + 1);
+ outb(0, dev->iobase + PCI1734_IDO + 2);
+ outb(0, dev->iobase + PCI1734_IDO + 3);
+ break;
+
+ case TYPE_PCI1736:
+ outb(0, dev->iobase+PCI1736_IDO);
+ outb(0, dev->iobase+PCI1736_IDO+1);
+ outb(0, dev->iobase+PCI1736_3_INT_EN); /* disable interrupts */
+ outb(0x0f, dev->iobase+PCI1736_3_INT_CLR);/* clear interrupts */
+ outb(0, dev->iobase+PCI1736_3_INT_RF); /* set rising edge trigger */
+ break;
+
+ case TYPE_PCI1750:
+ case TYPE_PCI1751:
+ outb(0x88, dev->iobase + PCI1750_ICR); /* disable & clear interrupts */
+ break;
+ case TYPE_PCI1752:
+ outw(0, dev->iobase + PCI1752_6_CFC); /* disable channel freeze function */
+ outw(0, dev->iobase + PCI1752_IDO); /* clear outputs */
+ outw(0, dev->iobase + PCI1752_IDO + 2);
+ outw(0, dev->iobase + PCI1752_IDO2);
+ outw(0, dev->iobase + PCI1752_IDO2 + 2);
+ break;
+ case TYPE_PCI1753E:
+ outb(0x88, dev->iobase + PCI1753E_ICR0); /* disable & clear interrupts */
+ outb(0x80, dev->iobase + PCI1753E_ICR1);
+ outb(0x80, dev->iobase + PCI1753E_ICR2);
+ outb(0x80, dev->iobase + PCI1753E_ICR3);
+ /* NO break there! */
+ case TYPE_PCI1753:
+ outb(0x88, dev->iobase + PCI1753_ICR0); /* disable & clear interrupts */
+ outb(0x80, dev->iobase + PCI1753_ICR1);
+ outb(0x80, dev->iobase + PCI1753_ICR2);
+ outb(0x80, dev->iobase + PCI1753_ICR3);
+ break;
+ case TYPE_PCI1754:
+ outw(0x08, dev->iobase + PCI1754_6_ICR0); /* disable and clear interrupts */
+ outw(0x08, dev->iobase + PCI1754_6_ICR1);
+ outw(0x08, dev->iobase + PCI1754_ICR2);
+ outw(0x08, dev->iobase + PCI1754_ICR3);
+ break;
+ case TYPE_PCI1756:
+ outw(0, dev->iobase + PCI1752_6_CFC); /* disable channel freeze function */
+ outw(0x08, dev->iobase + PCI1754_6_ICR0); /* disable and clear interrupts */
+ outw(0x08, dev->iobase + PCI1754_6_ICR1);
+ outw(0, dev->iobase + PCI1756_IDO); /* clear outputs */
+ outw(0, dev->iobase + PCI1756_IDO + 2);
+ break;
+ case TYPE_PCI1760:
+ pci1760_reset(dev);
+ break;
+ case TYPE_PCI1762:
+ outw(0x0101, dev->iobase + PCI1762_ICR); /* disable & clear interrupts */
+ break;
+ }
+
+ DPRINTK("adv_pci_dio EDBG: END: pci171x_reset(...)\n");
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int subdev = 0;
+
+ s = dev->subdevices + subdev;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->len_chanlist = 8;
+ s->range_table = &range_digital;
+ s->insn_bits = pci1760_insn_bits_di;
+ subdev++;
+
+ s = dev->subdevices + subdev;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->len_chanlist = 8;
+ s->range_table = &range_digital;
+ s->state = 0;
+ s->insn_bits = pci1760_insn_bits_do;
+ subdev++;
+
+ s = dev->subdevices + subdev;
+ s->type = COMEDI_SUBD_TIMER;
+ s->subdev_flags = SDF_WRITABLE | SDF_LSAMPL;
+ s->n_chan = 2;
+ s->maxdata = 0xffffffff;
+ s->len_chanlist = 2;
+/* s->insn_config=pci1760_insn_pwm_cfg; */
+ subdev++;
+
+ s = dev->subdevices + subdev;
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 0xffff;
+ s->len_chanlist = 8;
+ s->insn_read = pci1760_insn_cnt_read;
+ s->insn_write = pci1760_insn_cnt_write;
+/* s->insn_config=pci1760_insn_cnt_cfg; */
+ subdev++;
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci_dio_add_di(struct comedi_device * dev, struct comedi_subdevice * s,
+ const struct diosubd_data * d, int subdev)
+{
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON | d->specflags;
+ if (d->chans > 16)
+ s->subdev_flags |= SDF_LSAMPL;
+ s->n_chan = d->chans;
+ s->maxdata = 1;
+ s->len_chanlist = d->chans;
+ s->range_table = &range_digital;
+ switch (this_board->io_access) {
+ case IO_8b:
+ s->insn_bits = pci_dio_insn_bits_di_b;
+ break;
+ case IO_16b:
+ s->insn_bits = pci_dio_insn_bits_di_w;
+ break;
+ }
+ s->private = (void *)d;
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci_dio_add_do(struct comedi_device * dev, struct comedi_subdevice * s,
+ const struct diosubd_data * d, int subdev)
+{
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ if (d->chans > 16)
+ s->subdev_flags |= SDF_LSAMPL;
+ s->n_chan = d->chans;
+ s->maxdata = 1;
+ s->len_chanlist = d->chans;
+ s->range_table = &range_digital;
+ s->state = 0;
+ switch (this_board->io_access) {
+ case IO_8b:
+ s->insn_bits = pci_dio_insn_bits_do_b;
+ break;
+ case IO_16b:
+ s->insn_bits = pci_dio_insn_bits_do_w;
+ break;
+ }
+ s->private = (void *)d;
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int CheckAndAllocCard(struct comedi_device * dev, struct comedi_devconfig * it,
+ struct pci_dev *pcidev)
+{
+ struct pci_dio_private *pr, *prev;
+
+ for (pr = pci_priv, prev = NULL; pr != NULL; prev = pr, pr = pr->next) {
+ if (pr->pcidev == pcidev) {
+ return 0; /* this card is used, look for another */
+ }
+ }
+
+ if (prev) {
+ devpriv->prev = prev;
+ prev->next = devpriv;
+ } else {
+ pci_priv = devpriv;
+ }
+
+ devpriv->pcidev = pcidev;
+
+ return 1;
+}
+
+/*
+==============================================================================
+*/
+static int pci_dio_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int ret, subdev, n_subdevices, i, j;
+ unsigned long iobase;
+ struct pci_dev *pcidev;
+
+ rt_printk("comedi%d: adv_pci_dio: ", dev->minor);
+
+ if ((ret = alloc_private(dev, sizeof(struct pci_dio_private))) < 0) {
+ rt_printk(", Error: Cann't allocate private memory!\n");
+ return -ENOMEM;
+ }
+
+ for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pcidev != NULL;
+ pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
+ /* loop through cards supported by this driver */
+ for (i = 0; i < n_boardtypes; ++i) {
+ if (boardtypes[i].vendor_id != pcidev->vendor)
+ continue;
+ if (boardtypes[i].device_id != pcidev->device)
+ continue;
+ /* was a particular bus/slot requested? */
+ if (it->options[0] || it->options[1]) {
+ /* are we on the wrong bus/slot? */
+ if (pcidev->bus->number != it->options[0] ||
+ PCI_SLOT(pcidev->devfn) !=
+ it->options[1]) {
+ continue;
+ }
+ }
+ ret = CheckAndAllocCard(dev, it, pcidev);
+ if (ret != 1) continue;
+ dev->board_ptr = boardtypes + i;
+ break;
+ }
+ if (dev->board_ptr)
+ break;
+ }
+
+ if (!dev->board_ptr) {
+ rt_printk
+ (", Error: Requested type of the card was not found!\n");
+ return -EIO;
+ }
+
+ if (comedi_pci_enable(pcidev, driver_pci_dio.driver_name)) {
+ rt_printk
+ (", Error: Can't enable PCI device and request regions!\n");
+ return -EIO;
+ }
+ iobase = pci_resource_start(pcidev, this_board->main_pci_region);
+ rt_printk(", b:s:f=%d:%d:%d, io=0x%4lx",
+ pcidev->bus->number, PCI_SLOT(pcidev->devfn),
+ PCI_FUNC(pcidev->devfn), iobase);
+
+ dev->iobase = iobase;
+ dev->board_name = this_board->name;
+
+ if (this_board->cardtype == TYPE_PCI1760) {
+ n_subdevices = 4; /* 8 IDI, 8 IDO, 2 PWM, 8 CNT */
+ } else {
+ n_subdevices = 0;
+ for (i = 0; i < MAX_DI_SUBDEVS; i++)
+ if (this_board->sdi[i].chans)
+ n_subdevices++;
+ for (i = 0; i < MAX_DO_SUBDEVS; i++)
+ if (this_board->sdo[i].chans)
+ n_subdevices++;
+ for (i = 0; i < MAX_DIO_SUBDEVG; i++)
+ n_subdevices += this_board->sdio[i].regs;
+ if (this_board->boardid.chans)
+ n_subdevices++;
+ }
+
+ if ((ret = alloc_subdevices(dev, n_subdevices)) < 0) {
+ rt_printk(", Error: Cann't allocate subdevice memory!\n");
+ return ret;
+ }
+
+ rt_printk(".\n");
+
+ subdev = 0;
+
+ for (i = 0; i < MAX_DI_SUBDEVS; i++)
+ if (this_board->sdi[i].chans) {
+ s = dev->subdevices + subdev;
+ pci_dio_add_di(dev, s, &this_board->sdi[i], subdev);
+ subdev++;
+ }
+
+ for (i = 0; i < MAX_DO_SUBDEVS; i++)
+ if (this_board->sdo[i].chans) {
+ s = dev->subdevices + subdev;
+ pci_dio_add_do(dev, s, &this_board->sdo[i], subdev);
+ subdev++;
+ }
+
+ for (i = 0; i < MAX_DIO_SUBDEVG; i++)
+ for (j = 0; j < this_board->sdio[i].regs; j++) {
+ s = dev->subdevices + subdev;
+ subdev_8255_init(dev, s, NULL,
+ dev->iobase + this_board->sdio[i].addr +
+ SIZE_8255 * j);
+ subdev++;
+ }
+
+ if (this_board->boardid.chans) {
+ s = dev->subdevices + subdev;
+ s->type = COMEDI_SUBD_DI;
+ pci_dio_add_di(dev, s, &this_board->boardid, subdev);
+ subdev++;
+ }
+
+ if (this_board->cardtype == TYPE_PCI1760)
+ pci1760_attach(dev, it);
+
+ devpriv->valid = 1;
+
+ pci_dio_reset(dev);
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci_dio_detach(struct comedi_device * dev)
+{
+ int i, j;
+ struct comedi_subdevice *s;
+ int subdev;
+
+ if (dev->private) {
+ if (devpriv->valid) {
+ pci_dio_reset(dev);
+ }
+
+ /* This shows the silliness of using this kind of
+ * scheme for numbering subdevices. Don't do it. --ds */
+ subdev = 0;
+ for (i = 0; i < MAX_DI_SUBDEVS; i++) {
+ if (this_board->sdi[i].chans) {
+ subdev++;
+ }
+ }
+ for (i = 0; i < MAX_DO_SUBDEVS; i++) {
+ if (this_board->sdo[i].chans) {
+ subdev++;
+ }
+ }
+ for (i = 0; i < MAX_DIO_SUBDEVG; i++) {
+ for (j = 0; j < this_board->sdio[i].regs; j++) {
+ s = dev->subdevices + subdev;
+ subdev_8255_cleanup(dev, s);
+ subdev++;
+ }
+ }
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = dev->subdevices + i;
+ s->private = NULL;
+ }
+
+ if (devpriv->pcidev) {
+ if (dev->iobase) {
+ comedi_pci_disable(devpriv->pcidev);
+ }
+ pci_dev_put(devpriv->pcidev);
+ }
+
+ if (devpriv->prev) {
+ devpriv->prev->next = devpriv->next;
+ } else {
+ pci_priv = devpriv->next;
+ }
+ if (devpriv->next) {
+ devpriv->next->prev = devpriv->prev;
+ }
+ }
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+COMEDI_PCI_INITCLEANUP(driver_pci_dio, pci_dio_pci_table);
+/*
+==============================================================================
+*/
diff --git a/drivers/staging/comedi/drivers/aio_aio12_8.c b/drivers/staging/comedi/drivers/aio_aio12_8.c
new file mode 100644
index 000000000000..b97b4d82611b
--- /dev/null
+++ b/drivers/staging/comedi/drivers/aio_aio12_8.c
@@ -0,0 +1,226 @@
+/*
+
+ comedi/drivers/aio_aio12_8.c
+
+ Driver for Acces I/O Products PC-104 AIO12-8 Analog I/O Board
+ Copyright (C) 2006 C&C Technologies, Inc.
+
+ 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.
+*/
+
+/*
+
+Driver: aio_aio12_8
+Description: Acces I/O Products PC-104 AIO12-8 Analog I/O Board
+Author: Pablo Mejia <pablo.mejia@cctechnol.com>
+Devices:
+ [Acces I/O] PC-104 AIO12-8
+Status: experimental
+
+Configuration Options:
+ [0] - I/O port base address
+
+Notes:
+
+ Only synchronous operations are supported.
+
+*/
+
+#include "../comedidev.h"
+#include <linux/ioport.h>
+#include "8255.h"
+
+#define AIO12_8_STATUS 0x00
+#define AIO12_8_INTERRUPT 0x01
+#define AIO12_8_ADC 0x02
+#define AIO12_8_DAC_0 0x04
+#define AIO12_8_DAC_1 0x06
+#define AIO12_8_DAC_2 0x08
+#define AIO12_8_DAC_3 0x0A
+#define AIO12_8_COUNTER_0 0x0C
+#define AIO12_8_COUNTER_1 0x0D
+#define AIO12_8_COUNTER_2 0x0E
+#define AIO12_8_COUNTER_CONTROL 0x0F
+#define AIO12_8_DIO_0 0x10
+#define AIO12_8_DIO_1 0x11
+#define AIO12_8_DIO_2 0x12
+#define AIO12_8_DIO_STATUS 0x13
+#define AIO12_8_DIO_CONTROL 0x14
+#define AIO12_8_ADC_TRIGGER_CONTROL 0x15
+#define AIO12_8_TRIGGER 0x16
+#define AIO12_8_POWER 0x17
+
+#define STATUS_ADC_EOC 0x80
+
+#define ADC_MODE_NORMAL 0x00
+#define ADC_MODE_INTERNAL_CLOCK 0x40
+#define ADC_MODE_STANDBY 0x80
+#define ADC_MODE_POWERDOWN 0xC0
+
+#define DAC_ENABLE 0x18
+
+struct aio12_8_boardtype {
+ const char *name;
+};
+
+static const struct aio12_8_boardtype board_types[] = {
+ {
+ name: "aio_aio12_8"},
+};
+
+#define thisboard ((const struct aio12_8_boardtype *) dev->board_ptr)
+
+struct aio12_8_private {
+ unsigned int ao_readback[4];
+};
+
+#define devpriv ((struct aio12_8_private *) dev->private)
+
+static int aio_aio12_8_ai_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ unsigned char control =
+ ADC_MODE_NORMAL |
+ (CR_RANGE(insn->chanspec) << 3) | CR_CHAN(insn->chanspec);
+
+ //read status to clear EOC latch
+ inb(dev->iobase + AIO12_8_STATUS);
+
+ for (n = 0; n < insn->n; n++) {
+ int timeout = 5;
+
+ // Setup and start conversion
+ outb(control, dev->iobase + AIO12_8_ADC);
+
+ // Wait for conversion to complete
+ while (timeout &&
+ !(inb(dev->iobase + AIO12_8_STATUS) & STATUS_ADC_EOC)) {
+ timeout--;
+ printk("timeout %d\n", timeout);
+ comedi_udelay(1);
+ }
+ if (timeout == 0) {
+ comedi_error(dev, "ADC timeout");
+ return -EIO;
+ }
+
+ data[n] = inw(dev->iobase + AIO12_8_ADC) & 0x0FFF;
+ }
+ return n;
+}
+
+static int aio_aio12_8_ao_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int val = devpriv->ao_readback[CR_CHAN(insn->chanspec)];
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = val;
+ return insn->n;
+}
+
+static int aio_aio12_8_ao_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+ unsigned long port = dev->iobase + AIO12_8_DAC_0 + (2 * chan);
+
+ //enable DACs
+ outb(0x01, dev->iobase + DAC_ENABLE);
+
+ for (i = 0; i < insn->n; i++) {
+ outb(data[i] & 0xFF, port); // LSB
+ outb((data[i] >> 8) & 0x0F, port + 1); // MSB
+ devpriv->ao_readback[chan] = data[i];
+ }
+ return insn->n;
+}
+
+static const struct comedi_lrange range_aio_aio12_8 = {
+ 4,
+ {
+ UNI_RANGE(5),
+ BIP_RANGE(5),
+ UNI_RANGE(10),
+ BIP_RANGE(10),
+ }
+};
+
+static int aio_aio12_8_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int iobase;
+ struct comedi_subdevice *s;
+
+ iobase = it->options[0];
+ if (!request_region(iobase, 24, "aio_aio12_8")) {
+ printk("I/O port conflict");
+ return -EIO;
+ }
+
+ dev->board_name = thisboard->name;
+
+ dev->iobase = iobase;
+
+ if (alloc_private(dev, sizeof(struct aio12_8_private)) < 0)
+ return -ENOMEM;
+
+ if (alloc_subdevices(dev, 3) < 0)
+ return -ENOMEM;
+
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
+ s->n_chan = 8;
+ s->maxdata = (1 << 12) - 1;
+ s->range_table = &range_aio_aio12_8;
+ s->insn_read = aio_aio12_8_ai_read;
+
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_DIFF;
+ s->n_chan = 4;
+ s->maxdata = (1 << 12) - 1;
+ s->range_table = &range_aio_aio12_8;
+ s->insn_read = aio_aio12_8_ao_read;
+ s->insn_write = aio_aio12_8_ao_write;
+
+ s = &dev->subdevices[2];
+ subdev_8255_init(dev, s, NULL, dev->iobase + AIO12_8_DIO_0);
+
+ return 0;
+}
+
+static int aio_aio12_8_detach(struct comedi_device * dev)
+{
+ subdev_8255_cleanup(dev, &dev->subdevices[2]);
+ if (dev->iobase)
+ release_region(dev->iobase, 24);
+ return 0;
+}
+
+static struct comedi_driver driver_aio_aio12_8 = {
+ driver_name:"aio_aio12_8",
+ module:THIS_MODULE,
+ attach:aio_aio12_8_attach,
+ detach:aio_aio12_8_detach,
+ board_name:&board_types[0].name,
+ num_names:1,
+ offset:sizeof(struct aio12_8_boardtype),
+};
+
+COMEDI_INITCLEANUP(driver_aio_aio12_8);
diff --git a/drivers/staging/comedi/drivers/aio_iiro_16.c b/drivers/staging/comedi/drivers/aio_iiro_16.c
new file mode 100644
index 000000000000..9160fdf0ca37
--- /dev/null
+++ b/drivers/staging/comedi/drivers/aio_iiro_16.c
@@ -0,0 +1,177 @@
+/*
+
+ comedi/drivers/aio_iiro_16.c
+
+ Driver for Acces I/O Products PC-104 AIO-IIRO-16 Digital I/O board
+ Copyright (C) 2006 C&C Technologies, Inc.
+
+ 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.
+*/
+
+/*
+
+Driver: aio_iiro_16
+Description: Acces I/O Products PC-104 IIRO16 Relay And Isolated Input Board
+Author: Zachary Ware <zach.ware@cctechnol.com>
+Devices:
+ [Acces I/O] PC-104 AIO12-8
+Status: experimental
+
+Configuration Options:
+ [0] - I/O port base address
+
+*/
+
+#include "../comedidev.h"
+#include <linux/ioport.h>
+
+#define AIO_IIRO_16_SIZE 0x08
+#define AIO_IIRO_16_RELAY_0_7 0x00
+#define AIO_IIRO_16_INPUT_0_7 0x01
+#define AIO_IIRO_16_IRQ 0x02
+#define AIO_IIRO_16_RELAY_8_15 0x04
+#define AIO_IIRO_16_INPUT_8_15 0x05
+
+struct aio_iiro_16_board {
+ const char *name;
+ int do_;
+ int di;
+};
+
+static const struct aio_iiro_16_board aio_iiro_16_boards[] = {
+ {
+ name: "aio_iiro_16",
+ di: 16,
+ do_: 16},
+};
+
+#define thisboard ((const struct aio_iiro_16_board *) dev->board_ptr)
+
+struct aio_iiro_16_private {
+ int data;
+ struct pci_dev *pci_dev;
+ unsigned int ao_readback[2];
+};
+
+#define devpriv ((struct aio_iiro_16_private *) dev->private)
+
+static int aio_iiro_16_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+
+static int aio_iiro_16_detach(struct comedi_device * dev);
+
+static struct comedi_driver driver_aio_iiro_16 = {
+ driver_name:"aio_iiro_16",
+ module:THIS_MODULE,
+ attach:aio_iiro_16_attach,
+ detach:aio_iiro_16_detach,
+ board_name:&aio_iiro_16_boards[0].name,
+ offset:sizeof(struct aio_iiro_16_board),
+ num_names:sizeof(aio_iiro_16_boards) / sizeof(struct aio_iiro_16_board),
+};
+
+static int aio_iiro_16_dio_insn_bits_read(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data);
+
+static int aio_iiro_16_dio_insn_bits_write(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data);
+
+static int aio_iiro_16_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int iobase;
+ struct comedi_subdevice *s;
+
+ printk("comedi%d: aio_iiro_16: ", dev->minor);
+
+ dev->board_name = thisboard->name;
+
+ iobase = it->options[0];
+
+ if (!request_region(iobase, AIO_IIRO_16_SIZE, dev->board_name)) {
+ printk("I/O port conflict");
+ return -EIO;
+ }
+
+ dev->iobase = iobase;
+
+ if (alloc_private(dev, sizeof(struct aio_iiro_16_private)) < 0)
+ return -ENOMEM;
+
+ if (alloc_subdevices(dev, 2) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = aio_iiro_16_dio_insn_bits_write;
+
+ s = dev->subdevices + 1;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = aio_iiro_16_dio_insn_bits_read;
+
+ printk("attached\n");
+
+ return 1;
+}
+
+static int aio_iiro_16_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: aio_iiro_16: remove\n", dev->minor);
+
+ if (dev->iobase)
+ release_region(dev->iobase, AIO_IIRO_16_SIZE);
+
+ return 0;
+}
+
+static int aio_iiro_16_dio_insn_bits_write(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+ outb(s->state & 0xff, dev->iobase + AIO_IIRO_16_RELAY_0_7);
+ outb((s->state >> 8) & 0xff,
+ dev->iobase + AIO_IIRO_16_RELAY_8_15);
+ }
+
+ data[1] = s->state;
+
+ return 2;
+}
+
+static int aio_iiro_16_dio_insn_bits_read(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ data[1] = 0;
+ data[1] |= inb(dev->iobase + AIO_IIRO_16_INPUT_0_7);
+ data[1] |= inb(dev->iobase + AIO_IIRO_16_INPUT_8_15) << 8;
+
+ return 2;
+}
+
+COMEDI_INITCLEANUP(driver_aio_iiro_16);
diff --git a/drivers/staging/comedi/drivers/am9513.h b/drivers/staging/comedi/drivers/am9513.h
new file mode 100644
index 000000000000..f533cf1658cf
--- /dev/null
+++ b/drivers/staging/comedi/drivers/am9513.h
@@ -0,0 +1,79 @@
+/*
+ module/am9513.h
+ value added preprocessor definitions for Am9513 timer chip
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#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/drivers/staging/comedi/drivers/amcc_s5933.h b/drivers/staging/comedi/drivers/amcc_s5933.h
new file mode 100644
index 000000000000..aceffce7ef3f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amcc_s5933.h
@@ -0,0 +1,172 @@
+/*
+ comedi/drivers/amcc_s5933.h
+
+ Stuff for AMCC S5933 PCI Controller
+
+ Author: Michal Dobes <dobes@tesnet.cz>
+
+ Inspirated from general-purpose AMCC S5933 PCI Matchmaker driver
+ made by Andrea Cisternino <acister@pcape1.pi.infn.it>
+ and as result of espionage from MITE code made by David A. Schleef.
+ Thanks to AMCC for their on-line documentation and bus master DMA
+ example.
+*/
+
+#ifndef _AMCC_S5933_H_
+#define _AMCC_S5933_H_
+
+/****************************************************************************/
+/* AMCC Operation Register Offsets - PCI */
+/****************************************************************************/
+
+#define AMCC_OP_REG_OMB1 0x00
+#define AMCC_OP_REG_OMB2 0x04
+#define AMCC_OP_REG_OMB3 0x08
+#define AMCC_OP_REG_OMB4 0x0c
+#define AMCC_OP_REG_IMB1 0x10
+#define AMCC_OP_REG_IMB2 0x14
+#define AMCC_OP_REG_IMB3 0x18
+#define AMCC_OP_REG_IMB4 0x1c
+#define AMCC_OP_REG_FIFO 0x20
+#define AMCC_OP_REG_MWAR 0x24
+#define AMCC_OP_REG_MWTC 0x28
+#define AMCC_OP_REG_MRAR 0x2c
+#define AMCC_OP_REG_MRTC 0x30
+#define AMCC_OP_REG_MBEF 0x34
+#define AMCC_OP_REG_INTCSR 0x38
+#define AMCC_OP_REG_INTCSR_SRC (AMCC_OP_REG_INTCSR + 2) /* INT source */
+#define AMCC_OP_REG_INTCSR_FEC (AMCC_OP_REG_INTCSR + 3) /* FIFO ctrl */
+#define AMCC_OP_REG_MCSR 0x3c
+#define AMCC_OP_REG_MCSR_NVDATA (AMCC_OP_REG_MCSR + 2) /* Data in byte 2 */
+#define AMCC_OP_REG_MCSR_NVCMD (AMCC_OP_REG_MCSR + 3) /* Command in byte 3 */
+
+#define AMCC_FIFO_DEPTH_DWORD 8
+#define AMCC_FIFO_DEPTH_BYTES (8 * sizeof (u32))
+
+/****************************************************************************/
+/* AMCC - PCI Interrupt Control/Status Register */
+/****************************************************************************/
+#define INTCSR_OUTBOX_BYTE(x) ((x) & 0x3)
+#define INTCSR_OUTBOX_SELECT(x) (((x) & 0x3) << 2)
+#define INTCSR_OUTBOX_EMPTY_INT 0x10 // enable outbox empty interrupt
+#define INTCSR_INBOX_BYTE(x) (((x) & 0x3) << 8)
+#define INTCSR_INBOX_SELECT(x) (((x) & 0x3) << 10)
+#define INTCSR_INBOX_FULL_INT 0x1000 // enable inbox full interrupt
+#define INTCSR_INBOX_INTR_STATUS 0x20000 // read, or write clear inbox full interrupt
+#define INTCSR_INTR_ASSERTED 0x800000 // read only, interrupt asserted
+
+/****************************************************************************/
+/* AMCC - PCI non-volatile ram command register (byte 3 of master control/status register) */
+/****************************************************************************/
+#define MCSR_NV_LOAD_LOW_ADDR 0x0
+#define MCSR_NV_LOAD_HIGH_ADDR 0x20
+#define MCSR_NV_WRITE 0x40
+#define MCSR_NV_READ 0x60
+#define MCSR_NV_MASK 0x60
+#define MCSR_NV_ENABLE 0x80
+#define MCSR_NV_BUSY MCSR_NV_ENABLE
+
+/****************************************************************************/
+/* AMCC Operation Registers Size - PCI */
+/****************************************************************************/
+
+#define AMCC_OP_REG_SIZE 64 /* in bytes */
+
+/****************************************************************************/
+/* AMCC Operation Register Offsets - Add-on */
+/****************************************************************************/
+
+#define AMCC_OP_REG_AIMB1 0x00
+#define AMCC_OP_REG_AIMB2 0x04
+#define AMCC_OP_REG_AIMB3 0x08
+#define AMCC_OP_REG_AIMB4 0x0c
+#define AMCC_OP_REG_AOMB1 0x10
+#define AMCC_OP_REG_AOMB2 0x14
+#define AMCC_OP_REG_AOMB3 0x18
+#define AMCC_OP_REG_AOMB4 0x1c
+#define AMCC_OP_REG_AFIFO 0x20
+#define AMCC_OP_REG_AMWAR 0x24
+#define AMCC_OP_REG_APTA 0x28
+#define AMCC_OP_REG_APTD 0x2c
+#define AMCC_OP_REG_AMRAR 0x30
+#define AMCC_OP_REG_AMBEF 0x34
+#define AMCC_OP_REG_AINT 0x38
+#define AMCC_OP_REG_AGCSTS 0x3c
+#define AMCC_OP_REG_AMWTC 0x58
+#define AMCC_OP_REG_AMRTC 0x5c
+
+/****************************************************************************/
+/* AMCC - Add-on General Control/Status Register */
+/****************************************************************************/
+
+#define AGCSTS_CONTROL_MASK 0xfffff000
+#define AGCSTS_NV_ACC_MASK 0xe0000000
+#define AGCSTS_RESET_MASK 0x0e000000
+#define AGCSTS_NV_DA_MASK 0x00ff0000
+#define AGCSTS_BIST_MASK 0x0000f000
+#define AGCSTS_STATUS_MASK 0x000000ff
+#define AGCSTS_TCZERO_MASK 0x000000c0
+#define AGCSTS_FIFO_ST_MASK 0x0000003f
+
+#define AGCSTS_RESET_MBFLAGS 0x08000000
+#define AGCSTS_RESET_P2A_FIFO 0x04000000
+#define AGCSTS_RESET_A2P_FIFO 0x02000000
+#define AGCSTS_RESET_FIFOS (AGCSTS_RESET_A2P_FIFO | AGCSTS_RESET_P2A_FIFO)
+
+#define AGCSTS_A2P_TCOUNT 0x00000080
+#define AGCSTS_P2A_TCOUNT 0x00000040
+
+#define AGCSTS_FS_P2A_EMPTY 0x00000020
+#define AGCSTS_FS_P2A_HALF 0x00000010
+#define AGCSTS_FS_P2A_FULL 0x00000008
+
+#define AGCSTS_FS_A2P_EMPTY 0x00000004
+#define AGCSTS_FS_A2P_HALF 0x00000002
+#define AGCSTS_FS_A2P_FULL 0x00000001
+
+/****************************************************************************/
+/* AMCC - Add-on Interrupt Control/Status Register */
+/****************************************************************************/
+
+#define AINT_INT_MASK 0x00ff0000
+#define AINT_SEL_MASK 0x0000ffff
+#define AINT_IS_ENSEL_MASK 0x00001f1f
+
+#define AINT_INT_ASSERTED 0x00800000
+#define AINT_BM_ERROR 0x00200000
+#define AINT_BIST_INT 0x00100000
+
+#define AINT_RT_COMPLETE 0x00080000
+#define AINT_WT_COMPLETE 0x00040000
+
+#define AINT_OUT_MB_INT 0x00020000
+#define AINT_IN_MB_INT 0x00010000
+
+#define AINT_READ_COMPL 0x00008000
+#define AINT_WRITE_COMPL 0x00004000
+
+#define AINT_OMB_ENABLE 0x00001000
+#define AINT_OMB_SELECT 0x00000c00
+#define AINT_OMB_BYTE 0x00000300
+
+#define AINT_IMB_ENABLE 0x00000010
+#define AINT_IMB_SELECT 0x0000000c
+#define AINT_IMB_BYTE 0x00000003
+
+// these are bits from various different registers, needs cleanup XXX
+/* Enable Bus Mastering */
+#define EN_A2P_TRANSFERS 0x00000400
+/* FIFO Flag Reset */
+#define RESET_A2P_FLAGS 0x04000000L
+/* FIFO Relative Priority */
+#define A2P_HI_PRIORITY 0x00000100L
+/* Identify Interrupt Sources */
+#define ANY_S593X_INT 0x00800000L
+#define READ_TC_INT 0x00080000L
+#define WRITE_TC_INT 0x00040000L
+#define IN_MB_INT 0x00020000L
+#define MASTER_ABORT_INT 0x00100000L
+#define TARGET_ABORT_INT 0x00200000L
+#define BUS_MASTER_INT 0x00200000L
+
+#endif
diff --git a/drivers/staging/comedi/drivers/amplc_dio200.c b/drivers/staging/comedi/drivers/amplc_dio200.c
new file mode 100644
index 000000000000..8555e272a861
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_dio200.c
@@ -0,0 +1,1483 @@
+/*
+ comedi/drivers/amplc_dio200.c
+ Driver for Amplicon PC272E and PCI272 DIO boards.
+ (Support for other boards in Amplicon 200 series may be added at
+ a later date, e.g. PCI215.)
+
+ Copyright (C) 2005 MEV Ltd. <http://www.mev.co.uk/>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: amplc_dio200
+Description: Amplicon 200 Series Digital I/O
+Author: Ian Abbott <abbotti@mev.co.uk>
+Devices: [Amplicon] PC212E (pc212e), PC214E (pc214e), PC215E (pc215e),
+ PCI215 (pci215 or amplc_dio200), PC218E (pc218e), PC272E (pc272e),
+ PCI272 (pci272 or amplc_dio200)
+Updated: Wed, 22 Oct 2008 13:36:02 +0100
+Status: works
+
+Configuration options - PC212E, PC214E, PC215E, PC218E, PC272E:
+ [0] - I/O port base address
+ [1] - IRQ (optional, but commands won't work without it)
+
+Configuration options - PCI215, PCI272:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first available PCI device will
+ be used.
+
+Passing a zero for an option is the same as leaving it unspecified.
+
+SUBDEVICES
+
+ PC218E PC212E PC215E/PCI215
+ ------------- ------------- -------------
+ Subdevices 7 6 5
+ 0 CTR-X1 PPI-X PPI-X
+ 1 CTR-X2 CTR-Y1 PPI-Y
+ 2 CTR-Y1 CTR-Y2 CTR-Z1
+ 3 CTR-Y2 CTR-Z1 CTR-Z2
+ 4 CTR-Z1 CTR-Z2 INTERRUPT
+ 5 CTR-Z2 INTERRUPT
+ 6 INTERRUPT
+
+ PC214E PC272E/PCI272
+ ------------- -------------
+ Subdevices 4 4
+ 0 PPI-X PPI-X
+ 1 PPI-Y PPI-Y
+ 2 CTR-Z1* PPI-Z
+ 3 INTERRUPT* INTERRUPT
+
+Each PPI is a 8255 chip providing 24 DIO channels. The DIO channels
+are configurable as inputs or outputs in four groups:
+
+ Port A - channels 0 to 7
+ Port B - channels 8 to 15
+ Port CL - channels 16 to 19
+ Port CH - channels 20 to 23
+
+Only mode 0 of the 8255 chips is supported.
+
+Each CTR is a 8254 chip providing 3 16-bit counter channels. Each
+channel is configured individually with INSN_CONFIG instructions. The
+specific type of configuration instruction is specified in data[0].
+Some configuration instructions expect an additional parameter in
+data[1]; others return a value in data[1]. The following configuration
+instructions are supported:
+
+ INSN_CONFIG_SET_COUNTER_MODE. Sets the counter channel's mode and
+ BCD/binary setting specified in data[1].
+
+ INSN_CONFIG_8254_READ_STATUS. Reads the status register value for the
+ counter channel into data[1].
+
+ INSN_CONFIG_SET_CLOCK_SRC. Sets the counter channel's clock source as
+ specified in data[1] (this is a hardware-specific value). Not
+ supported on PC214E. For the other boards, valid clock sources are
+ 0 to 7 as follows:
+
+ 0. CLK n, the counter channel's dedicated CLK input from the SK1
+ connector. (N.B. for other values, the counter channel's CLKn
+ pin on the SK1 connector is an output!)
+ 1. Internal 10 MHz clock.
+ 2. Internal 1 MHz clock.
+ 3. Internal 100 kHz clock.
+ 4. Internal 10 kHz clock.
+ 5. Internal 1 kHz clock.
+ 6. OUT n-1, the output of counter channel n-1 (see note 1 below).
+ 7. Ext Clock, the counter chip's dedicated Ext Clock input from
+ the SK1 connector. This pin is shared by all three counter
+ channels on the chip.
+
+ INSN_CONFIG_GET_CLOCK_SRC. Returns the counter channel's current
+ clock source in data[1]. For internal clock sources, data[2] is set
+ to the period in ns.
+
+ INSN_CONFIG_SET_GATE_SRC. Sets the counter channel's gate source as
+ specified in data[2] (this is a hardware-specific value). Not
+ supported on PC214E. For the other boards, valid gate sources are 0
+ to 7 as follows:
+
+ 0. VCC (internal +5V d.c.), i.e. gate permanently enabled.
+ 1. GND (internal 0V d.c.), i.e. gate permanently disabled.
+ 2. GAT n, the counter channel's dedicated GAT input from the SK1
+ connector. (N.B. for other values, the counter channel's GATn
+ pin on the SK1 connector is an output!)
+ 3. /OUT n-2, the inverted output of counter channel n-2 (see note
+ 2 below).
+ 4. Reserved.
+ 5. Reserved.
+ 6. Reserved.
+ 7. Reserved.
+
+ INSN_CONFIG_GET_GATE_SRC. Returns the counter channel's current gate
+ source in data[2].
+
+Clock and gate interconnection notes:
+
+ 1. Clock source OUT n-1 is the output of the preceding channel on the
+ same counter subdevice if n > 0, or the output of channel 2 on the
+ preceding counter subdevice (see note 3) if n = 0.
+
+ 2. Gate source /OUT n-2 is the inverted output of channel 0 on the
+ same counter subdevice if n = 2, or the inverted output of channel n+1
+ on the preceding counter subdevice (see note 3) if n < 2.
+
+ 3. The counter subdevices are connected in a ring, so the highest
+ counter subdevice precedes the lowest.
+
+The 'INTERRUPT' subdevice pretends to be a digital input subdevice. The
+digital inputs come from the interrupt status register. The number of
+channels matches the number of interrupt sources. The PC214E does not
+have an interrupt status register; see notes on 'INTERRUPT SOURCES'
+below.
+
+INTERRUPT SOURCES
+
+ PC218E PC212E PC215E/PCI215
+ ------------- ------------- -------------
+ Sources 6 6 6
+ 0 CTR-X1-OUT PPI-X-C0 PPI-X-C0
+ 1 CTR-X2-OUT PPI-X-C3 PPI-X-C3
+ 2 CTR-Y1-OUT CTR-Y1-OUT PPI-Y-C0
+ 3 CTR-Y2-OUT CTR-Y2-OUT PPI-Y-C3
+ 4 CTR-Z1-OUT CTR-Z1-OUT CTR-Z1-OUT
+ 5 CTR-Z2-OUT CTR-Z2-OUT CTR-Z2-OUT
+
+ PC214E PC272E/PCI272
+ ------------- -------------
+ Sources 1 6
+ 0 JUMPER-J5 PPI-X-C0
+ 1 PPI-X-C3
+ 2 PPI-Y-C0
+ 3 PPI-Y-C3
+ 4 PPI-Z-C0
+ 5 PPI-Z-C3
+
+When an interrupt source is enabled in the interrupt source enable
+register, a rising edge on the source signal latches the corresponding
+bit to 1 in the interrupt status register.
+
+When the interrupt status register value as a whole (actually, just the
+6 least significant bits) goes from zero to non-zero, the board will
+generate an interrupt. For level-triggered hardware interrupts (PCI
+card), the interrupt will remain asserted until the interrupt status
+register is cleared to zero. For edge-triggered hardware interrupts
+(ISA card), no further interrupts will occur until the interrupt status
+register is cleared to zero. To clear a bit to zero in the interrupt
+status register, the corresponding interrupt source must be disabled
+in the interrupt source enable register (there is no separate interrupt
+clear register).
+
+The PC214E does not have an interrupt source enable register or an
+interrupt status register; its 'INTERRUPT' subdevice has a single
+channel and its interrupt source is selected by the position of jumper
+J5.
+
+COMMANDS
+
+The driver supports a read streaming acquisition command on the
+'INTERRUPT' subdevice. The channel list selects the interrupt sources
+to be enabled. All channels will be sampled together (convert_src ==
+TRIG_NOW). The scan begins a short time after the hardware interrupt
+occurs, subject to interrupt latencies (scan_begin_src == TRIG_EXT,
+scan_begin_arg == 0). The value read from the interrupt status register
+is packed into a short value, one bit per requested channel, in the
+order they appear in the channel list.
+*/
+
+#include "../comedidev.h"
+
+#include "comedi_pci.h"
+
+#include "8255.h"
+#include "8253.h"
+
+#define DIO200_DRIVER_NAME "amplc_dio200"
+
+/* PCI IDs */
+/* #define PCI_VENDOR_ID_AMPLICON 0x14dc */
+#define PCI_DEVICE_ID_AMPLICON_PCI272 0x000a
+#define PCI_DEVICE_ID_AMPLICON_PCI215 0x000b
+#define PCI_DEVICE_ID_INVALID 0xffff
+
+/* 200 series registers */
+#define DIO200_IO_SIZE 0x20
+#define DIO200_XCLK_SCE 0x18 /* Group X clock selection register */
+#define DIO200_YCLK_SCE 0x19 /* Group Y clock selection register */
+#define DIO200_ZCLK_SCE 0x1a /* Group Z clock selection register */
+#define DIO200_XGAT_SCE 0x1b /* Group X gate selection register */
+#define DIO200_YGAT_SCE 0x1c /* Group Y gate selection register */
+#define DIO200_ZGAT_SCE 0x1d /* Group Z gate selection register */
+#define DIO200_INT_SCE 0x1e /* Interrupt enable/status register */
+
+/*
+ * Macros for constructing value for DIO_200_?CLK_SCE and
+ * DIO_200_?GAT_SCE registers:
+ *
+ * 'which' is: 0 for CTR-X1, CTR-Y1, CTR-Z1; 1 for CTR-X2, CTR-Y2 or CTR-Z2.
+ * 'chan' is the channel: 0, 1 or 2.
+ * 'source' is the signal source: 0 to 7.
+ */
+#define CLK_SCE(which, chan, source) (((which) << 5) | ((chan) << 3) | (source))
+#define GAT_SCE(which, chan, source) (((which) << 5) | ((chan) << 3) | (source))
+
+/*
+ * Periods of the internal clock sources in nanoseconds.
+ */
+static const unsigned clock_period[8] = {
+ 0, /* dedicated clock input/output pin */
+ 100, /* 10 MHz */
+ 1000, /* 1 MHz */
+ 10000, /* 100 kHz */
+ 100000, /* 10 kHz */
+ 1000000, /* 1 kHz */
+ 0, /* OUT N-1 */
+ 0 /* group clock input pin */
+};
+
+/*
+ * Board descriptions.
+ */
+
+enum dio200_bustype { isa_bustype, pci_bustype };
+
+enum dio200_model {
+ pc212e_model,
+ pc214e_model,
+ pc215e_model, pci215_model,
+ pc218e_model,
+ pc272e_model, pci272_model,
+ anypci_model
+};
+
+enum dio200_layout {
+ pc212_layout,
+ pc214_layout,
+ pc215_layout,
+ pc218_layout,
+ pc272_layout
+};
+
+struct dio200_board {
+ const char *name;
+ unsigned short devid;
+ enum dio200_bustype bustype;
+ enum dio200_model model;
+ enum dio200_layout layout;
+};
+
+static const struct dio200_board dio200_boards[] = {
+ {
+ name: "pc212e",
+ bustype: isa_bustype,
+ model: pc212e_model,
+ layout: pc212_layout,
+ },
+ {
+ name: "pc214e",
+ bustype: isa_bustype,
+ model: pc214e_model,
+ layout: pc214_layout,
+ },
+ {
+ name: "pc215e",
+ bustype: isa_bustype,
+ model: pc215e_model,
+ layout: pc215_layout,
+ },
+#ifdef CONFIG_COMEDI_PCI
+ {
+ name: "pci215",
+ devid: PCI_DEVICE_ID_AMPLICON_PCI215,
+ bustype: pci_bustype,
+ model: pci215_model,
+ layout: pc215_layout,
+ },
+#endif
+ {
+ name: "pc218e",
+ bustype: isa_bustype,
+ model: pc218e_model,
+ layout: pc218_layout,
+ },
+ {
+ name: "pc272e",
+ bustype: isa_bustype,
+ model: pc272e_model,
+ layout: pc272_layout,
+ },
+#ifdef CONFIG_COMEDI_PCI
+ {
+ name: "pci272",
+ devid: PCI_DEVICE_ID_AMPLICON_PCI272,
+ bustype: pci_bustype,
+ model: pci272_model,
+ layout: pc272_layout,
+ },
+#endif
+#ifdef CONFIG_COMEDI_PCI
+ {
+ name: DIO200_DRIVER_NAME,
+ devid: PCI_DEVICE_ID_INVALID,
+ bustype: pci_bustype,
+ model: anypci_model, /* wildcard */
+ },
+#endif
+};
+
+/*
+ * Layout descriptions - some ISA and PCI board descriptions share the same
+ * layout.
+ */
+
+enum dio200_sdtype { sd_none, sd_intr, sd_8255, sd_8254 };
+
+#define DIO200_MAX_SUBDEVS 7
+#define DIO200_MAX_ISNS 6
+
+struct dio200_layout_struct {
+ unsigned short n_subdevs; /* number of subdevices */
+ unsigned char sdtype[DIO200_MAX_SUBDEVS]; /* enum dio200_sdtype */
+ unsigned char sdinfo[DIO200_MAX_SUBDEVS]; /* depends on sdtype */
+ char has_int_sce; /* has interrupt enable/status register */
+ char has_clk_gat_sce; /* has clock/gate selection registers */
+};
+
+static const struct dio200_layout_struct dio200_layouts[] = {
+ [pc212_layout] = {
+ n_subdevs:6,
+ sdtype: {sd_8255, sd_8254, sd_8254, sd_8254,
+ sd_8254,
+ sd_intr},
+ sdinfo: {0x00, 0x08, 0x0C, 0x10, 0x14,
+ 0x3F},
+ has_int_sce:1,
+ has_clk_gat_sce:1,
+ },
+ [pc214_layout] = {
+ n_subdevs:4,
+ sdtype: {sd_8255, sd_8255, sd_8254,
+ sd_intr},
+ sdinfo: {0x00, 0x08, 0x10, 0x01},
+ has_int_sce:0,
+ has_clk_gat_sce:0,
+ },
+ [pc215_layout] = {
+ n_subdevs:5,
+ sdtype: {sd_8255, sd_8255, sd_8254,
+ sd_8254,
+ sd_intr},
+ sdinfo: {0x00, 0x08, 0x10, 0x14, 0x3F},
+ has_int_sce:1,
+ has_clk_gat_sce:1,
+ },
+ [pc218_layout] = {
+ n_subdevs:7,
+ sdtype: {sd_8254, sd_8254, sd_8255, sd_8254,
+ sd_8254,
+ sd_intr},
+ sdinfo: {0x00, 0x04, 0x08, 0x0C, 0x10,
+ 0x14,
+ 0x3F},
+ has_int_sce:1,
+ has_clk_gat_sce:1,
+ },
+ [pc272_layout] = {
+ n_subdevs:4,
+ sdtype: {sd_8255, sd_8255, sd_8255,
+ sd_intr},
+ sdinfo: {0x00, 0x08, 0x10, 0x3F},
+ has_int_sce:1,
+ has_clk_gat_sce:0,
+ },
+};
+
+/*
+ * PCI driver table.
+ */
+
+#ifdef CONFIG_COMEDI_PCI
+static DEFINE_PCI_DEVICE_TABLE(dio200_pci_table) = {
+ {PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI215,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI272,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, dio200_pci_table);
+#endif /* CONFIG_COMEDI_PCI */
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct dio200_board *)dev->board_ptr)
+#define thislayout (&dio200_layouts[((struct dio200_board *)dev->board_ptr)->layout])
+
+/* 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 struct comedi_device struct. */
+struct dio200_private {
+#ifdef CONFIG_COMEDI_PCI
+ struct pci_dev *pci_dev; /* PCI device */
+#endif
+ int intr_sd;
+};
+
+#define devpriv ((struct dio200_private *)dev->private)
+
+struct dio200_subdev_8254 {
+ unsigned long iobase; /* Counter base address */
+ unsigned long clk_sce_iobase; /* CLK_SCE base address */
+ unsigned long gat_sce_iobase; /* GAT_SCE base address */
+ int which; /* Bit 5 of CLK_SCE or GAT_SCE */
+ int has_clk_gat_sce;
+ unsigned clock_src[3]; /* Current clock sources */
+ unsigned gate_src[3]; /* Current gate sources */
+};
+
+struct dio200_subdev_intr {
+ unsigned long iobase;
+ spinlock_t spinlock;
+ int active;
+ int has_int_sce;
+ unsigned int valid_isns;
+ unsigned int enabled_isns;
+ unsigned int stopcount;
+ int continuous;
+};
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int dio200_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int dio200_detach(struct comedi_device * dev);
+static struct comedi_driver driver_amplc_dio200 = {
+ driver_name:DIO200_DRIVER_NAME,
+ module:THIS_MODULE,
+ attach:dio200_attach,
+ detach:dio200_detach,
+ board_name:&dio200_boards[0].name,
+ offset:sizeof(struct dio200_board),
+ num_names:sizeof(dio200_boards) / sizeof(struct dio200_board),
+};
+
+#ifdef CONFIG_COMEDI_PCI
+COMEDI_PCI_INITCLEANUP(driver_amplc_dio200, dio200_pci_table);
+#else
+COMEDI_INITCLEANUP(driver_amplc_dio200);
+#endif
+
+/*
+ * This function looks for a PCI device matching the requested board name,
+ * bus and slot.
+ */
+#ifdef CONFIG_COMEDI_PCI
+static int
+dio200_find_pci(struct comedi_device * dev, int bus, int slot,
+ struct pci_dev **pci_dev_p)
+{
+ struct pci_dev *pci_dev = NULL;
+
+ *pci_dev_p = NULL;
+
+ /* Look for matching PCI device. */
+ for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
+ pci_dev != NULL;
+ pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
+ PCI_ANY_ID, pci_dev)) {
+ /* If bus/slot specified, check them. */
+ if (bus || slot) {
+ if (bus != pci_dev->bus->number
+ || slot != PCI_SLOT(pci_dev->devfn))
+ continue;
+ }
+ if (thisboard->model == anypci_model) {
+ /* Match any supported model. */
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dio200_boards); i++) {
+ if (dio200_boards[i].bustype != pci_bustype)
+ continue;
+ if (pci_dev->device == dio200_boards[i].devid) {
+ /* Change board_ptr to matched board. */
+ dev->board_ptr = &dio200_boards[i];
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(dio200_boards))
+ continue;
+ } else {
+ /* Match specific model name. */
+ if (pci_dev->device != thisboard->devid)
+ continue;
+ }
+
+ /* Found a match. */
+ *pci_dev_p = pci_dev;
+ return 0;
+ }
+ /* No match found. */
+ if (bus || slot) {
+ printk(KERN_ERR
+ "comedi%d: error! no %s found at pci %02x:%02x!\n",
+ dev->minor, thisboard->name, bus, slot);
+ } else {
+ printk(KERN_ERR "comedi%d: error! no %s found!\n",
+ dev->minor, thisboard->name);
+ }
+ return -EIO;
+}
+#endif
+
+/*
+ * This function checks and requests an I/O region, reporting an error
+ * if there is a conflict.
+ */
+static int
+dio200_request_region(unsigned minor, unsigned long from, unsigned long extent)
+{
+ if (!from || !request_region(from, extent, DIO200_DRIVER_NAME)) {
+ printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
+ minor, from, extent);
+ return -EIO;
+ }
+ return 0;
+}
+
+/*
+ * 'insn_bits' function for an 'INTERRUPT' subdevice.
+ */
+static int
+dio200_subdev_intr_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ struct dio200_subdev_intr *subpriv = s->private;
+
+ if (subpriv->has_int_sce) {
+ /* Just read the interrupt status register. */
+ data[1] = inb(subpriv->iobase) & subpriv->valid_isns;
+ } else {
+ /* No interrupt status register. */
+ data[0] = 0;
+ }
+
+ return 2;
+}
+
+/*
+ * Called to stop acquisition for an 'INTERRUPT' subdevice.
+ */
+static void dio200_stop_intr(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct dio200_subdev_intr *subpriv = s->private;
+
+ subpriv->active = 0;
+ subpriv->enabled_isns = 0;
+ if (subpriv->has_int_sce) {
+ outb(0, subpriv->iobase);
+ }
+}
+
+/*
+ * Called to start acquisition for an 'INTERRUPT' subdevice.
+ */
+static int dio200_start_intr(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned int n;
+ unsigned isn_bits;
+ struct dio200_subdev_intr *subpriv = s->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int retval = 0;
+
+ if (!subpriv->continuous && subpriv->stopcount == 0) {
+ /* An empty acquisition! */
+ s->async->events |= COMEDI_CB_EOA;
+ subpriv->active = 0;
+ retval = 1;
+ } else {
+ /* Determine interrupt sources to enable. */
+ isn_bits = 0;
+ if (cmd->chanlist) {
+ for (n = 0; n < cmd->chanlist_len; n++) {
+ isn_bits |= (1U << CR_CHAN(cmd->chanlist[n]));
+ }
+ }
+ isn_bits &= subpriv->valid_isns;
+ /* Enable interrupt sources. */
+ subpriv->enabled_isns = isn_bits;
+ if (subpriv->has_int_sce) {
+ outb(isn_bits, subpriv->iobase);
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
+ */
+static int
+dio200_inttrig_start_intr(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trignum)
+{
+ struct dio200_subdev_intr *subpriv;
+ unsigned long flags;
+ int event = 0;
+
+ if (trignum != 0)
+ return -EINVAL;
+
+ subpriv = s->private;
+
+ comedi_spin_lock_irqsave(&subpriv->spinlock, flags);
+ s->async->inttrig = 0;
+ if (subpriv->active) {
+ event = dio200_start_intr(dev, s);
+ }
+ comedi_spin_unlock_irqrestore(&subpriv->spinlock, flags);
+
+ if (event) {
+ comedi_event(dev, s);
+ }
+
+ return 1;
+}
+
+/*
+ * This is called from the interrupt service routine to handle a read
+ * scan on an 'INTERRUPT' subdevice.
+ */
+static int dio200_handle_read_intr(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct dio200_subdev_intr *subpriv = s->private;
+ unsigned triggered;
+ unsigned intstat;
+ unsigned cur_enabled;
+ unsigned int oldevents;
+ unsigned long flags;
+
+ triggered = 0;
+
+ comedi_spin_lock_irqsave(&subpriv->spinlock, flags);
+ oldevents = s->async->events;
+ if (subpriv->has_int_sce) {
+ /*
+ * Collect interrupt sources that have triggered and disable
+ * them temporarily. Loop around until no extra interrupt
+ * sources have triggered, at which point, the valid part of
+ * the interrupt status register will read zero, clearing the
+ * cause of the interrupt.
+ *
+ * Mask off interrupt sources already seen to avoid infinite
+ * loop in case of misconfiguration.
+ */
+ cur_enabled = subpriv->enabled_isns;
+ while ((intstat = (inb(subpriv->iobase) & subpriv->valid_isns
+ & ~triggered)) != 0) {
+ triggered |= intstat;
+ cur_enabled &= ~triggered;
+ outb(cur_enabled, subpriv->iobase);
+ }
+ } else {
+ /*
+ * No interrupt status register. Assume the single interrupt
+ * source has triggered.
+ */
+ triggered = subpriv->enabled_isns;
+ }
+
+ if (triggered) {
+ /*
+ * Some interrupt sources have triggered and have been
+ * temporarily disabled to clear the cause of the interrupt.
+ *
+ * Reenable them NOW to minimize the time they are disabled.
+ */
+ cur_enabled = subpriv->enabled_isns;
+ if (subpriv->has_int_sce) {
+ outb(cur_enabled, subpriv->iobase);
+ }
+
+ if (subpriv->active) {
+ /*
+ * The command is still active.
+ *
+ * Ignore interrupt sources that the command isn't
+ * interested in (just in case there's a race
+ * condition).
+ */
+ if (triggered & subpriv->enabled_isns) {
+ /* Collect scan data. */
+ short val;
+ unsigned int n, ch, len;
+
+ val = 0;
+ len = s->async->cmd.chanlist_len;
+ for (n = 0; n < len; n++) {
+ ch = CR_CHAN(s->async->cmd.chanlist[n]);
+ if (triggered & (1U << ch)) {
+ val |= (1U << n);
+ }
+ }
+ /* Write the scan to the buffer. */
+ if (comedi_buf_put(s->async, val)) {
+ s->async->events |= (COMEDI_CB_BLOCK |
+ COMEDI_CB_EOS);
+ } else {
+ /* Error! Stop acquisition. */
+ dio200_stop_intr(dev, s);
+ s->async->events |= COMEDI_CB_ERROR
+ | COMEDI_CB_OVERFLOW;
+ comedi_error(dev, "buffer overflow");
+ }
+
+ /* Check for end of acquisition. */
+ if (!subpriv->continuous) {
+ /* stop_src == TRIG_COUNT */
+ if (subpriv->stopcount > 0) {
+ subpriv->stopcount--;
+ if (subpriv->stopcount == 0) {
+ s->async->events |=
+ COMEDI_CB_EOA;
+ dio200_stop_intr(dev,
+ s);
+ }
+ }
+ }
+ }
+ }
+ }
+ comedi_spin_unlock_irqrestore(&subpriv->spinlock, flags);
+
+ if (oldevents != s->async->events) {
+ comedi_event(dev, s);
+ }
+
+ return (triggered != 0);
+}
+
+/*
+ * 'cancel' function for an 'INTERRUPT' subdevice.
+ */
+static int dio200_subdev_intr_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct dio200_subdev_intr *subpriv = s->private;
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&subpriv->spinlock, flags);
+ if (subpriv->active) {
+ dio200_stop_intr(dev, s);
+ }
+ comedi_spin_unlock_irqrestore(&subpriv->spinlock, flags);
+
+ return 0;
+}
+
+/*
+ * 'do_cmdtest' function for an 'INTERRUPT' subdevice.
+ */
+static int
+dio200_subdev_intr_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ unsigned int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= (TRIG_NOW | TRIG_INT);
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= (TRIG_COUNT | TRIG_NONE);
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ /* these tests are true if more than one _src bit is set */
+ if ((cmd->start_src & (cmd->start_src - 1)) != 0)
+ err++;
+ if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
+ err++;
+ if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
+ err++;
+ if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
+ err++;
+ if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ /* cmd->start_src == TRIG_NOW || cmd->start_src == TRIG_INT */
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ /* cmd->scan_begin_src == TRIG_EXT */
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+
+ /* cmd->convert_src == TRIG_NOW */
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+
+ /* cmd->scan_end_src == TRIG_COUNT */
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ /* any count allowed */
+ break;
+ case TRIG_NONE:
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ /* if (err) return 4; */
+
+ return 0;
+}
+
+/*
+ * 'do_cmd' function for an 'INTERRUPT' subdevice.
+ */
+static int dio200_subdev_intr_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ struct dio200_subdev_intr *subpriv = s->private;
+ unsigned long flags;
+ int event = 0;
+
+ comedi_spin_lock_irqsave(&subpriv->spinlock, flags);
+ subpriv->active = 1;
+
+ /* Set up end of acquisition. */
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ subpriv->continuous = 0;
+ subpriv->stopcount = cmd->stop_arg;
+ break;
+ default:
+ /* TRIG_NONE */
+ subpriv->continuous = 1;
+ subpriv->stopcount = 0;
+ break;
+ }
+
+ /* Set up start of acquisition. */
+ switch (cmd->start_src) {
+ case TRIG_INT:
+ s->async->inttrig = dio200_inttrig_start_intr;
+ break;
+ default:
+ /* TRIG_NOW */
+ event = dio200_start_intr(dev, s);
+ break;
+ }
+ comedi_spin_unlock_irqrestore(&subpriv->spinlock, flags);
+
+ if (event) {
+ comedi_event(dev, s);
+ }
+
+ return 0;
+}
+
+/*
+ * This function initializes an 'INTERRUPT' subdevice.
+ */
+static int
+dio200_subdev_intr_init(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned long iobase, unsigned valid_isns, int has_int_sce)
+{
+ struct dio200_subdev_intr *subpriv;
+
+ subpriv = kzalloc(sizeof(*subpriv), GFP_KERNEL);
+ if (!subpriv) {
+ printk(KERN_ERR "comedi%d: error! out of memory!\n",
+ dev->minor);
+ return -ENOMEM;
+ }
+ subpriv->iobase = iobase;
+ subpriv->has_int_sce = has_int_sce;
+ subpriv->valid_isns = valid_isns;
+ spin_lock_init(&subpriv->spinlock);
+
+ if (has_int_sce) {
+ outb(0, subpriv->iobase); /* Disable interrupt sources. */
+ }
+
+ s->private = subpriv;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+ if (has_int_sce) {
+ s->n_chan = DIO200_MAX_ISNS;
+ s->len_chanlist = DIO200_MAX_ISNS;
+ } else {
+ /* No interrupt source register. Support single channel. */
+ s->n_chan = 1;
+ s->len_chanlist = 1;
+ }
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+ s->insn_bits = dio200_subdev_intr_insn_bits;
+ s->do_cmdtest = dio200_subdev_intr_cmdtest;
+ s->do_cmd = dio200_subdev_intr_cmd;
+ s->cancel = dio200_subdev_intr_cancel;
+
+ return 0;
+}
+
+/*
+ * This function cleans up an 'INTERRUPT' subdevice.
+ */
+static void
+dio200_subdev_intr_cleanup(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct dio200_subdev_intr *subpriv = s->private;
+
+ if (subpriv) {
+ kfree(subpriv);
+ }
+}
+
+/*
+ * Interrupt service routine.
+ */
+static irqreturn_t dio200_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ int handled;
+
+ if (!dev->attached) {
+ return IRQ_NONE;
+ }
+
+ if (devpriv->intr_sd >= 0) {
+ handled = dio200_handle_read_intr(dev,
+ dev->subdevices + devpriv->intr_sd);
+ } else {
+ handled = 0;
+ }
+
+ return IRQ_RETVAL(handled);
+}
+
+/*
+ * Handle 'insn_read' for an '8254' counter subdevice.
+ */
+static int
+dio200_subdev_8254_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ struct dio200_subdev_8254 *subpriv = s->private;
+ int chan = CR_CHAN(insn->chanspec);
+
+ data[0] = i8254_read(subpriv->iobase, 0, chan);
+
+ return 1;
+}
+
+/*
+ * Handle 'insn_write' for an '8254' counter subdevice.
+ */
+static int
+dio200_subdev_8254_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ struct dio200_subdev_8254 *subpriv = s->private;
+ int chan = CR_CHAN(insn->chanspec);
+
+ i8254_write(subpriv->iobase, 0, chan, data[0]);
+
+ return 1;
+}
+
+/*
+ * Set gate source for an '8254' counter subdevice channel.
+ */
+static int
+dio200_set_gate_src(struct dio200_subdev_8254 * subpriv, unsigned int counter_number,
+ unsigned int gate_src)
+{
+ unsigned char byte;
+
+ if (!subpriv->has_clk_gat_sce)
+ return -1;
+ if (counter_number > 2)
+ return -1;
+ if (gate_src > 7)
+ return -1;
+
+ subpriv->gate_src[counter_number] = gate_src;
+ byte = GAT_SCE(subpriv->which, counter_number, gate_src);
+ outb(byte, subpriv->gat_sce_iobase);
+
+ return 0;
+}
+
+/*
+ * Get gate source for an '8254' counter subdevice channel.
+ */
+static int
+dio200_get_gate_src(struct dio200_subdev_8254 * subpriv, unsigned int counter_number)
+{
+ if (!subpriv->has_clk_gat_sce)
+ return -1;
+ if (counter_number > 2)
+ return -1;
+
+ return subpriv->gate_src[counter_number];
+}
+
+/*
+ * Set clock source for an '8254' counter subdevice channel.
+ */
+static int
+dio200_set_clock_src(struct dio200_subdev_8254 * subpriv, unsigned int counter_number,
+ unsigned int clock_src)
+{
+ unsigned char byte;
+
+ if (!subpriv->has_clk_gat_sce)
+ return -1;
+ if (counter_number > 2)
+ return -1;
+ if (clock_src > 7)
+ return -1;
+
+ subpriv->clock_src[counter_number] = clock_src;
+ byte = CLK_SCE(subpriv->which, counter_number, clock_src);
+ outb(byte, subpriv->clk_sce_iobase);
+
+ return 0;
+}
+
+/*
+ * Get clock source for an '8254' counter subdevice channel.
+ */
+static int
+dio200_get_clock_src(struct dio200_subdev_8254 * subpriv, unsigned int counter_number,
+ unsigned int * period_ns)
+{
+ unsigned clock_src;
+
+ if (!subpriv->has_clk_gat_sce)
+ return -1;
+ if (counter_number > 2)
+ return -1;
+
+ clock_src = subpriv->clock_src[counter_number];
+ *period_ns = clock_period[clock_src];
+ return clock_src;
+}
+
+/*
+ * Handle 'insn_config' for an '8254' counter subdevice.
+ */
+static int
+dio200_subdev_8254_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ struct dio200_subdev_8254 *subpriv = s->private;
+ int ret;
+ int chan = CR_CHAN(insn->chanspec);
+
+ switch (data[0]) {
+ case INSN_CONFIG_SET_COUNTER_MODE:
+ ret = i8254_set_mode(subpriv->iobase, 0, chan, data[1]);
+ if (ret < 0)
+ return -EINVAL;
+ break;
+ case INSN_CONFIG_8254_READ_STATUS:
+ data[1] = i8254_status(subpriv->iobase, 0, chan);
+ break;
+ case INSN_CONFIG_SET_GATE_SRC:
+ ret = dio200_set_gate_src(subpriv, chan, data[2]);
+ if (ret < 0)
+ return -EINVAL;
+ break;
+ case INSN_CONFIG_GET_GATE_SRC:
+ ret = dio200_get_gate_src(subpriv, chan);
+ if (ret < 0)
+ return -EINVAL;
+ data[2] = ret;
+ break;
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ ret = dio200_set_clock_src(subpriv, chan, data[1]);
+ if (ret < 0)
+ return -EINVAL;
+ break;
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ ret = dio200_get_clock_src(subpriv, chan, &data[2]);
+ if (ret < 0)
+ return -EINVAL;
+ data[1] = ret;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return insn->n;
+}
+
+/*
+ * This function initializes an '8254' counter subdevice.
+ *
+ * Note: iobase is the base address of the board, not the subdevice;
+ * offset is the offset to the 8254 chip.
+ */
+static int
+dio200_subdev_8254_init(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned long iobase, unsigned offset, int has_clk_gat_sce)
+{
+ struct dio200_subdev_8254 *subpriv;
+ unsigned int chan;
+
+ subpriv = kzalloc(sizeof(*subpriv), GFP_KERNEL);
+ if (!subpriv) {
+ printk(KERN_ERR "comedi%d: error! out of memory!\n",
+ dev->minor);
+ return -ENOMEM;
+ }
+
+ s->private = subpriv;
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 3;
+ s->maxdata = 0xFFFF;
+ s->insn_read = dio200_subdev_8254_read;
+ s->insn_write = dio200_subdev_8254_write;
+ s->insn_config = dio200_subdev_8254_config;
+
+ subpriv->iobase = offset + iobase;
+ subpriv->has_clk_gat_sce = has_clk_gat_sce;
+ if (has_clk_gat_sce) {
+ /* Derive CLK_SCE and GAT_SCE register offsets from
+ * 8254 offset. */
+ subpriv->clk_sce_iobase =
+ DIO200_XCLK_SCE + (offset >> 3) + iobase;
+ subpriv->gat_sce_iobase =
+ DIO200_XGAT_SCE + (offset >> 3) + iobase;
+ subpriv->which = (offset >> 2) & 1;
+ }
+
+ /* Initialize channels. */
+ for (chan = 0; chan < 3; chan++) {
+ i8254_set_mode(subpriv->iobase, 0, chan,
+ I8254_MODE0 | I8254_BINARY);
+ if (subpriv->has_clk_gat_sce) {
+ /* Gate source 0 is VCC (logic 1). */
+ dio200_set_gate_src(subpriv, chan, 0);
+ /* Clock source 0 is the dedicated clock input. */
+ dio200_set_clock_src(subpriv, chan, 0);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * This function cleans up an '8254' counter subdevice.
+ */
+static void
+dio200_subdev_8254_cleanup(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct dio200_subdev_intr *subpriv = s->private;
+
+ if (subpriv) {
+ kfree(subpriv);
+ }
+}
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int dio200_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase = 0;
+ unsigned int irq = 0;
+#ifdef CONFIG_COMEDI_PCI
+ struct pci_dev *pci_dev = NULL;
+ int bus = 0, slot = 0;
+#endif
+ const struct dio200_layout_struct *layout;
+ int share_irq = 0;
+ int sdx;
+ unsigned n;
+ int ret;
+
+ printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
+ DIO200_DRIVER_NAME);
+
+ if ((ret = alloc_private(dev, sizeof(struct dio200_private))) < 0) {
+ printk(KERN_ERR "comedi%d: error! out of memory!\n",
+ dev->minor);
+ return ret;
+ }
+
+ /* Process options. */
+ switch (thisboard->bustype) {
+ case isa_bustype:
+ iobase = it->options[0];
+ irq = it->options[1];
+ share_irq = 0;
+ break;
+#ifdef CONFIG_COMEDI_PCI
+ case pci_bustype:
+ bus = it->options[0];
+ slot = it->options[1];
+ share_irq = 1;
+
+ if ((ret = dio200_find_pci(dev, bus, slot, &pci_dev)) < 0)
+ return ret;
+ devpriv->pci_dev = pci_dev;
+ break;
+#endif
+ default:
+ printk(KERN_ERR
+ "comedi%d: %s: BUG! cannot determine board type!\n",
+ dev->minor, DIO200_DRIVER_NAME);
+ return -EINVAL;
+ break;
+ }
+
+ devpriv->intr_sd = -1;
+
+ /* Enable device and reserve I/O spaces. */
+#ifdef CONFIG_COMEDI_PCI
+ if (pci_dev) {
+ ret = comedi_pci_enable(pci_dev, DIO200_DRIVER_NAME);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "comedi%d: error! cannot enable PCI device and request regions!\n",
+ dev->minor);
+ return ret;
+ }
+ iobase = pci_resource_start(pci_dev, 2);
+ irq = pci_dev->irq;
+ } else
+#endif
+ {
+ ret = dio200_request_region(dev->minor, iobase, DIO200_IO_SIZE);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ dev->iobase = iobase;
+
+ layout = thislayout;
+ if ((ret = alloc_subdevices(dev, layout->n_subdevs)) < 0) {
+ printk(KERN_ERR "comedi%d: error! out of memory!\n",
+ dev->minor);
+ return ret;
+ }
+
+ for (n = 0; n < dev->n_subdevices; n++) {
+ s = &dev->subdevices[n];
+ switch (layout->sdtype[n]) {
+ case sd_8254:
+ /* counter subdevice (8254) */
+ ret = dio200_subdev_8254_init(dev, s, iobase,
+ layout->sdinfo[n], layout->has_clk_gat_sce);
+ if (ret < 0) {
+ return ret;
+ }
+ break;
+ case sd_8255:
+ /* digital i/o subdevice (8255) */
+ ret = subdev_8255_init(dev, s, 0,
+ iobase + layout->sdinfo[n]);
+ if (ret < 0) {
+ return ret;
+ }
+ break;
+ case sd_intr:
+ /* 'INTERRUPT' subdevice */
+ if (irq) {
+ ret = dio200_subdev_intr_init(dev, s,
+ iobase + DIO200_INT_SCE,
+ layout->sdinfo[n], layout->has_int_sce);
+ if (ret < 0) {
+ return ret;
+ }
+ devpriv->intr_sd = n;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+ break;
+ default:
+ s->type = COMEDI_SUBD_UNUSED;
+ break;
+ }
+ }
+
+ sdx = devpriv->intr_sd;
+ if (sdx >= 0 && sdx < dev->n_subdevices) {
+ dev->read_subdev = &dev->subdevices[sdx];
+ }
+
+ dev->board_name = thisboard->name;
+
+ if (irq) {
+ unsigned long flags = share_irq ? IRQF_SHARED : 0;
+
+ if (comedi_request_irq(irq, dio200_interrupt, flags,
+ DIO200_DRIVER_NAME, dev) >= 0) {
+ dev->irq = irq;
+ } else {
+ printk(KERN_WARNING
+ "comedi%d: warning! irq %u unavailable!\n",
+ dev->minor, irq);
+ }
+ }
+
+ printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
+ if (thisboard->bustype == isa_bustype) {
+ printk("(base %#lx) ", iobase);
+ } else {
+#ifdef CONFIG_COMEDI_PCI
+ printk("(pci %s) ", pci_name(pci_dev));
+#endif
+ }
+ if (irq) {
+ printk("(irq %u%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));
+ } else {
+ printk("(no irq) ");
+ }
+
+ printk("attached\n");
+
+ return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int dio200_detach(struct comedi_device * dev)
+{
+ const struct dio200_layout_struct *layout;
+ unsigned n;
+
+ printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
+ DIO200_DRIVER_NAME);
+
+ if (dev->irq) {
+ comedi_free_irq(dev->irq, dev);
+ }
+ if (dev->subdevices) {
+ layout = thislayout;
+ for (n = 0; n < dev->n_subdevices; n++) {
+ struct comedi_subdevice *s = &dev->subdevices[n];
+ switch (layout->sdtype[n]) {
+ case sd_8254:
+ dio200_subdev_8254_cleanup(dev, s);
+ break;
+ case sd_8255:
+ subdev_8255_cleanup(dev, s);
+ break;
+ case sd_intr:
+ dio200_subdev_intr_cleanup(dev, s);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (devpriv) {
+#ifdef CONFIG_COMEDI_PCI
+ if (devpriv->pci_dev) {
+ if (dev->iobase) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ } else
+#endif
+ {
+ if (dev->iobase) {
+ release_region(dev->iobase, DIO200_IO_SIZE);
+ }
+ }
+ }
+ if (dev->board_name) {
+ printk(KERN_INFO "comedi%d: %s removed\n",
+ dev->minor, dev->board_name);
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/amplc_pc236.c b/drivers/staging/comedi/drivers/amplc_pc236.c
new file mode 100644
index 000000000000..2027c75feaca
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_pc236.c
@@ -0,0 +1,654 @@
+/*
+ comedi/drivers/amplc_pc236.c
+ Driver for Amplicon PC36AT and PCI236 DIO boards.
+
+ Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: amplc_pc236
+Description: Amplicon PC36AT, PCI236
+Author: Ian Abbott <abbotti@mev.co.uk>
+Devices: [Amplicon] PC36AT (pc36at), PCI236 (pci236 or amplc_pc236)
+Updated: Wed, 22 Oct 2008 13:40:03 +0100
+Status: works
+
+Configuration options - PC36AT:
+ [0] - I/O port base address
+ [1] - IRQ (optional)
+
+Configuration options - PCI236:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first available PCI device will be
+ used.
+
+The PC36AT ISA board and PCI236 PCI board have a single 8255 appearing
+as subdevice 0.
+
+Subdevice 1 pretends to be a digital input device, but it always returns
+0 when read. However, if you run a command with scan_begin_src=TRIG_EXT,
+a rising edge on port C bit 7 acts as an external trigger, which can be
+used to wake up tasks. This is like the comedi_parport device, but the
+only way to physically disable the interrupt on the PC36AT is to remove
+the IRQ jumper. If no interrupt is connected, then subdevice 1 is
+unused.
+*/
+
+#include "../comedidev.h"
+
+#include "comedi_pci.h"
+
+#include "8255.h"
+#include "plx9052.h"
+
+#define PC236_DRIVER_NAME "amplc_pc236"
+
+/* PCI236 PCI configuration register information */
+#define PCI_VENDOR_ID_AMPLICON 0x14dc
+#define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
+#define PCI_DEVICE_ID_INVALID 0xffff
+
+/* PC36AT / PCI236 registers */
+
+#define PC236_IO_SIZE 4
+#define PC236_LCR_IO_SIZE 128
+
+/*
+ * INTCSR values for PCI236.
+ */
+/* Disable interrupt, also clear any interrupt there */
+#define PCI236_INTR_DISABLE ( PLX9052_INTCSR_LI1ENAB_DISABLED \
+ | PLX9052_INTCSR_LI1POL_HIGH \
+ | PLX9052_INTCSR_LI2POL_HIGH \
+ | PLX9052_INTCSR_PCIENAB_DISABLED \
+ | PLX9052_INTCSR_LI1SEL_EDGE \
+ | PLX9052_INTCSR_LI1CLRINT_ASSERTED )
+/* Enable interrupt, also clear any interrupt there. */
+#define PCI236_INTR_ENABLE ( PLX9052_INTCSR_LI1ENAB_ENABLED \
+ | PLX9052_INTCSR_LI1POL_HIGH \
+ | PLX9052_INTCSR_LI2POL_HIGH \
+ | PLX9052_INTCSR_PCIENAB_ENABLED \
+ | PLX9052_INTCSR_LI1SEL_EDGE \
+ | PLX9052_INTCSR_LI1CLRINT_ASSERTED )
+
+/*
+ * Board descriptions for Amplicon PC36AT and PCI236.
+ */
+
+enum pc236_bustype { isa_bustype, pci_bustype };
+enum pc236_model { pc36at_model, pci236_model, anypci_model };
+
+struct pc236_board {
+ const char *name;
+ const char *fancy_name;
+ unsigned short devid;
+ enum pc236_bustype bustype;
+ enum pc236_model model;
+};
+static const struct pc236_board pc236_boards[] = {
+ {
+ name: "pc36at",
+ fancy_name:"PC36AT",
+ bustype: isa_bustype,
+ model: pc36at_model,
+ },
+#ifdef CONFIG_COMEDI_PCI
+ {
+ name: "pci236",
+ fancy_name:"PCI236",
+ devid: PCI_DEVICE_ID_AMPLICON_PCI236,
+ bustype: pci_bustype,
+ model: pci236_model,
+ },
+#endif
+#ifdef CONFIG_COMEDI_PCI
+ {
+ name: PC236_DRIVER_NAME,
+ fancy_name:PC236_DRIVER_NAME,
+ devid: PCI_DEVICE_ID_INVALID,
+ bustype: pci_bustype,
+ model: anypci_model, /* wildcard */
+ },
+#endif
+};
+
+#ifdef CONFIG_COMEDI_PCI
+static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
+ {PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236, PCI_ANY_ID,
+ PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, pc236_pci_table);
+#endif /* CONFIG_COMEDI_PCI */
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct pc236_board *)dev->board_ptr)
+
+/* 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 struct comedi_device struct. */
+struct pc236_private {
+#ifdef CONFIG_COMEDI_PCI
+ /* PCI device */
+ struct pci_dev *pci_dev;
+ unsigned long lcr_iobase; /* PLX PCI9052 config registers in PCIBAR1 */
+#endif
+ int enable_irq;
+};
+
+#define devpriv ((struct pc236_private *)dev->private)
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int pc236_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pc236_detach(struct comedi_device * dev);
+static struct comedi_driver driver_amplc_pc236 = {
+ driver_name:PC236_DRIVER_NAME,
+ module:THIS_MODULE,
+ attach:pc236_attach,
+ detach:pc236_detach,
+ board_name:&pc236_boards[0].name,
+ offset:sizeof(struct pc236_board),
+ num_names:sizeof(pc236_boards) / sizeof(struct pc236_board),
+};
+
+#ifdef CONFIG_COMEDI_PCI
+COMEDI_PCI_INITCLEANUP(driver_amplc_pc236, pc236_pci_table);
+#else
+COMEDI_INITCLEANUP(driver_amplc_pc236);
+#endif
+
+static int pc236_request_region(unsigned minor, unsigned long from,
+ unsigned long extent);
+static void pc236_intr_disable(struct comedi_device * dev);
+static void pc236_intr_enable(struct comedi_device * dev);
+static int pc236_intr_check(struct comedi_device * dev);
+static int pc236_intr_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int pc236_intr_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int pc236_intr_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int pc236_intr_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static irqreturn_t pc236_interrupt(int irq, void *d PT_REGS_ARG);
+
+/*
+ * This function looks for a PCI device matching the requested board name,
+ * bus and slot.
+ */
+#ifdef CONFIG_COMEDI_PCI
+static int
+pc236_find_pci(struct comedi_device * dev, int bus, int slot,
+ struct pci_dev **pci_dev_p)
+{
+ struct pci_dev *pci_dev = NULL;
+
+ *pci_dev_p = NULL;
+
+ /* Look for matching PCI device. */
+ for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
+ pci_dev != NULL;
+ pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
+ PCI_ANY_ID, pci_dev)) {
+ /* If bus/slot specified, check them. */
+ if (bus || slot) {
+ if (bus != pci_dev->bus->number
+ || slot != PCI_SLOT(pci_dev->devfn))
+ continue;
+ }
+ if (thisboard->model == anypci_model) {
+ /* Match any supported model. */
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pc236_boards); i++) {
+ if (pc236_boards[i].bustype != pci_bustype)
+ continue;
+ if (pci_dev->device == pc236_boards[i].devid) {
+ /* Change board_ptr to matched board. */
+ dev->board_ptr = &pc236_boards[i];
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(pc236_boards))
+ continue;
+ } else {
+ /* Match specific model name. */
+ if (pci_dev->device != thisboard->devid)
+ continue;
+ }
+
+ /* Found a match. */
+ *pci_dev_p = pci_dev;
+ return 0;
+ }
+ /* No match found. */
+ if (bus || slot) {
+ printk(KERN_ERR
+ "comedi%d: error! no %s found at pci %02x:%02x!\n",
+ dev->minor, thisboard->name, bus, slot);
+ } else {
+ printk(KERN_ERR "comedi%d: error! no %s found!\n",
+ dev->minor, thisboard->name);
+ }
+ return -EIO;
+}
+#endif
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int pc236_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase = 0;
+ unsigned int irq = 0;
+#ifdef CONFIG_COMEDI_PCI
+ struct pci_dev *pci_dev = NULL;
+ int bus = 0, slot = 0;
+#endif
+ int share_irq = 0;
+ int ret;
+
+ printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
+ PC236_DRIVER_NAME);
+/*
+ * Allocate the private structure area. alloc_private() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if ((ret = alloc_private(dev, sizeof(struct pc236_private))) < 0) {
+ printk(KERN_ERR "comedi%d: error! out of memory!\n",
+ dev->minor);
+ return ret;
+ }
+ /* Process options. */
+ switch (thisboard->bustype) {
+ case isa_bustype:
+ iobase = it->options[0];
+ irq = it->options[1];
+ share_irq = 0;
+ break;
+#ifdef CONFIG_COMEDI_PCI
+ case pci_bustype:
+ bus = it->options[0];
+ slot = it->options[1];
+ share_irq = 1;
+
+ if ((ret = pc236_find_pci(dev, bus, slot, &pci_dev)) < 0)
+ return ret;
+ devpriv->pci_dev = pci_dev;
+ break;
+#endif /* CONFIG_COMEDI_PCI */
+ default:
+ printk(KERN_ERR
+ "comedi%d: %s: BUG! cannot determine board type!\n",
+ dev->minor, PC236_DRIVER_NAME);
+ return -EINVAL;
+ break;
+ }
+
+/*
+ * Initialize dev->board_name.
+ */
+ dev->board_name = thisboard->name;
+
+ /* Enable device and reserve I/O spaces. */
+#ifdef CONFIG_COMEDI_PCI
+ if (pci_dev) {
+ if ((ret = comedi_pci_enable(pci_dev, PC236_DRIVER_NAME)) < 0) {
+ printk(KERN_ERR
+ "comedi%d: error! cannot enable PCI device and request regions!\n",
+ dev->minor);
+ return ret;
+ }
+ devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
+ iobase = pci_resource_start(pci_dev, 2);
+ irq = pci_dev->irq;
+ } else
+#endif
+ {
+ ret = pc236_request_region(dev->minor, iobase, PC236_IO_SIZE);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ dev->iobase = iobase;
+
+/*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if ((ret = alloc_subdevices(dev, 2)) < 0) {
+ printk(KERN_ERR "comedi%d: error! out of memory!\n",
+ dev->minor);
+ return ret;
+ }
+
+ s = dev->subdevices + 0;
+ /* digital i/o subdevice (8255) */
+ if ((ret = subdev_8255_init(dev, s, NULL, iobase)) < 0) {
+ printk(KERN_ERR "comedi%d: error! out of memory!\n",
+ dev->minor);
+ return ret;
+ }
+ s = dev->subdevices + 1;
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_UNUSED;
+ pc236_intr_disable(dev);
+ if (irq) {
+ unsigned long flags = share_irq ? IRQF_SHARED : 0;
+
+ if (comedi_request_irq(irq, pc236_interrupt, flags,
+ PC236_DRIVER_NAME, dev) >= 0) {
+ dev->irq = irq;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+ s->n_chan = 1;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pc236_intr_insn;
+ s->do_cmdtest = pc236_intr_cmdtest;
+ s->do_cmd = pc236_intr_cmd;
+ s->cancel = pc236_intr_cancel;
+ }
+ }
+ printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
+ if (thisboard->bustype == isa_bustype) {
+ printk("(base %#lx) ", iobase);
+ } else {
+#ifdef CONFIG_COMEDI_PCI
+ printk("(pci %s) ", pci_name(pci_dev));
+#endif
+ }
+ if (irq) {
+ printk("(irq %u%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));
+ } else {
+ printk("(no irq) ");
+ }
+
+ printk("attached\n");
+
+ return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int pc236_detach(struct comedi_device * dev)
+{
+ printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
+ PC236_DRIVER_NAME);
+ if (devpriv) {
+ pc236_intr_disable(dev);
+ }
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+ if (dev->subdevices) {
+ subdev_8255_cleanup(dev, dev->subdevices + 0);
+ }
+ if (devpriv) {
+#ifdef CONFIG_COMEDI_PCI
+ if (devpriv->pci_dev) {
+ if (dev->iobase) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ } else
+#endif
+ {
+ if (dev->iobase) {
+ release_region(dev->iobase, PC236_IO_SIZE);
+ }
+ }
+ }
+ if (dev->board_name) {
+ printk(KERN_INFO "comedi%d: %s removed\n",
+ dev->minor, dev->board_name);
+ }
+ return 0;
+}
+
+/*
+ * This function checks and requests an I/O region, reporting an error
+ * if there is a conflict.
+ */
+static int pc236_request_region(unsigned minor, unsigned long from,
+ unsigned long extent)
+{
+ if (!from || !request_region(from, extent, PC236_DRIVER_NAME)) {
+ printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
+ minor, from, extent);
+ return -EIO;
+ }
+ return 0;
+}
+
+/*
+ * This function is called to mark the interrupt as disabled (no command
+ * configured on subdevice 1) and to physically disable the interrupt
+ * (not possible on the PC36AT, except by removing the IRQ jumper!).
+ */
+static void pc236_intr_disable(struct comedi_device * dev)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->enable_irq = 0;
+#ifdef CONFIG_COMEDI_PCI
+ if (devpriv->lcr_iobase)
+ outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
+#endif
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+/*
+ * This function is called to mark the interrupt as enabled (a command
+ * configured on subdevice 1) and to physically enable the interrupt
+ * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!).
+ */
+static void pc236_intr_enable(struct comedi_device * dev)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->enable_irq = 1;
+#ifdef CONFIG_COMEDI_PCI
+ if (devpriv->lcr_iobase)
+ outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
+#endif
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+/*
+ * This function is called when an interrupt occurs to check whether
+ * the interrupt has been marked as enabled and was generated by the
+ * board. If so, the function prepares the hardware for the next
+ * interrupt.
+ * Returns 0 if the interrupt should be ignored.
+ */
+static int pc236_intr_check(struct comedi_device * dev)
+{
+ int retval = 0;
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ if (devpriv->enable_irq) {
+ retval = 1;
+#ifdef CONFIG_COMEDI_PCI
+ if (devpriv->lcr_iobase) {
+ if ((inl(devpriv->lcr_iobase + PLX9052_INTCSR)
+ & PLX9052_INTCSR_LI1STAT_MASK)
+ == PLX9052_INTCSR_LI1STAT_INACTIVE) {
+ retval = 0;
+ } else {
+ /* Clear interrupt and keep it enabled. */
+ outl(PCI236_INTR_ENABLE,
+ devpriv->lcr_iobase + PLX9052_INTCSR);
+ }
+ }
+#endif
+ }
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return retval;
+}
+
+/*
+ * Input from subdevice 1.
+ * Copied from the comedi_parport driver.
+ */
+static int pc236_intr_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[1] = 0;
+ return 2;
+}
+
+/*
+ * Subdevice 1 command test.
+ * Copied from the comedi_parport driver.
+ */
+static int pc236_intr_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* step 1 */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_FOLLOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: ignored */
+
+ if (err)
+ return 2;
+
+ /* step 3: */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ if (cmd->scan_end_arg != 1) {
+ cmd->scan_end_arg = 1;
+ err++;
+ }
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: ignored */
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+/*
+ * Subdevice 1 command.
+ */
+static int pc236_intr_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ pc236_intr_enable(dev);
+
+ return 0;
+}
+
+/*
+ * Subdevice 1 cancel command.
+ */
+static int pc236_intr_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ pc236_intr_disable(dev);
+
+ return 0;
+}
+
+/*
+ * Interrupt service routine.
+ * Based on the comedi_parport driver.
+ */
+static irqreturn_t pc236_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 1;
+ int handled;
+
+ handled = pc236_intr_check(dev);
+ if (dev->attached && handled) {
+ comedi_buf_put(s->async, 0);
+ s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
+ comedi_event(dev, s);
+ }
+ return IRQ_RETVAL(handled);
+}
diff --git a/drivers/staging/comedi/drivers/amplc_pc263.c b/drivers/staging/comedi/drivers/amplc_pc263.c
new file mode 100644
index 000000000000..97ac4157adaf
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_pc263.c
@@ -0,0 +1,431 @@
+/*
+ comedi/drivers/amplc_pc263.c
+ Driver for Amplicon PC263 and PCI263 relay boards.
+
+ Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: amplc_pc263
+Description: Amplicon PC263, PCI263
+Author: Ian Abbott <abbotti@mev.co.uk>
+Devices: [Amplicon] PC263 (pc263), PCI263 (pci263 or amplc_pc263)
+Updated: Wed, 22 Oct 2008 14:10:53 +0100
+Status: works
+
+Configuration options - PC263:
+ [0] - I/O port base address
+
+Configuration options - PCI263:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first available PCI device will be
+ used.
+
+Each board appears as one subdevice, with 16 digital outputs, each
+connected to a reed-relay. Relay contacts are closed when output is 1.
+The state of the outputs can be read.
+*/
+
+#include "../comedidev.h"
+
+#include "comedi_pci.h"
+
+#define PC263_DRIVER_NAME "amplc_pc263"
+
+/* PCI263 PCI configuration register information */
+#define PCI_VENDOR_ID_AMPLICON 0x14dc
+#define PCI_DEVICE_ID_AMPLICON_PCI263 0x000c
+#define PCI_DEVICE_ID_INVALID 0xffff
+
+/* PC263 / PCI263 registers */
+#define PC263_IO_SIZE 2
+
+/*
+ * Board descriptions for Amplicon PC263 / PCI263.
+ */
+
+enum pc263_bustype { isa_bustype, pci_bustype };
+enum pc263_model { pc263_model, pci263_model, anypci_model };
+
+struct pc263_board {
+ const char *name;
+ const char *fancy_name;
+ unsigned short devid;
+ enum pc263_bustype bustype;
+ enum pc263_model model;
+};
+static const struct pc263_board pc263_boards[] = {
+ {
+ name: "pc263",
+ fancy_name:"PC263",
+ bustype: isa_bustype,
+ model: pc263_model,
+ },
+#ifdef CONFIG_COMEDI_PCI
+ {
+ name: "pci263",
+ fancy_name:"PCI263",
+ devid: PCI_DEVICE_ID_AMPLICON_PCI263,
+ bustype: pci_bustype,
+ model: pci263_model,
+ },
+#endif
+#ifdef CONFIG_COMEDI_PCI
+ {
+ name: PC263_DRIVER_NAME,
+ fancy_name:PC263_DRIVER_NAME,
+ devid: PCI_DEVICE_ID_INVALID,
+ bustype: pci_bustype,
+ model: anypci_model, /* wildcard */
+ },
+#endif
+};
+
+#ifdef CONFIG_COMEDI_PCI
+static DEFINE_PCI_DEVICE_TABLE(pc263_pci_table) = {
+ {PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI263, PCI_ANY_ID,
+ PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, pc263_pci_table);
+#endif /* CONFIG_COMEDI_PCI */
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct pc263_board *)dev->board_ptr)
+
+/* 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 struct comedi_device struct. */
+#ifdef CONFIG_COMEDI_PCI
+struct pc263_private {
+ /* PCI device. */
+ struct pci_dev *pci_dev;
+};
+
+#define devpriv ((struct pc263_private *)dev->private)
+#endif /* CONFIG_COMEDI_PCI */
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int pc263_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pc263_detach(struct comedi_device * dev);
+static struct comedi_driver driver_amplc_pc263 = {
+ driver_name:PC263_DRIVER_NAME,
+ module:THIS_MODULE,
+ attach:pc263_attach,
+ detach:pc263_detach,
+ board_name:&pc263_boards[0].name,
+ offset:sizeof(struct pc263_board),
+ num_names:sizeof(pc263_boards) / sizeof(struct pc263_board),
+};
+
+static int pc263_request_region(unsigned minor, unsigned long from,
+ unsigned long extent);
+static int pc263_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int pc263_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+/*
+ * This function looks for a PCI device matching the requested board name,
+ * bus and slot.
+ */
+#ifdef CONFIG_COMEDI_PCI
+static int
+pc263_find_pci(struct comedi_device * dev, int bus, int slot,
+ struct pci_dev **pci_dev_p)
+{
+ struct pci_dev *pci_dev = NULL;
+
+ *pci_dev_p = NULL;
+
+ /* Look for matching PCI device. */
+ for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
+ pci_dev != NULL;
+ pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
+ PCI_ANY_ID, pci_dev)) {
+ /* If bus/slot specified, check them. */
+ if (bus || slot) {
+ if (bus != pci_dev->bus->number
+ || slot != PCI_SLOT(pci_dev->devfn))
+ continue;
+ }
+ if (thisboard->model == anypci_model) {
+ /* Match any supported model. */
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pc263_boards); i++) {
+ if (pc263_boards[i].bustype != pci_bustype)
+ continue;
+ if (pci_dev->device == pc263_boards[i].devid) {
+ /* Change board_ptr to matched board. */
+ dev->board_ptr = &pc263_boards[i];
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(pc263_boards))
+ continue;
+ } else {
+ /* Match specific model name. */
+ if (pci_dev->device != thisboard->devid)
+ continue;
+ }
+
+ /* Found a match. */
+ *pci_dev_p = pci_dev;
+ return 0;
+ }
+ /* No match found. */
+ if (bus || slot) {
+ printk(KERN_ERR
+ "comedi%d: error! no %s found at pci %02x:%02x!\n",
+ dev->minor, thisboard->name, bus, slot);
+ } else {
+ printk(KERN_ERR "comedi%d: error! no %s found!\n",
+ dev->minor, thisboard->name);
+ }
+ return -EIO;
+}
+#endif
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int pc263_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase = 0;
+#ifdef CONFIG_COMEDI_PCI
+ struct pci_dev *pci_dev = NULL;
+ int bus = 0, slot = 0;
+#endif
+ int ret;
+
+ printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
+ PC263_DRIVER_NAME);
+/*
+ * Allocate the private structure area. alloc_private() is a
+ * convenient macro defined in comedidev.h.
+ */
+#ifdef CONFIG_COMEDI_PCI
+ if ((ret = alloc_private(dev, sizeof(struct pc263_private))) < 0) {
+ printk(KERN_ERR "comedi%d: error! out of memory!\n",
+ dev->minor);
+ return ret;
+ }
+#endif
+ /* Process options. */
+ switch (thisboard->bustype) {
+ case isa_bustype:
+ iobase = it->options[0];
+ break;
+#ifdef CONFIG_COMEDI_PCI
+ case pci_bustype:
+ bus = it->options[0];
+ slot = it->options[1];
+
+ if ((ret = pc263_find_pci(dev, bus, slot, &pci_dev)) < 0)
+ return ret;
+ devpriv->pci_dev = pci_dev;
+ break;
+#endif /* CONFIG_COMEDI_PCI */
+ default:
+ printk(KERN_ERR
+ "comedi%d: %s: BUG! cannot determine board type!\n",
+ dev->minor, PC263_DRIVER_NAME);
+ return -EINVAL;
+ break;
+ }
+
+/*
+ * Initialize dev->board_name.
+ */
+ dev->board_name = thisboard->name;
+
+ /* Enable device and reserve I/O spaces. */
+#ifdef CONFIG_COMEDI_PCI
+ if (pci_dev) {
+ if ((ret = comedi_pci_enable(pci_dev, PC263_DRIVER_NAME)) < 0) {
+ printk(KERN_ERR
+ "comedi%d: error! cannot enable PCI device and request regions!\n",
+ dev->minor);
+ return ret;
+ }
+ iobase = pci_resource_start(pci_dev, 2);
+ } else
+#endif
+ {
+ ret = pc263_request_region(dev->minor, iobase, PC263_IO_SIZE);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ dev->iobase = iobase;
+
+/*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if ((ret = alloc_subdevices(dev, 1)) < 0) {
+ printk(KERN_ERR "comedi%d: error! out of memory!\n",
+ dev->minor);
+ return ret;
+ }
+
+ s = dev->subdevices + 0;
+ /* digital i/o subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_RT;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pc263_dio_insn_bits;
+ s->insn_config = pc263_dio_insn_config;
+ /* all outputs */
+ s->io_bits = 0xffff;
+ /* read initial relay state */
+ s->state = inb(dev->iobase);
+ s->state = s->state | (inb(dev->iobase) << 8);
+
+ printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
+ if (thisboard->bustype == isa_bustype) {
+ printk("(base %#lx) ", iobase);
+ } else {
+#ifdef CONFIG_COMEDI_PCI
+ printk("(pci %s) ", pci_name(pci_dev));
+#endif
+ }
+
+ printk("attached\n");
+
+ return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int pc263_detach(struct comedi_device * dev)
+{
+ printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
+ PC263_DRIVER_NAME);
+
+#ifdef CONFIG_COMEDI_PCI
+ if (devpriv)
+#endif
+ {
+#ifdef CONFIG_COMEDI_PCI
+ if (devpriv->pci_dev) {
+ if (dev->iobase) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ } else
+#endif
+ {
+ if (dev->iobase) {
+ release_region(dev->iobase, PC263_IO_SIZE);
+ }
+ }
+ }
+ if (dev->board_name) {
+ printk(KERN_INFO "comedi%d: %s removed\n",
+ dev->minor, dev->board_name);
+ }
+ return 0;
+}
+
+/*
+ * This function checks and requests an I/O region, reporting an error
+ * if there is a conflict.
+ */
+static int pc263_request_region(unsigned minor, unsigned long from,
+ unsigned long extent)
+{
+ if (!from || !request_region(from, extent, PC263_DRIVER_NAME)) {
+ printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
+ minor, from, extent);
+ return -EIO;
+ }
+ return 0;
+}
+
+/* DIO devices are slightly special. Although it is possible to
+ * implement the insn_read/insn_write interface, it is much more
+ * useful to applications if you implement the insn_bits interface.
+ * This allows packed reading/writing of the DIO channels. The
+ * comedi core can convert between insn_bits and insn_read/write */
+static int pc263_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ /* The insn data is a mask in data[0] and the new data
+ * in data[1], each channel cooresponding to a bit. */
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+ /* Write out the new digital output lines */
+ outb(s->state & 0xFF, dev->iobase);
+ outb(s->state >> 8, dev->iobase + 1);
+ }
+
+ /* on return, data[1] contains the value of the digital
+ * input and output lines. */
+ /* or we could just return the software copy of the output values if
+ * it was a purely digital output subdevice */
+ data[1] = s->state;
+
+ return 2;
+}
+
+static int pc263_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 1)
+ return -EINVAL;
+ return 1;
+}
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+#ifdef CONFIG_COMEDI_PCI
+COMEDI_PCI_INITCLEANUP(driver_amplc_pc263, pc263_pci_table);
+#else
+COMEDI_INITCLEANUP(driver_amplc_pc263);
+#endif
diff --git a/drivers/staging/comedi/drivers/amplc_pci224.c b/drivers/staging/comedi/drivers/amplc_pci224.c
new file mode 100644
index 000000000000..770b96648932
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_pci224.c
@@ -0,0 +1,1545 @@
+/*
+ comedi/drivers/amplc_pci224.c
+ Driver for Amplicon PCI224 and PCI234 AO boards.
+
+ Copyright (C) 2005 MEV Ltd. <http://www.mev.co.uk/>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: amplc_pci224
+Description: Amplicon PCI224, PCI234
+Author: Ian Abbott <abbotti@mev.co.uk>
+Devices: [Amplicon] PCI224 (amplc_pci224 or pci224),
+ PCI234 (amplc_pci224 or pci234)
+Updated: Wed, 22 Oct 2008 12:25:08 +0100
+Status: works, but see caveats
+
+Supports:
+
+ - ao_insn read/write
+ - ao_do_cmd mode with the following sources:
+
+ - start_src TRIG_INT TRIG_EXT
+ - scan_begin_src TRIG_TIMER TRIG_EXT
+ - convert_src TRIG_NOW
+ - scan_end_src TRIG_COUNT
+ - stop_src TRIG_COUNT TRIG_EXT TRIG_NONE
+
+ The channel list must contain at least one channel with no repeated
+ channels. The scan end count must equal the number of channels in
+ the channel list.
+
+ There is only one external trigger source so only one of start_src,
+ scan_begin_src or stop_src may use TRIG_EXT.
+
+Configuration options - PCI224:
+ [0] - PCI bus of device (optional).
+ [1] - PCI slot of device (optional).
+ If bus/slot is not specified, the first available PCI device
+ will be used.
+ [2] - Select available ranges according to jumper LK1. All channels
+ are set to the same range:
+ 0=Jumper position 1-2 (factory default), 4 software-selectable
+ internal voltage references, giving 4 bipolar and 4 unipolar
+ ranges:
+ [-10V,+10V], [-5V,+5V], [-2.5V,+2.5V], [-1.25V,+1.25V],
+ [0,+10V], [0,+5V], [0,+2.5V], [0,1.25V].
+ 1=Jumper position 2-3, 1 external voltage reference, giving
+ 1 bipolar and 1 unipolar range:
+ [-Vext,+Vext], [0,+Vext].
+
+Configuration options - PCI234:
+ [0] - PCI bus of device (optional).
+ [1] - PCI slot of device (optional).
+ If bus/slot is not specified, the first available PCI device
+ will be used.
+ [2] - Select internal or external voltage reference according to
+ jumper LK1. This affects all channels:
+ 0=Jumper position 1-2 (factory default), Vref=5V internal.
+ 1=Jumper position 2-3, Vref=Vext external.
+ [3] - Select channel 0 range according to jumper LK2:
+ 0=Jumper position 2-3 (factory default), range [-2*Vref,+2*Vref]
+ (10V bipolar when options[2]=0).
+ 1=Jumper position 1-2, range [-Vref,+Vref]
+ (5V bipolar when options[2]=0).
+ [4] - Select channel 1 range according to jumper LK3: cf. options[3].
+ [5] - Select channel 2 range according to jumper LK4: cf. options[3].
+ [6] - Select channel 3 range according to jumper LK5: cf. options[3].
+
+Passing a zero for an option is the same as leaving it unspecified.
+
+Caveats:
+
+ 1) All channels on the PCI224 share the same range. Any change to the
+ range as a result of insn_write or a streaming command will affect
+ the output voltages of all channels, including those not specified
+ by the instruction or command.
+
+ 2) For the analog output command, the first scan may be triggered
+ falsely at the start of acquisition. This occurs when the DAC scan
+ trigger source is switched from 'none' to 'timer' (scan_begin_src =
+ TRIG_TIMER) or 'external' (scan_begin_src == TRIG_EXT) at the start
+ of acquisition and the trigger source is at logic level 1 at the
+ time of the switch. This is very likely for TRIG_TIMER. For
+ TRIG_EXT, it depends on the state of the external line and whether
+ the CR_INVERT flag has been set. The remaining scans are triggered
+ correctly.
+*/
+
+#include "../comedidev.h"
+
+#include "comedi_pci.h"
+
+#include "comedi_fc.h"
+#include "8253.h"
+
+#define DRIVER_NAME "amplc_pci224"
+
+/*
+ * PCI IDs.
+ */
+/* #define PCI_VENDOR_ID_AMPLICON 0x14dc */
+#define PCI_DEVICE_ID_AMPLICON_PCI224 0x0007
+#define PCI_DEVICE_ID_AMPLICON_PCI234 0x0008
+#define PCI_DEVICE_ID_INVALID 0xffff
+
+/*
+ * PCI224/234 i/o space 1 (PCIBAR2) registers.
+ */
+#define PCI224_IO1_SIZE 0x20 /* Size of i/o space 1 (8-bit registers) */
+#define PCI224_Z2_CT0 0x14 /* 82C54 counter/timer 0 */
+#define PCI224_Z2_CT1 0x15 /* 82C54 counter/timer 1 */
+#define PCI224_Z2_CT2 0x16 /* 82C54 counter/timer 2 */
+#define PCI224_Z2_CTC 0x17 /* 82C54 counter/timer control word */
+#define PCI224_ZCLK_SCE 0x1A /* Group Z Clock Configuration Register */
+#define PCI224_ZGAT_SCE 0x1D /* Group Z Gate Configuration Register */
+#define PCI224_INT_SCE 0x1E /* ISR Interrupt source mask register */
+ /* /Interrupt status */
+
+/*
+ * PCI224/234 i/o space 2 (PCIBAR3) 16-bit registers.
+ */
+#define PCI224_IO2_SIZE 0x10 /* Size of i/o space 2 (16-bit registers). */
+#define PCI224_DACDATA 0x00 /* (w-o) DAC FIFO data. */
+#define PCI224_SOFTTRIG 0x00 /* (r-o) DAC software scan trigger. */
+#define PCI224_DACCON 0x02 /* (r/w) DAC status/configuration. */
+#define PCI224_FIFOSIZ 0x04 /* (w-o) FIFO size for wraparound mode. */
+#define PCI224_DACCEN 0x06 /* (w-o) DAC channel enable register. */
+
+/*
+ * DACCON values.
+ */
+/* (r/w) Scan trigger. */
+#define PCI224_DACCON_TRIG_MASK (7 << 0)
+#define PCI224_DACCON_TRIG_NONE (0 << 0) /* none */
+#define PCI224_DACCON_TRIG_SW (1 << 0) /* software trig */
+#define PCI224_DACCON_TRIG_EXTP (2 << 0) /* ext +ve edge */
+#define PCI224_DACCON_TRIG_EXTN (3 << 0) /* ext -ve edge */
+#define PCI224_DACCON_TRIG_Z2CT0 (4 << 0) /* Z2 CT0 out */
+#define PCI224_DACCON_TRIG_Z2CT1 (5 << 0) /* Z2 CT1 out */
+#define PCI224_DACCON_TRIG_Z2CT2 (6 << 0) /* Z2 CT2 out */
+/* (r/w) Polarity (PCI224 only, PCI234 always bipolar!). */
+#define PCI224_DACCON_POLAR_MASK (1 << 3)
+#define PCI224_DACCON_POLAR_UNI (0 << 3) /* range [0,Vref] */
+#define PCI224_DACCON_POLAR_BI (1 << 3) /* range [-Vref,Vref] */
+/* (r/w) Internal Vref (PCI224 only, when LK1 in position 1-2). */
+#define PCI224_DACCON_VREF_MASK (3 << 4)
+#define PCI224_DACCON_VREF_1_25 (0 << 4) /* Vref = 1.25V */
+#define PCI224_DACCON_VREF_2_5 (1 << 4) /* Vref = 2.5V */
+#define PCI224_DACCON_VREF_5 (2 << 4) /* Vref = 5V */
+#define PCI224_DACCON_VREF_10 (3 << 4) /* Vref = 10V */
+/* (r/w) Wraparound mode enable (to play back stored waveform). */
+#define PCI224_DACCON_FIFOWRAP (1 << 7)
+/* (r/w) FIFO enable. It MUST be set! */
+#define PCI224_DACCON_FIFOENAB (1 << 8)
+/* (r/w) FIFO interrupt trigger level (most values are not very useful). */
+#define PCI224_DACCON_FIFOINTR_MASK (7 << 9)
+#define PCI224_DACCON_FIFOINTR_EMPTY (0 << 9) /* when empty */
+#define PCI224_DACCON_FIFOINTR_NEMPTY (1 << 9) /* when not empty */
+#define PCI224_DACCON_FIFOINTR_NHALF (2 << 9) /* when not half full */
+#define PCI224_DACCON_FIFOINTR_HALF (3 << 9) /* when half full */
+#define PCI224_DACCON_FIFOINTR_NFULL (4 << 9) /* when not full */
+#define PCI224_DACCON_FIFOINTR_FULL (5 << 9) /* when full */
+/* (r-o) FIFO fill level. */
+#define PCI224_DACCON_FIFOFL_MASK (7 << 12)
+#define PCI224_DACCON_FIFOFL_EMPTY (1 << 12) /* 0 */
+#define PCI224_DACCON_FIFOFL_ONETOHALF (0 << 12) /* [1,2048] */
+#define PCI224_DACCON_FIFOFL_HALFTOFULL (4 << 12) /* [2049,4095] */
+#define PCI224_DACCON_FIFOFL_FULL (6 << 12) /* 4096 */
+/* (r-o) DAC busy flag. */
+#define PCI224_DACCON_BUSY (1 << 15)
+/* (w-o) FIFO reset. */
+#define PCI224_DACCON_FIFORESET (1 << 12)
+/* (w-o) Global reset (not sure what it does). */
+#define PCI224_DACCON_GLOBALRESET (1 << 13)
+
+/*
+ * DAC FIFO size.
+ */
+#define PCI224_FIFO_SIZE 4096
+
+/*
+ * DAC FIFO guaranteed minimum room available, depending on reported fill level.
+ * The maximum room available depends on the reported fill level and how much
+ * has been written!
+ */
+#define PCI224_FIFO_ROOM_EMPTY PCI224_FIFO_SIZE
+#define PCI224_FIFO_ROOM_ONETOHALF (PCI224_FIFO_SIZE / 2)
+#define PCI224_FIFO_ROOM_HALFTOFULL 1
+#define PCI224_FIFO_ROOM_FULL 0
+
+/*
+ * Counter/timer clock input configuration sources.
+ */
+#define CLK_CLK 0 /* reserved (channel-specific clock) */
+#define CLK_10MHZ 1 /* internal 10 MHz clock */
+#define CLK_1MHZ 2 /* internal 1 MHz clock */
+#define CLK_100KHZ 3 /* internal 100 kHz clock */
+#define CLK_10KHZ 4 /* internal 10 kHz clock */
+#define CLK_1KHZ 5 /* internal 1 kHz clock */
+#define CLK_OUTNM1 6 /* output of channel-1 modulo total */
+#define CLK_EXT 7 /* external clock */
+/* Macro to construct clock input configuration register value. */
+#define CLK_CONFIG(chan, src) ((((chan) & 3) << 3) | ((src) & 7))
+/* Timebases in ns. */
+#define TIMEBASE_10MHZ 100
+#define TIMEBASE_1MHZ 1000
+#define TIMEBASE_100KHZ 10000
+#define TIMEBASE_10KHZ 100000
+#define TIMEBASE_1KHZ 1000000
+
+/*
+ * Counter/timer gate input configuration sources.
+ */
+#define GAT_VCC 0 /* VCC (i.e. enabled) */
+#define GAT_GND 1 /* GND (i.e. disabled) */
+#define GAT_EXT 2 /* reserved (external gate input) */
+#define GAT_NOUTNM2 3 /* inverted output of channel-2 modulo total */
+/* Macro to construct gate input configuration register value. */
+#define GAT_CONFIG(chan, src) ((((chan) & 3) << 3) | ((src) & 7))
+
+/*
+ * Summary of CLK_OUTNM1 and GAT_NOUTNM2 connections for PCI224 and PCI234:
+ *
+ * Channel's Channel's
+ * clock input gate input
+ * Channel CLK_OUTNM1 GAT_NOUTNM2
+ * ------- ---------- -----------
+ * Z2-CT0 Z2-CT2-OUT /Z2-CT1-OUT
+ * Z2-CT1 Z2-CT0-OUT /Z2-CT2-OUT
+ * Z2-CT2 Z2-CT1-OUT /Z2-CT0-OUT
+ */
+
+/*
+ * Interrupt enable/status bits
+ */
+#define PCI224_INTR_EXT 0x01 /* rising edge on external input */
+#define PCI224_INTR_DAC 0x04 /* DAC (FIFO) interrupt */
+#define PCI224_INTR_Z2CT1 0x20 /* rising edge on Z2-CT1 output */
+
+#define PCI224_INTR_EDGE_BITS (PCI224_INTR_EXT | PCI224_INTR_Z2CT1)
+#define PCI224_INTR_LEVEL_BITS PCI224_INTR_DACFIFO
+
+/*
+ * Handy macros.
+ */
+
+/* Combine old and new bits. */
+#define COMBINE(old, new, mask) (((old) & ~(mask)) | ((new) & (mask)))
+
+/* A generic null function pointer value. */
+#define NULLFUNC 0
+
+/* Current CPU. XXX should this be hard_smp_processor_id()? */
+#define THISCPU smp_processor_id()
+
+/* State bits for use with atomic bit operations. */
+#define AO_CMD_STARTED 0
+
+/*
+ * Range tables.
+ */
+
+/* The software selectable internal ranges for PCI224 (option[2] == 0). */
+static const struct comedi_lrange range_pci224_internal = {
+ 8,
+ {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ }
+};
+
+static const unsigned short hwrange_pci224_internal[8] = {
+ PCI224_DACCON_POLAR_BI | PCI224_DACCON_VREF_10,
+ PCI224_DACCON_POLAR_BI | PCI224_DACCON_VREF_5,
+ PCI224_DACCON_POLAR_BI | PCI224_DACCON_VREF_2_5,
+ PCI224_DACCON_POLAR_BI | PCI224_DACCON_VREF_1_25,
+ PCI224_DACCON_POLAR_UNI | PCI224_DACCON_VREF_10,
+ PCI224_DACCON_POLAR_UNI | PCI224_DACCON_VREF_5,
+ PCI224_DACCON_POLAR_UNI | PCI224_DACCON_VREF_2_5,
+ PCI224_DACCON_POLAR_UNI | PCI224_DACCON_VREF_1_25,
+};
+
+/* The software selectable external ranges for PCI224 (option[2] == 1). */
+static const struct comedi_lrange range_pci224_external = {
+ 2,
+ {
+ RANGE_ext(-1, 1), /* bipolar [-Vref,+Vref] */
+ RANGE_ext(0, 1), /* unipolar [0,+Vref] */
+ }
+};
+
+static const unsigned short hwrange_pci224_external[2] = {
+ PCI224_DACCON_POLAR_BI,
+ PCI224_DACCON_POLAR_UNI,
+};
+
+/* The hardware selectable Vref*2 external range for PCI234
+ * (option[2] == 1, option[3+n] == 0). */
+static const struct comedi_lrange range_pci234_ext2 = {
+ 1,
+ {
+ RANGE_ext(-2, 2),
+ }
+};
+
+/* The hardware selectable Vref external range for PCI234
+ * (option[2] == 1, option[3+n] == 1). */
+static const struct comedi_lrange range_pci234_ext = {
+ 1,
+ {
+ RANGE_ext(-1, 1),
+ }
+};
+
+/* This serves for all the PCI234 ranges. */
+static const unsigned short hwrange_pci234[1] = {
+ PCI224_DACCON_POLAR_BI, /* bipolar - hardware ignores it! */
+};
+
+/*
+ * Board descriptions.
+ */
+
+enum pci224_model { any_model, pci224_model, pci234_model };
+
+struct pci224_board {
+ const char *name;
+ unsigned short devid;
+ enum pci224_model model;
+ unsigned int ao_chans;
+ unsigned int ao_bits;
+};
+
+static const struct pci224_board pci224_boards[] = {
+ {
+ name: "pci224",
+ devid: PCI_DEVICE_ID_AMPLICON_PCI224,
+ model: pci224_model,
+ ao_chans:16,
+ ao_bits: 12,
+ },
+ {
+ name: "pci234",
+ devid: PCI_DEVICE_ID_AMPLICON_PCI234,
+ model: pci234_model,
+ ao_chans:4,
+ ao_bits: 16,
+ },
+ {
+ name: DRIVER_NAME,
+ devid: PCI_DEVICE_ID_INVALID,
+ model: any_model, /* wildcard */
+ },
+};
+
+/*
+ * PCI driver table.
+ */
+
+static DEFINE_PCI_DEVICE_TABLE(pci224_pci_table) = {
+ {PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI224,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI234,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, pci224_pci_table);
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((struct pci224_board *)dev->board_ptr)
+
+/* 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 struct comedi_device struct. */
+struct pci224_private {
+ struct pci_dev *pci_dev; /* PCI device */
+ const unsigned short *hwrange;
+ unsigned long iobase1;
+ unsigned long state;
+ spinlock_t ao_spinlock;
+ unsigned int *ao_readback;
+ short *ao_scan_vals;
+ unsigned char *ao_scan_order;
+ int intr_cpuid;
+ short intr_running;
+ unsigned short daccon;
+ unsigned int cached_div1;
+ unsigned int cached_div2;
+ unsigned int ao_stop_count;
+ short ao_stop_continuous;
+ unsigned short ao_enab; /* max 16 channels so 'short' will do */
+ unsigned char intsce;
+};
+
+#define devpriv ((struct pci224_private *)dev->private)
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int pci224_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pci224_detach(struct comedi_device * dev);
+static struct comedi_driver driver_amplc_pci224 = {
+ driver_name:DRIVER_NAME,
+ module:THIS_MODULE,
+ attach:pci224_attach,
+ detach:pci224_detach,
+ board_name:&pci224_boards[0].name,
+ offset:sizeof(struct pci224_board),
+ num_names:sizeof(pci224_boards) / sizeof(struct pci224_board),
+};
+
+COMEDI_PCI_INITCLEANUP(driver_amplc_pci224, pci224_pci_table);
+
+/*
+ * Called from the 'insn_write' function to perform a single write.
+ */
+static void
+pci224_ao_set_data(struct comedi_device * dev, int chan, int range, unsigned int data)
+{
+ unsigned short mangled;
+
+ /* Store unmangled data for readback. */
+ devpriv->ao_readback[chan] = data;
+ /* Enable the channel. */
+ outw(1 << chan, dev->iobase + PCI224_DACCEN);
+ /* Set range and reset FIFO. */
+ devpriv->daccon = COMBINE(devpriv->daccon, devpriv->hwrange[range],
+ (PCI224_DACCON_POLAR_MASK | PCI224_DACCON_VREF_MASK));
+ outw(devpriv->daccon | PCI224_DACCON_FIFORESET,
+ dev->iobase + PCI224_DACCON);
+ /*
+ * Mangle the data. The hardware expects:
+ * - bipolar: 16-bit 2's complement
+ * - unipolar: 16-bit unsigned
+ */
+ mangled = (unsigned short)data << (16 - thisboard->ao_bits);
+ if ((devpriv->daccon & PCI224_DACCON_POLAR_MASK) ==
+ PCI224_DACCON_POLAR_BI) {
+ mangled ^= 0x8000;
+ }
+ /* Write mangled data to the FIFO. */
+ outw(mangled, dev->iobase + PCI224_DACDATA);
+ /* Trigger the conversion. */
+ inw(dev->iobase + PCI224_SOFTTRIG);
+}
+
+/*
+ * 'insn_write' function for AO subdevice.
+ */
+static int
+pci224_ao_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan, range;
+
+ /* Unpack channel and range. */
+ chan = CR_CHAN(insn->chanspec);
+ range = CR_RANGE(insn->chanspec);
+
+ /* Writing a list of values to an AO channel is probably not
+ * very useful, but that's how the interface is defined. */
+ for (i = 0; i < insn->n; i++) {
+ pci224_ao_set_data(dev, chan, range, data[i]);
+ }
+ return i;
+}
+
+/*
+ * 'insn_read' function for AO subdevice.
+ *
+ * N.B. The value read will not be valid if the DAC channel has
+ * never been written successfully since the device was attached
+ * or since the channel has been used by an AO streaming write
+ * command.
+ */
+static int
+pci224_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan;
+
+ chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++) {
+ data[i] = devpriv->ao_readback[chan];
+ }
+
+ return i;
+}
+
+/*
+ * Just a wrapper for the inline function 'i8253_cascade_ns_to_timer'.
+ */
+static void
+pci224_cascade_ns_to_timer(int osc_base, unsigned int *d1, unsigned int *d2,
+ unsigned int *nanosec, int round_mode)
+{
+ i8253_cascade_ns_to_timer(osc_base, d1, d2, nanosec, round_mode);
+}
+
+/*
+ * Kills a command running on the AO subdevice.
+ */
+static void pci224_ao_stop(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long flags;
+
+ if (!test_and_clear_bit(AO_CMD_STARTED, &devpriv->state)) {
+ return;
+ }
+
+ comedi_spin_lock_irqsave(&devpriv->ao_spinlock, flags);
+ /* Kill the interrupts. */
+ devpriv->intsce = 0;
+ outb(0, devpriv->iobase1 + PCI224_INT_SCE);
+ /*
+ * Interrupt routine may or may not be running. We may or may not
+ * have been called from the interrupt routine (directly or
+ * indirectly via a comedi_events() callback routine). It's highly
+ * unlikely that we've been called from some other interrupt routine
+ * but who knows what strange things coders get up to!
+ *
+ * If the interrupt routine is currently running, wait for it to
+ * finish, unless we appear to have been called via the interrupt
+ * routine.
+ */
+ while (devpriv->intr_running && devpriv->intr_cpuid != THISCPU) {
+ comedi_spin_unlock_irqrestore(&devpriv->ao_spinlock, flags);
+ comedi_spin_lock_irqsave(&devpriv->ao_spinlock, flags);
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->ao_spinlock, flags);
+ /* Reconfigure DAC for insn_write usage. */
+ outw(0, dev->iobase + PCI224_DACCEN); /* Disable channels. */
+ devpriv->daccon = COMBINE(devpriv->daccon,
+ PCI224_DACCON_TRIG_SW | PCI224_DACCON_FIFOINTR_EMPTY,
+ PCI224_DACCON_TRIG_MASK | PCI224_DACCON_FIFOINTR_MASK);
+ outw(devpriv->daccon | PCI224_DACCON_FIFORESET,
+ dev->iobase + PCI224_DACCON);
+}
+
+/*
+ * Handles start of acquisition for the AO subdevice.
+ */
+static void pci224_ao_start(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned long flags;
+
+ set_bit(AO_CMD_STARTED, &devpriv->state);
+ if (!devpriv->ao_stop_continuous && devpriv->ao_stop_count == 0) {
+ /* An empty acquisition! */
+ pci224_ao_stop(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ } else {
+ /* Enable interrupts. */
+ comedi_spin_lock_irqsave(&devpriv->ao_spinlock, flags);
+ if (cmd->stop_src == TRIG_EXT) {
+ devpriv->intsce = PCI224_INTR_EXT | PCI224_INTR_DAC;
+ } else {
+ devpriv->intsce = PCI224_INTR_DAC;
+ }
+ outb(devpriv->intsce, devpriv->iobase1 + PCI224_INT_SCE);
+ comedi_spin_unlock_irqrestore(&devpriv->ao_spinlock, flags);
+ }
+}
+
+/*
+ * Handles interrupts from the DAC FIFO.
+ */
+static void pci224_ao_handle_fifo(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int num_scans;
+ unsigned int room;
+ unsigned short dacstat;
+ unsigned int i, n;
+ unsigned int bytes_per_scan;
+
+ if (cmd->chanlist_len) {
+ bytes_per_scan = cmd->chanlist_len * sizeof(short);
+ } else {
+ /* Shouldn't get here! */
+ bytes_per_scan = sizeof(short);
+ }
+ /* Determine number of scans available in buffer. */
+ num_scans = comedi_buf_read_n_available(s->async) / bytes_per_scan;
+ if (!devpriv->ao_stop_continuous) {
+ /* Fixed number of scans. */
+ if (num_scans > devpriv->ao_stop_count) {
+ num_scans = devpriv->ao_stop_count;
+ }
+ }
+
+ /* Determine how much room is in the FIFO (in samples). */
+ dacstat = inw(dev->iobase + PCI224_DACCON);
+ switch (dacstat & PCI224_DACCON_FIFOFL_MASK) {
+ case PCI224_DACCON_FIFOFL_EMPTY:
+ room = PCI224_FIFO_ROOM_EMPTY;
+ if (!devpriv->ao_stop_continuous
+ && devpriv->ao_stop_count == 0) {
+ /* FIFO empty at end of counted acquisition. */
+ pci224_ao_stop(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ return;
+ }
+ break;
+ case PCI224_DACCON_FIFOFL_ONETOHALF:
+ room = PCI224_FIFO_ROOM_ONETOHALF;
+ break;
+ case PCI224_DACCON_FIFOFL_HALFTOFULL:
+ room = PCI224_FIFO_ROOM_HALFTOFULL;
+ break;
+ default:
+ room = PCI224_FIFO_ROOM_FULL;
+ break;
+ }
+ if (room >= PCI224_FIFO_ROOM_ONETOHALF) {
+ /* FIFO is less than half-full. */
+ if (num_scans == 0) {
+ /* Nothing left to put in the FIFO. */
+ pci224_ao_stop(dev, s);
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ rt_printk(KERN_ERR "comedi%d: "
+ "AO buffer underrun\n", dev->minor);
+ }
+ }
+ /* Determine how many new scans can be put in the FIFO. */
+ if (cmd->chanlist_len) {
+ room /= cmd->chanlist_len;
+ }
+ /* Determine how many scans to process. */
+ if (num_scans > room) {
+ num_scans = room;
+ }
+ /* Process scans. */
+ for (n = 0; n < num_scans; n++) {
+ cfc_read_array_from_buffer(s, &devpriv->ao_scan_vals[0],
+ bytes_per_scan);
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ outw(devpriv->ao_scan_vals[devpriv->
+ ao_scan_order[i]],
+ dev->iobase + PCI224_DACDATA);
+ }
+ }
+ if (!devpriv->ao_stop_continuous) {
+ devpriv->ao_stop_count -= num_scans;
+ if (devpriv->ao_stop_count == 0) {
+ /*
+ * Change FIFO interrupt trigger level to wait
+ * until FIFO is empty.
+ */
+ devpriv->daccon = COMBINE(devpriv->daccon,
+ PCI224_DACCON_FIFOINTR_EMPTY,
+ PCI224_DACCON_FIFOINTR_MASK);
+ outw(devpriv->daccon,
+ dev->iobase + PCI224_DACCON);
+ }
+ }
+ if ((devpriv->daccon & PCI224_DACCON_TRIG_MASK) ==
+ PCI224_DACCON_TRIG_NONE) {
+ unsigned short trig;
+
+ /*
+ * This is the initial DAC FIFO interrupt at the
+ * start of the acquisition. The DAC's scan trigger
+ * has been set to 'none' up until now.
+ *
+ * Now that data has been written to the FIFO, the
+ * DAC's scan trigger source can be set to the
+ * correct value.
+ *
+ * BUG: The first scan will be triggered immediately
+ * if the scan trigger source is at logic level 1.
+ */
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ trig = PCI224_DACCON_TRIG_Z2CT0;
+ } else {
+ /* cmd->scan_begin_src == TRIG_EXT */
+ if (cmd->scan_begin_arg & CR_INVERT) {
+ trig = PCI224_DACCON_TRIG_EXTN;
+ } else {
+ trig = PCI224_DACCON_TRIG_EXTP;
+ }
+ }
+ devpriv->daccon = COMBINE(devpriv->daccon, trig,
+ PCI224_DACCON_TRIG_MASK);
+ outw(devpriv->daccon, dev->iobase + PCI224_DACCON);
+ }
+ if (s->async->events) {
+ comedi_event(dev, s);
+ }
+}
+
+/*
+ * Internal trigger function to start acquisition on AO subdevice.
+ */
+static int
+pci224_ao_inttrig_start(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trignum)
+{
+ if (trignum != 0)
+ return -EINVAL;
+
+ s->async->inttrig = NULLFUNC;
+ pci224_ao_start(dev, s);
+
+ return 1;
+}
+
+#define MAX_SCAN_PERIOD 0xFFFFFFFFU
+#define MIN_SCAN_PERIOD 2500
+#define CONVERT_PERIOD 625
+
+/*
+ * 'do_cmdtest' function for AO subdevice.
+ */
+static int
+pci224_ao_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s, struct comedi_cmd * cmd)
+{
+ int err = 0;
+ unsigned int tmp;
+
+ /* Step 1: make sure trigger sources are trivially valid. */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_INT | TRIG_EXT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_EXT | TRIG_TIMER;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_EXT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* Step 2: make sure trigger sources are unique and mutually
+ * compatible. */
+
+ /* these tests are true if more than one _src bit is set */
+ if ((cmd->start_src & (cmd->start_src - 1)) != 0)
+ err++;
+ if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
+ err++;
+ if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
+ err++;
+ if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
+ err++;
+ if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
+ err++;
+
+ /* There's only one external trigger signal (which makes these
+ * tests easier). Only one thing can use it. */
+ tmp = 0;
+ if (cmd->start_src & TRIG_EXT)
+ tmp++;
+ if (cmd->scan_begin_src & TRIG_EXT)
+ tmp++;
+ if (cmd->stop_src & TRIG_EXT)
+ tmp++;
+ if (tmp > 1)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* Step 3: make sure arguments are trivially compatible. */
+
+ switch (cmd->start_src) {
+ case TRIG_INT:
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ break;
+ case TRIG_EXT:
+ /* Force to external trigger 0. */
+ if ((cmd->start_arg & ~CR_FLAGS_MASK) != 0) {
+ cmd->start_arg = COMBINE(cmd->start_arg, 0,
+ ~CR_FLAGS_MASK);
+ err++;
+ }
+ /* The only flag allowed is CR_EDGE, which is ignored. */
+ if ((cmd->start_arg & CR_FLAGS_MASK & ~CR_EDGE) != 0) {
+ cmd->start_arg = COMBINE(cmd->start_arg, 0,
+ CR_FLAGS_MASK & ~CR_EDGE);
+ err++;
+ }
+ break;
+ }
+
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ if (cmd->scan_begin_arg > MAX_SCAN_PERIOD) {
+ cmd->scan_begin_arg = MAX_SCAN_PERIOD;
+ err++;
+ }
+ tmp = cmd->chanlist_len * CONVERT_PERIOD;
+ if (tmp < MIN_SCAN_PERIOD) {
+ tmp = MIN_SCAN_PERIOD;
+ }
+ if (cmd->scan_begin_arg < tmp) {
+ cmd->scan_begin_arg = tmp;
+ err++;
+ }
+ break;
+ case TRIG_EXT:
+ /* Force to external trigger 0. */
+ if ((cmd->scan_begin_arg & ~CR_FLAGS_MASK) != 0) {
+ cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0,
+ ~CR_FLAGS_MASK);
+ err++;
+ }
+ /* Only allow flags CR_EDGE and CR_INVERT. Ignore CR_EDGE. */
+ if ((cmd->scan_begin_arg & CR_FLAGS_MASK &
+ ~(CR_EDGE | CR_INVERT)) != 0) {
+ cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0,
+ CR_FLAGS_MASK & ~(CR_EDGE | CR_INVERT));
+ err++;
+ }
+ break;
+ }
+
+ /* cmd->convert_src == TRIG_NOW */
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+
+ /* cmd->scan_end_arg == TRIG_COUNT */
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ /* Any count allowed. */
+ break;
+ case TRIG_EXT:
+ /* Force to external trigger 0. */
+ if ((cmd->stop_arg & ~CR_FLAGS_MASK) != 0) {
+ cmd->stop_arg = COMBINE(cmd->stop_arg, 0,
+ ~CR_FLAGS_MASK);
+ err++;
+ }
+ /* The only flag allowed is CR_EDGE, which is ignored. */
+ if ((cmd->stop_arg & CR_FLAGS_MASK & ~CR_EDGE) != 0) {
+ cmd->stop_arg = COMBINE(cmd->stop_arg, 0,
+ CR_FLAGS_MASK & ~CR_EDGE);
+ }
+ break;
+ case TRIG_NONE:
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ break;
+ }
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments. */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ unsigned int div1, div2, round;
+ int round_mode = cmd->flags & TRIG_ROUND_MASK;
+
+ tmp = cmd->scan_begin_arg;
+ /* Check whether to use a single timer. */
+ switch (round_mode) {
+ case TRIG_ROUND_NEAREST:
+ default:
+ round = TIMEBASE_10MHZ / 2;
+ break;
+ case TRIG_ROUND_DOWN:
+ round = 0;
+ break;
+ case TRIG_ROUND_UP:
+ round = TIMEBASE_10MHZ - 1;
+ break;
+ }
+ /* Be careful to avoid overflow! */
+ div2 = cmd->scan_begin_arg / TIMEBASE_10MHZ;
+ div2 += (round + cmd->scan_begin_arg % TIMEBASE_10MHZ) /
+ TIMEBASE_10MHZ;
+ if (div2 <= 0x10000) {
+ /* A single timer will suffice. */
+ if (div2 < 2)
+ div2 = 2;
+ cmd->scan_begin_arg = div2 * TIMEBASE_10MHZ;
+ if (cmd->scan_begin_arg < div2 ||
+ cmd->scan_begin_arg < TIMEBASE_10MHZ) {
+ /* Overflow! */
+ cmd->scan_begin_arg = MAX_SCAN_PERIOD;
+ }
+ } else {
+ /* Use two timers. */
+ div1 = devpriv->cached_div1;
+ div2 = devpriv->cached_div2;
+ pci224_cascade_ns_to_timer(TIMEBASE_10MHZ, &div1, &div2,
+ &cmd->scan_begin_arg, round_mode);
+ devpriv->cached_div1 = div1;
+ devpriv->cached_div2 = div2;
+ }
+ if (tmp != cmd->scan_begin_arg) {
+ err++;
+ }
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list. */
+
+ if (cmd->chanlist && (cmd->chanlist_len > 0)) {
+ unsigned int range;
+ enum { range_err = 1, dupchan_err = 2, };
+ unsigned errors;
+ unsigned int n;
+ unsigned int ch;
+
+ /*
+ * Check all channels have the same range index. Don't care
+ * about analogue reference, as we can't configure it.
+ *
+ * Check the list has no duplicate channels.
+ */
+ range = CR_RANGE(cmd->chanlist[0]);
+ errors = 0;
+ tmp = 0;
+ for (n = 0; n < cmd->chanlist_len; n++) {
+ ch = CR_CHAN(cmd->chanlist[n]);
+ if (tmp & (1U << ch)) {
+ errors |= dupchan_err;
+ }
+ tmp |= (1U << ch);
+ if (CR_RANGE(cmd->chanlist[n]) != range) {
+ errors |= range_err;
+ }
+ }
+ if (errors) {
+ if (errors & dupchan_err) {
+ DPRINTK("comedi%d: " DRIVER_NAME
+ ": ao_cmdtest: "
+ "entries in chanlist must contain no "
+ "duplicate channels\n", dev->minor);
+ }
+ if (errors & range_err) {
+ DPRINTK("comedi%d: " DRIVER_NAME
+ ": ao_cmdtest: "
+ "entries in chanlist must all have "
+ "the same range index\n", dev->minor);
+ }
+ err++;
+ }
+ }
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+/*
+ * 'do_cmd' function for AO subdevice.
+ */
+static int pci224_ao_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int range;
+ unsigned int i, j;
+ unsigned int ch;
+ unsigned int rank;
+ unsigned long flags;
+
+ /* Cannot handle null/empty chanlist. */
+ if (cmd->chanlist == NULL || cmd->chanlist_len == 0) {
+ return -EINVAL;
+ }
+
+ /* Determine which channels are enabled and their load order. */
+ devpriv->ao_enab = 0;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ ch = CR_CHAN(cmd->chanlist[i]);
+ devpriv->ao_enab |= 1U << ch;
+ rank = 0;
+ for (j = 0; j < cmd->chanlist_len; j++) {
+ if (CR_CHAN(cmd->chanlist[j]) < ch) {
+ rank++;
+ }
+ }
+ devpriv->ao_scan_order[rank] = i;
+ }
+
+ /* Set enabled channels. */
+ outw(devpriv->ao_enab, dev->iobase + PCI224_DACCEN);
+
+ /* Determine range and polarity. All channels the same. */
+ range = CR_RANGE(cmd->chanlist[0]);
+
+ /*
+ * Set DAC range and polarity.
+ * Set DAC scan trigger source to 'none'.
+ * Set DAC FIFO interrupt trigger level to 'not half full'.
+ * Reset DAC FIFO.
+ *
+ * N.B. DAC FIFO interrupts are currently disabled.
+ */
+ devpriv->daccon = COMBINE(devpriv->daccon,
+ (devpriv->hwrange[range] | PCI224_DACCON_TRIG_NONE |
+ PCI224_DACCON_FIFOINTR_NHALF),
+ (PCI224_DACCON_POLAR_MASK | PCI224_DACCON_VREF_MASK |
+ PCI224_DACCON_TRIG_MASK | PCI224_DACCON_FIFOINTR_MASK));
+ outw(devpriv->daccon | PCI224_DACCON_FIFORESET,
+ dev->iobase + PCI224_DACCON);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ unsigned int div1, div2, round;
+ unsigned int ns = cmd->scan_begin_arg;
+ int round_mode = cmd->flags & TRIG_ROUND_MASK;
+
+ /* Check whether to use a single timer. */
+ switch (round_mode) {
+ case TRIG_ROUND_NEAREST:
+ default:
+ round = TIMEBASE_10MHZ / 2;
+ break;
+ case TRIG_ROUND_DOWN:
+ round = 0;
+ break;
+ case TRIG_ROUND_UP:
+ round = TIMEBASE_10MHZ - 1;
+ break;
+ }
+ /* Be careful to avoid overflow! */
+ div2 = cmd->scan_begin_arg / TIMEBASE_10MHZ;
+ div2 += (round + cmd->scan_begin_arg % TIMEBASE_10MHZ) /
+ TIMEBASE_10MHZ;
+ if (div2 <= 0x10000) {
+ /* A single timer will suffice. */
+ if (div2 < 2)
+ div2 = 2;
+ div2 &= 0xffff;
+ div1 = 1; /* Flag that single timer to be used. */
+ } else {
+ /* Use two timers. */
+ div1 = devpriv->cached_div1;
+ div2 = devpriv->cached_div2;
+ pci224_cascade_ns_to_timer(TIMEBASE_10MHZ, &div1, &div2,
+ &ns, round_mode);
+ }
+
+ /*
+ * The output of timer Z2-0 will be used as the scan trigger
+ * source.
+ */
+ /* Make sure Z2-0 is gated on. */
+ outb(GAT_CONFIG(0, GAT_VCC),
+ devpriv->iobase1 + PCI224_ZGAT_SCE);
+ if (div1 == 1) {
+ /* Not cascading. Z2-0 needs 10 MHz clock. */
+ outb(CLK_CONFIG(0, CLK_10MHZ),
+ devpriv->iobase1 + PCI224_ZCLK_SCE);
+ } else {
+ /* Cascading with Z2-2. */
+ /* Make sure Z2-2 is gated on. */
+ outb(GAT_CONFIG(2, GAT_VCC),
+ devpriv->iobase1 + PCI224_ZGAT_SCE);
+ /* Z2-2 needs 10 MHz clock. */
+ outb(CLK_CONFIG(2, CLK_10MHZ),
+ devpriv->iobase1 + PCI224_ZCLK_SCE);
+ /* Load Z2-2 mode (2) and counter (div1). */
+ i8254_load(devpriv->iobase1 + PCI224_Z2_CT0, 0,
+ 2, div1, 2);
+ /* Z2-0 is clocked from Z2-2's output. */
+ outb(CLK_CONFIG(0, CLK_OUTNM1),
+ devpriv->iobase1 + PCI224_ZCLK_SCE);
+ }
+ /* Load Z2-0 mode (2) and counter (div2). */
+ i8254_load(devpriv->iobase1 + PCI224_Z2_CT0, 0, 0, div2, 2);
+ }
+
+ /*
+ * Sort out end of acquisition.
+ */
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ /* Fixed number of scans. */
+ devpriv->ao_stop_continuous = 0;
+ devpriv->ao_stop_count = cmd->stop_arg;
+ break;
+ default:
+ /* Continuous scans. */
+ devpriv->ao_stop_continuous = 1;
+ devpriv->ao_stop_count = 0;
+ break;
+ }
+
+ /*
+ * Sort out start of acquisition.
+ */
+ switch (cmd->start_src) {
+ case TRIG_INT:
+ comedi_spin_lock_irqsave(&devpriv->ao_spinlock, flags);
+ s->async->inttrig = &pci224_ao_inttrig_start;
+ comedi_spin_unlock_irqrestore(&devpriv->ao_spinlock, flags);
+ break;
+ case TRIG_EXT:
+ /* Enable external interrupt trigger to start acquisition. */
+ comedi_spin_lock_irqsave(&devpriv->ao_spinlock, flags);
+ devpriv->intsce |= PCI224_INTR_EXT;
+ outb(devpriv->intsce, devpriv->iobase1 + PCI224_INT_SCE);
+ comedi_spin_unlock_irqrestore(&devpriv->ao_spinlock, flags);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * 'cancel' function for AO subdevice.
+ */
+static int pci224_ao_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ pci224_ao_stop(dev, s);
+ return 0;
+}
+
+/*
+ * 'munge' data for AO command.
+ */
+static void
+pci224_ao_munge(struct comedi_device * dev, struct comedi_subdevice * s, void *data,
+ unsigned int num_bytes, unsigned int chan_index)
+{
+ struct comedi_async *async = s->async;
+ short *array = data;
+ unsigned int length = num_bytes / sizeof(*array);
+ unsigned int offset;
+ unsigned int shift;
+ unsigned int i;
+
+ /* The hardware expects 16-bit numbers. */
+ shift = 16 - thisboard->ao_bits;
+ /* Channels will be all bipolar or all unipolar. */
+ if ((devpriv->hwrange[CR_RANGE(async->cmd.chanlist[0])] &
+ PCI224_DACCON_POLAR_MASK) == PCI224_DACCON_POLAR_UNI) {
+ /* Unipolar */
+ offset = 0;
+ } else {
+ /* Bipolar */
+ offset = 32768;
+ }
+ /* Munge the data. */
+ for (i = 0; i < length; i++) {
+ array[i] = (array[i] << shift) - offset;
+ }
+}
+
+/*
+ * Interrupt handler.
+ */
+static irqreturn_t pci224_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = &dev->subdevices[0];
+ struct comedi_cmd *cmd;
+ unsigned char intstat, valid_intstat;
+ unsigned char curenab;
+ int retval = 0;
+ unsigned long flags;
+
+ intstat = inb(devpriv->iobase1 + PCI224_INT_SCE) & 0x3F;
+ if (intstat) {
+ retval = 1;
+ comedi_spin_lock_irqsave(&devpriv->ao_spinlock, flags);
+ valid_intstat = devpriv->intsce & intstat;
+ /* Temporarily disable interrupt sources. */
+ curenab = devpriv->intsce & ~intstat;
+ outb(curenab, devpriv->iobase1 + PCI224_INT_SCE);
+ devpriv->intr_running = 1;
+ devpriv->intr_cpuid = THISCPU;
+ comedi_spin_unlock_irqrestore(&devpriv->ao_spinlock, flags);
+ if (valid_intstat != 0) {
+ cmd = &s->async->cmd;
+ if (valid_intstat & PCI224_INTR_EXT) {
+ devpriv->intsce &= ~PCI224_INTR_EXT;
+ if (cmd->start_src == TRIG_EXT) {
+ pci224_ao_start(dev, s);
+ } else if (cmd->stop_src == TRIG_EXT) {
+ pci224_ao_stop(dev, s);
+ }
+ }
+ if (valid_intstat & PCI224_INTR_DAC) {
+ pci224_ao_handle_fifo(dev, s);
+ }
+ }
+ /* Reenable interrupt sources. */
+ comedi_spin_lock_irqsave(&devpriv->ao_spinlock, flags);
+ if (curenab != devpriv->intsce) {
+ outb(devpriv->intsce,
+ devpriv->iobase1 + PCI224_INT_SCE);
+ }
+ devpriv->intr_running = 0;
+ comedi_spin_unlock_irqrestore(&devpriv->ao_spinlock, flags);
+ }
+ return IRQ_RETVAL(retval);
+}
+
+/*
+ * This function looks for a PCI device matching the requested board name,
+ * bus and slot.
+ */
+static int
+pci224_find_pci(struct comedi_device * dev, int bus, int slot,
+ struct pci_dev **pci_dev_p)
+{
+ struct pci_dev *pci_dev = NULL;
+
+ *pci_dev_p = NULL;
+
+ /* Look for matching PCI device. */
+ for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
+ pci_dev != NULL;
+ pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID,
+ pci_dev)) {
+ /* If bus/slot specified, check them. */
+ if (bus || slot) {
+ if (bus != pci_dev->bus->number
+ || slot != PCI_SLOT(pci_dev->devfn))
+ continue;
+ }
+ if (thisboard->model == any_model) {
+ /* Match any supported model. */
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pci224_boards); i++) {
+ if (pci_dev->device == pci224_boards[i].devid) {
+ /* Change board_ptr to matched board. */
+ dev->board_ptr = &pci224_boards[i];
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(pci224_boards))
+ continue;
+ } else {
+ /* Match specific model name. */
+ if (thisboard->devid != pci_dev->device)
+ continue;
+ }
+
+ /* Found a match. */
+ *pci_dev_p = pci_dev;
+ return 0;
+ }
+ /* No match found. */
+ if (bus || slot) {
+ printk(KERN_ERR "comedi%d: error! "
+ "no %s found at pci %02x:%02x!\n",
+ dev->minor, thisboard->name, bus, slot);
+ } else {
+ printk(KERN_ERR "comedi%d: error! no %s found!\n",
+ dev->minor, thisboard->name);
+ }
+ return -EIO;
+}
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int pci224_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ struct pci_dev *pci_dev;
+ unsigned int irq;
+ int bus = 0, slot = 0;
+ unsigned n;
+ int ret;
+
+ printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor, DRIVER_NAME);
+
+ bus = it->options[0];
+ slot = it->options[1];
+ if ((ret = alloc_private(dev, sizeof(struct pci224_private))) < 0) {
+ printk(KERN_ERR "comedi%d: error! out of memory!\n",
+ dev->minor);
+ return ret;
+ }
+ if ((ret = pci224_find_pci(dev, bus, slot, &pci_dev)) < 0)
+ return ret;
+ devpriv->pci_dev = pci_dev;
+
+ if ((ret = comedi_pci_enable(pci_dev, DRIVER_NAME)) < 0) {
+ printk(KERN_ERR
+ "comedi%d: error! cannot enable PCI device "
+ "and request regions!\n", dev->minor);
+ return ret;
+ }
+ spin_lock_init(&devpriv->ao_spinlock);
+
+ devpriv->iobase1 = pci_resource_start(pci_dev, 2);
+ dev->iobase = pci_resource_start(pci_dev, 3);
+ irq = pci_dev->irq;
+
+ /* Allocate readback buffer for AO channels. */
+ devpriv->ao_readback = kmalloc(sizeof(devpriv->ao_readback[0]) *
+ thisboard->ao_chans, GFP_KERNEL);
+ if (!devpriv->ao_readback) {
+ return -ENOMEM;
+ }
+
+ /* Allocate buffer to hold values for AO channel scan. */
+ devpriv->ao_scan_vals = kmalloc(sizeof(devpriv->ao_scan_vals[0]) *
+ thisboard->ao_chans, GFP_KERNEL);
+ if (!devpriv->ao_scan_vals) {
+ return -ENOMEM;
+ }
+
+ /* Allocate buffer to hold AO channel scan order. */
+ devpriv->ao_scan_order = kmalloc(sizeof(devpriv->ao_scan_order[0]) *
+ thisboard->ao_chans, GFP_KERNEL);
+ if (!devpriv->ao_scan_order) {
+ return -ENOMEM;
+ }
+
+ /* Disable interrupt sources. */
+ devpriv->intsce = 0;
+ outb(0, devpriv->iobase1 + PCI224_INT_SCE);
+
+ /* Initialize the DAC hardware. */
+ outw(PCI224_DACCON_GLOBALRESET, dev->iobase + PCI224_DACCON);
+ outw(0, dev->iobase + PCI224_DACCEN);
+ outw(0, dev->iobase + PCI224_FIFOSIZ);
+ devpriv->daccon = (PCI224_DACCON_TRIG_SW | PCI224_DACCON_POLAR_BI |
+ PCI224_DACCON_FIFOENAB | PCI224_DACCON_FIFOINTR_EMPTY);
+ outw(devpriv->daccon | PCI224_DACCON_FIFORESET,
+ dev->iobase + PCI224_DACCON);
+
+ /* Allocate subdevices. There is only one! */
+ if ((ret = alloc_subdevices(dev, 1)) < 0) {
+ printk(KERN_ERR "comedi%d: error! out of memory!\n",
+ dev->minor);
+ return ret;
+ }
+
+ s = dev->subdevices + 0;
+ /* Analog output subdevice. */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_CMD_WRITE;
+ s->n_chan = thisboard->ao_chans;
+ s->maxdata = (1 << thisboard->ao_bits) - 1;
+ s->insn_write = &pci224_ao_insn_write;
+ s->insn_read = &pci224_ao_insn_read;
+ s->len_chanlist = s->n_chan;
+
+ dev->write_subdev = s;
+ s->do_cmd = &pci224_ao_cmd;
+ s->do_cmdtest = &pci224_ao_cmdtest;
+ s->cancel = &pci224_ao_cancel;
+ s->munge = &pci224_ao_munge;
+
+ /* Sort out channel range options. */
+ if (thisboard->model == pci234_model) {
+ /* PCI234 range options. */
+ const struct comedi_lrange **range_table_list;
+
+ s->range_table_list = range_table_list =
+ kmalloc(sizeof(struct comedi_lrange *) * s->n_chan,
+ GFP_KERNEL);
+ if (!s->range_table_list) {
+ return -ENOMEM;
+ }
+ for (n = 2; n < 3 + s->n_chan; n++) {
+ if (it->options[n] < 0 || it->options[n] > 1) {
+ printk(KERN_WARNING "comedi%d: %s: warning! "
+ "bad options[%u]=%d\n",
+ dev->minor, DRIVER_NAME, n,
+ it->options[n]);
+ }
+ }
+ for (n = 0; n < s->n_chan; n++) {
+ if (n < COMEDI_NDEVCONFOPTS - 3 &&
+ it->options[3 + n] == 1) {
+ if (it->options[2] == 1) {
+ range_table_list[n] = &range_pci234_ext;
+ } else {
+ range_table_list[n] = &range_bipolar5;
+ }
+ } else {
+ if (it->options[2] == 1) {
+ range_table_list[n] =
+ &range_pci234_ext2;
+ } else {
+ range_table_list[n] = &range_bipolar10;
+ }
+ }
+ }
+ devpriv->hwrange = hwrange_pci234;
+ } else {
+ /* PCI224 range options. */
+ if (it->options[2] == 1) {
+ s->range_table = &range_pci224_external;
+ devpriv->hwrange = hwrange_pci224_external;
+ } else {
+ if (it->options[2] != 0) {
+ printk(KERN_WARNING "comedi%d: %s: warning! "
+ "bad options[2]=%d\n",
+ dev->minor, DRIVER_NAME,
+ it->options[2]);
+ }
+ s->range_table = &range_pci224_internal;
+ devpriv->hwrange = hwrange_pci224_internal;
+ }
+ }
+
+ dev->board_name = thisboard->name;
+
+ if (irq) {
+ ret = comedi_request_irq(irq, pci224_interrupt, IRQF_SHARED,
+ DRIVER_NAME, dev);
+ if (ret < 0) {
+ printk(KERN_ERR "comedi%d: error! "
+ "unable to allocate irq %u\n", dev->minor, irq);
+ return ret;
+ } else {
+ dev->irq = irq;
+ }
+ }
+
+ printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
+ printk("(pci %s) ", pci_name(pci_dev));
+ if (irq) {
+ printk("(irq %u%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));
+ } else {
+ printk("(no irq) ");
+ }
+
+ printk("attached\n");
+
+ return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int pci224_detach(struct comedi_device * dev)
+{
+ printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor, DRIVER_NAME);
+
+ if (dev->irq) {
+ comedi_free_irq(dev->irq, dev);
+ }
+ if (dev->subdevices) {
+ struct comedi_subdevice *s;
+
+ s = dev->subdevices + 0;
+ /* AO subdevice */
+ if (s->range_table_list) {
+ kfree(s->range_table_list);
+ }
+ }
+ if (devpriv) {
+ if (devpriv->ao_readback) {
+ kfree(devpriv->ao_readback);
+ }
+ if (devpriv->ao_scan_vals) {
+ kfree(devpriv->ao_scan_vals);
+ }
+ if (devpriv->ao_scan_order) {
+ kfree(devpriv->ao_scan_order);
+ }
+ if (devpriv->pci_dev) {
+ if (dev->iobase) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ }
+ }
+ if (dev->board_name) {
+ printk(KERN_INFO "comedi%d: %s removed\n",
+ dev->minor, dev->board_name);
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/amplc_pci230.c b/drivers/staging/comedi/drivers/amplc_pci230.c
new file mode 100644
index 000000000000..0c9e5737e100
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_pci230.c
@@ -0,0 +1,2977 @@
+ /*
+ comedi/drivers/amplc_pci230.c
+ Driver for Amplicon PCI230 and PCI260 Multifunction I/O boards.
+
+ Copyright (C) 2001 Allan Willcox <allanwillcox@ozemail.com.au>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/*
+Driver: amplc_pci230
+Description: Amplicon PCI230, PCI260 Multifunction I/O boards
+Author: Allan Willcox <allanwillcox@ozemail.com.au>,
+ Steve D Sharples <steve.sharples@nottingham.ac.uk>,
+ Ian Abbott <abbotti@mev.co.uk>
+Updated: Wed, 22 Oct 2008 12:34:49 +0100
+Devices: [Amplicon] PCI230 (pci230 or amplc_pci230),
+ PCI230+ (pci230+ or amplc_pci230),
+ PCI260 (pci260 or amplc_pci230), PCI260+ (pci260+ or amplc_pci230)
+Status: works
+
+Configuration options:
+ [0] - PCI bus of device (optional).
+ [1] - PCI slot of device (optional).
+ If bus/slot is not specified, the first available PCI device
+ will be used.
+
+Configuring a "amplc_pci230" will match any supported card and it will
+choose the best match, picking the "+" models if possible. Configuring
+a "pci230" will match a PCI230 or PCI230+ card and it will be treated as
+a PCI230. Configuring a "pci260" will match a PCI260 or PCI260+ card
+and it will be treated as a PCI260. Configuring a "pci230+" will match
+a PCI230+ card. Configuring a "pci260+" will match a PCI260+ card.
+
+Subdevices:
+
+ PCI230(+) PCI260(+)
+ --------- ---------
+ Subdevices 3 1
+ 0 AI AI
+ 1 AO
+ 2 DIO
+
+AI Subdevice:
+
+ The AI subdevice has 16 single-ended channels or 8 differential
+ channels.
+
+ The PCI230 and PCI260 cards have 12-bit resolution. The PCI230+ and
+ PCI260+ cards have 16-bit resolution.
+
+ For differential mode, use inputs 2N and 2N+1 for channel N (e.g. use
+ inputs 14 and 15 for channel 7). If the card is physically a PCI230
+ or PCI260 then it actually uses a "pseudo-differential" mode where the
+ inputs are sampled a few microseconds apart. The PCI230+ and PCI260+
+ use true differential sampling. Another difference is that if the
+ card is physically a PCI230 or PCI260, the inverting input is 2N,
+ whereas for a PCI230+ or PCI260+ the inverting input is 2N+1. So if a
+ PCI230 is physically replaced by a PCI230+ (or a PCI260 with a
+ PCI260+) and differential mode is used, the differential inputs need
+ to be physically swapped on the connector.
+
+ The following input ranges are supported:
+
+ 0 => [-10, +10] V
+ 1 => [-5, +5] V
+ 2 => [-2.5, +2.5] V
+ 3 => [-1.25, +1.25] V
+ 4 => [0, 10] V
+ 5 => [0, 5] V
+ 6 => [0, 2.5] V
+
+AI Commands:
+
+ +=========+==============+===========+============+==========+
+ |start_src|scan_begin_src|convert_src|scan_end_src| stop_src |
+ +=========+==============+===========+============+==========+
+ |TRIG_NOW | TRIG_FOLLOW |TRIG_TIMER | TRIG_COUNT |TRIG_NONE |
+ |TRIG_INT | |TRIG_EXT(3)| |TRIG_COUNT|
+ | | |TRIG_INT | | |
+ | |--------------|-----------| | |
+ | | TRIG_TIMER(1)|TRIG_TIMER | | |
+ | | TRIG_EXT(2) | | | |
+ | | TRIG_INT | | | |
+ +---------+--------------+-----------+------------+----------+
+
+ Note 1: If AI command and AO command are used simultaneously, only
+ one may have scan_begin_src == TRIG_TIMER.
+
+ Note 2: For PCI230 and PCI230+, scan_begin_src == TRIG_EXT uses
+ DIO channel 16 (pin 49) which will need to be configured as
+ a digital input. For PCI260+, the EXTTRIG/EXTCONVCLK input
+ (pin 17) is used instead. For PCI230, scan_begin_src ==
+ TRIG_EXT is not supported. The trigger is a rising edge
+ on the input.
+
+ Note 3: For convert_src == TRIG_EXT, the EXTTRIG/EXTCONVCLK input
+ (pin 25 on PCI230(+), pin 17 on PCI260(+)) is used. The
+ convert_arg value is interpreted as follows:
+
+ convert_arg == (CR_EDGE | 0) => rising edge
+ convert_arg == (CR_EDGE | CR_INVERT | 0) => falling edge
+ convert_arg == 0 => falling edge (backwards compatibility)
+ convert_arg == 1 => rising edge (backwards compatibility)
+
+ All entries in the channel list must use the same analogue reference.
+ If the analogue reference is not AREF_DIFF (not differential) each
+ pair of channel numbers (0 and 1, 2 and 3, etc.) must use the same
+ input range. The input ranges used in the sequence must be all
+ bipolar (ranges 0 to 3) or all unipolar (ranges 4 to 6). The channel
+ sequence must consist of 1 or more identical subsequences. Within the
+ subsequence, channels must be in ascending order with no repeated
+ channels. For example, the following sequences are valid: 0 1 2 3
+ (single valid subsequence), 0 2 3 5 0 2 3 5 (repeated valid
+ subsequence), 1 1 1 1 (repeated valid subsequence). The following
+ sequences are invalid: 0 3 2 1 (invalid subsequence), 0 2 3 5 0 2 3
+ (incompletely repeated subsequence). Some versions of the PCI230+ and
+ PCI260+ have a bug that requires a subsequence longer than one entry
+ long to include channel 0.
+
+AO Subdevice:
+
+ The AO subdevice has 2 channels with 12-bit resolution.
+
+ The following output ranges are supported:
+
+ 0 => [0, 10] V
+ 1 => [-10, +10] V
+
+AO Commands:
+
+ +=========+==============+===========+============+==========+
+ |start_src|scan_begin_src|convert_src|scan_end_src| stop_src |
+ +=========+==============+===========+============+==========+
+ |TRIG_INT | TRIG_TIMER(1)| TRIG_NOW | TRIG_COUNT |TRIG_NONE |
+ | | TRIG_EXT(2) | | |TRIG_COUNT|
+ | | TRIG_INT | | | |
+ +---------+--------------+-----------+------------+----------+
+
+ Note 1: If AI command and AO command are used simultaneously, only
+ one may have scan_begin_src == TRIG_TIMER.
+
+ Note 2: scan_begin_src == TRIG_EXT is only supported if the card is
+ configured as a PCI230+ and is only supported on later
+ versions of the card. As a card configured as a PCI230+ is
+ not guaranteed to support external triggering, please consider
+ this support to be a bonus. It uses the EXTTRIG/ EXTCONVCLK
+ input (PCI230+ pin 25). Triggering will be on the rising edge
+ unless the CR_INVERT flag is set in scan_begin_arg.
+
+ The channels in the channel sequence must be in ascending order with
+ no repeats. All entries in the channel sequence must use the same
+ output range.
+
+DIO Subdevice:
+
+ The DIO subdevice is a 8255 chip providing 24 DIO channels. The DIO
+ channels are configurable as inputs or outputs in four groups:
+
+ Port A - channels 0 to 7
+ Port B - channels 8 to 15
+ Port CL - channels 16 to 19
+ Port CH - channels 20 to 23
+
+ Only mode 0 of the 8255 chip is supported.
+
+ Bit 0 of port C (DIO channel 16) is also used as an external scan
+ trigger input for AI commands on PCI230 and PCI230+, so would need to
+ be configured as an input to use it for that purpose.
+*/
+/*
+Extra triggered scan functionality, interrupt bug-fix added by Steve Sharples.
+Support for PCI230+/260+, more triggered scan functionality, and workarounds
+for (or detection of) various hardware problems added by Ian Abbott.
+*/
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+
+#include "comedi_pci.h"
+#include "8253.h"
+#include "8255.h"
+
+/* PCI230 PCI configuration register information */
+#define PCI_VENDOR_ID_AMPLICON 0x14dc
+#define PCI_DEVICE_ID_PCI230 0x0000
+#define PCI_DEVICE_ID_PCI260 0x0006
+#define PCI_DEVICE_ID_INVALID 0xffff
+
+#define PCI230_IO1_SIZE 32 /* Size of I/O space 1 */
+#define PCI230_IO2_SIZE 16 /* Size of I/O space 2 */
+
+/* PCI230 i/o space 1 registers. */
+#define PCI230_PPI_X_BASE 0x00 /* User PPI (82C55) base */
+#define PCI230_PPI_X_A 0x00 /* User PPI (82C55) port A */
+#define PCI230_PPI_X_B 0x01 /* User PPI (82C55) port B */
+#define PCI230_PPI_X_C 0x02 /* User PPI (82C55) port C */
+#define PCI230_PPI_X_CMD 0x03 /* User PPI (82C55) control word */
+#define PCI230_Z2_CT_BASE 0x14 /* 82C54 counter/timer base */
+#define PCI230_Z2_CT0 0x14 /* 82C54 counter/timer 0 */
+#define PCI230_Z2_CT1 0x15 /* 82C54 counter/timer 1 */
+#define PCI230_Z2_CT2 0x16 /* 82C54 counter/timer 2 */
+#define PCI230_Z2_CTC 0x17 /* 82C54 counter/timer control word */
+#define PCI230_ZCLK_SCE 0x1A /* Group Z Clock Configuration */
+#define PCI230_ZGAT_SCE 0x1D /* Group Z Gate Configuration */
+#define PCI230_INT_SCE 0x1E /* Interrupt source mask (w) */
+#define PCI230_INT_STAT 0x1E /* Interrupt status (r) */
+
+/* PCI230 i/o space 2 registers. */
+#define PCI230_DACCON 0x00 /* DAC control */
+#define PCI230_DACOUT1 0x02 /* DAC channel 0 (w) */
+#define PCI230_DACOUT2 0x04 /* DAC channel 1 (w) (not FIFO mode) */
+#define PCI230_ADCDATA 0x08 /* ADC data (r) */
+#define PCI230_ADCSWTRIG 0x08 /* ADC software trigger (w) */
+#define PCI230_ADCCON 0x0A /* ADC control */
+#define PCI230_ADCEN 0x0C /* ADC channel enable bits */
+#define PCI230_ADCG 0x0E /* ADC gain control bits */
+/* PCI230+ i/o space 2 additional registers. */
+#define PCI230P_ADCTRIG 0x10 /* ADC start acquisition trigger */
+#define PCI230P_ADCTH 0x12 /* ADC analog trigger threshold */
+#define PCI230P_ADCFFTH 0x14 /* ADC FIFO interrupt threshold */
+#define PCI230P_ADCFFLEV 0x16 /* ADC FIFO level (r) */
+#define PCI230P_ADCPTSC 0x18 /* ADC pre-trigger sample count (r) */
+#define PCI230P_ADCHYST 0x1A /* ADC analog trigger hysteresys */
+#define PCI230P_EXTFUNC 0x1C /* Extended functions */
+#define PCI230P_HWVER 0x1E /* Hardware version (r) */
+/* PCI230+ hardware version 2 onwards. */
+#define PCI230P2_DACDATA 0x02 /* DAC data (FIFO mode) (w) */
+#define PCI230P2_DACSWTRIG 0x02 /* DAC soft trigger (FIFO mode) (r) */
+#define PCI230P2_DACEN 0x06 /* DAC channel enable (FIFO mode) */
+
+/* Convertor related constants. */
+#define PCI230_DAC_SETTLE 5 /* Analogue output settling time in µs */
+ /* (DAC itself is 1µs nominally). */
+#define PCI230_ADC_SETTLE 1 /* Analogue input settling time in µs */
+ /* (ADC itself is 1.6µs nominally but we poll
+ * anyway). */
+#define PCI230_MUX_SETTLE 10 /* ADC MUX settling time in µS */
+ /* - 10µs for se, 20µs de. */
+
+/* DACCON read-write values. */
+#define PCI230_DAC_OR_UNI (0<<0) /* Output range unipolar */
+#define PCI230_DAC_OR_BIP (1<<0) /* Output range bipolar */
+#define PCI230_DAC_OR_MASK (1<<0)
+/* The following applies only if DAC FIFO support is enabled in the EXTFUNC
+ * register (and only for PCI230+ hardware version 2 onwards). */
+#define PCI230P2_DAC_FIFO_EN (1<<8) /* FIFO enable */
+/* The following apply only if the DAC FIFO is enabled (and only for PCI230+
+ * hardware version 2 onwards). */
+#define PCI230P2_DAC_TRIG_NONE (0<<2) /* No trigger */
+#define PCI230P2_DAC_TRIG_SW (1<<2) /* Software trigger trigger */
+#define PCI230P2_DAC_TRIG_EXTP (2<<2) /* EXTTRIG +ve edge trigger */
+#define PCI230P2_DAC_TRIG_EXTN (3<<2) /* EXTTRIG -ve edge trigger */
+#define PCI230P2_DAC_TRIG_Z2CT0 (4<<2) /* CT0-OUT +ve edge trigger */
+#define PCI230P2_DAC_TRIG_Z2CT1 (5<<2) /* CT1-OUT +ve edge trigger */
+#define PCI230P2_DAC_TRIG_Z2CT2 (6<<2) /* CT2-OUT +ve edge trigger */
+#define PCI230P2_DAC_TRIG_MASK (7<<2)
+#define PCI230P2_DAC_FIFO_WRAP (1<<7) /* FIFO wraparound mode */
+#define PCI230P2_DAC_INT_FIFO_EMPTY (0<<9) /* FIFO interrupt empty */
+#define PCI230P2_DAC_INT_FIFO_NEMPTY (1<<9)
+#define PCI230P2_DAC_INT_FIFO_NHALF (2<<9) /* FIFO intr not half full */
+#define PCI230P2_DAC_INT_FIFO_HALF (3<<9)
+#define PCI230P2_DAC_INT_FIFO_NFULL (4<<9) /* FIFO interrupt not full */
+#define PCI230P2_DAC_INT_FIFO_FULL (5<<9)
+#define PCI230P2_DAC_INT_FIFO_MASK (7<<9)
+
+/* DACCON read-only values. */
+#define PCI230_DAC_BUSY (1<<1) /* DAC busy. */
+/* The following apply only if the DAC FIFO is enabled (and only for PCI230+
+ * hardware version 2 onwards). */
+#define PCI230P2_DAC_FIFO_UNDERRUN_LATCHED (1<<5) /* Underrun error */
+#define PCI230P2_DAC_FIFO_EMPTY (1<<13) /* FIFO empty */
+#define PCI230P2_DAC_FIFO_FULL (1<<14) /* FIFO full */
+#define PCI230P2_DAC_FIFO_HALF (1<<15) /* FIFO half full */
+
+/* DACCON write-only, transient values. */
+/* The following apply only if the DAC FIFO is enabled (and only for PCI230+
+ * hardware version 2 onwards). */
+#define PCI230P2_DAC_FIFO_UNDERRUN_CLEAR (1<<5) /* Clear underrun */
+#define PCI230P2_DAC_FIFO_RESET (1<<12) /* FIFO reset */
+
+/* PCI230+ hardware version 2 DAC FIFO levels. */
+#define PCI230P2_DAC_FIFOLEVEL_HALF 512
+#define PCI230P2_DAC_FIFOLEVEL_FULL 1024
+/* Free space in DAC FIFO. */
+#define PCI230P2_DAC_FIFOROOM_EMPTY PCI230P2_DAC_FIFOLEVEL_FULL
+#define PCI230P2_DAC_FIFOROOM_ONETOHALF \
+ (PCI230P2_DAC_FIFOLEVEL_FULL - PCI230P2_DAC_FIFOLEVEL_HALF)
+#define PCI230P2_DAC_FIFOROOM_HALFTOFULL 1
+#define PCI230P2_DAC_FIFOROOM_FULL 0
+
+/* ADCCON read/write values. */
+#define PCI230_ADC_TRIG_NONE (0<<0) /* No trigger */
+#define PCI230_ADC_TRIG_SW (1<<0) /* Software trigger trigger */
+#define PCI230_ADC_TRIG_EXTP (2<<0) /* EXTTRIG +ve edge trigger */
+#define PCI230_ADC_TRIG_EXTN (3<<0) /* EXTTRIG -ve edge trigger */
+#define PCI230_ADC_TRIG_Z2CT0 (4<<0) /* CT0-OUT +ve edge trigger */
+#define PCI230_ADC_TRIG_Z2CT1 (5<<0) /* CT1-OUT +ve edge trigger */
+#define PCI230_ADC_TRIG_Z2CT2 (6<<0) /* CT2-OUT +ve edge trigger */
+#define PCI230_ADC_TRIG_MASK (7<<0)
+#define PCI230_ADC_IR_UNI (0<<3) /* Input range unipolar */
+#define PCI230_ADC_IR_BIP (1<<3) /* Input range bipolar */
+#define PCI230_ADC_IR_MASK (1<<3)
+#define PCI230_ADC_IM_SE (0<<4) /* Input mode single ended */
+#define PCI230_ADC_IM_DIF (1<<4) /* Input mode differential */
+#define PCI230_ADC_IM_MASK (1<<4)
+#define PCI230_ADC_FIFO_EN (1<<8) /* FIFO enable */
+#define PCI230_ADC_INT_FIFO_EMPTY (0<<9)
+#define PCI230_ADC_INT_FIFO_NEMPTY (1<<9) /* FIFO interrupt not empty */
+#define PCI230_ADC_INT_FIFO_NHALF (2<<9)
+#define PCI230_ADC_INT_FIFO_HALF (3<<9) /* FIFO interrupt half full */
+#define PCI230_ADC_INT_FIFO_NFULL (4<<9)
+#define PCI230_ADC_INT_FIFO_FULL (5<<9) /* FIFO interrupt full */
+#define PCI230P_ADC_INT_FIFO_THRESH (7<<9) /* FIFO interrupt threshold */
+#define PCI230_ADC_INT_FIFO_MASK (7<<9)
+
+/* ADCCON write-only, transient values. */
+#define PCI230_ADC_FIFO_RESET (1<<12) /* FIFO reset */
+#define PCI230_ADC_GLOB_RESET (1<<13) /* Global reset */
+
+/* ADCCON read-only values. */
+#define PCI230_ADC_BUSY (1<<15) /* ADC busy */
+#define PCI230_ADC_FIFO_EMPTY (1<<12) /* FIFO empty */
+#define PCI230_ADC_FIFO_FULL (1<<13) /* FIFO full */
+#define PCI230_ADC_FIFO_HALF (1<<14) /* FIFO half full */
+#define PCI230_ADC_FIFO_FULL_LATCHED (1<<5) /* Indicates overrun occurred */
+
+/* PCI230 ADC FIFO levels. */
+#define PCI230_ADC_FIFOLEVEL_HALFFULL 2049 /* Value for FIFO half full */
+#define PCI230_ADC_FIFOLEVEL_FULL 4096 /* FIFO size */
+
+/* Value to write to ADCSWTRIG to trigger ADC conversion in software trigger
+ * mode. Can be anything. */
+#define PCI230_ADC_CONV 0xffff
+
+/* PCI230+ EXTFUNC values. */
+#define PCI230P_EXTFUNC_GAT_EXTTRIG (1<<0)
+ /* Route EXTTRIG pin to external gate inputs. */
+/* PCI230+ hardware version 2 values. */
+#define PCI230P2_EXTFUNC_DACFIFO (1<<1)
+ /* Allow DAC FIFO to be enabled. */
+
+/*
+ * Counter/timer clock input configuration sources.
+ */
+#define CLK_CLK 0 /* reserved (channel-specific clock) */
+#define CLK_10MHZ 1 /* internal 10 MHz clock */
+#define CLK_1MHZ 2 /* internal 1 MHz clock */
+#define CLK_100KHZ 3 /* internal 100 kHz clock */
+#define CLK_10KHZ 4 /* internal 10 kHz clock */
+#define CLK_1KHZ 5 /* internal 1 kHz clock */
+#define CLK_OUTNM1 6 /* output of channel-1 modulo total */
+#define CLK_EXT 7 /* external clock */
+/* Macro to construct clock input configuration register value. */
+#define CLK_CONFIG(chan, src) ((((chan) & 3) << 3) | ((src) & 7))
+/* Timebases in ns. */
+#define TIMEBASE_10MHZ 100
+#define TIMEBASE_1MHZ 1000
+#define TIMEBASE_100KHZ 10000
+#define TIMEBASE_10KHZ 100000
+#define TIMEBASE_1KHZ 1000000
+
+/*
+ * Counter/timer gate input configuration sources.
+ */
+#define GAT_VCC 0 /* VCC (i.e. enabled) */
+#define GAT_GND 1 /* GND (i.e. disabled) */
+#define GAT_EXT 2 /* external gate input (PPCn on PCI230) */
+#define GAT_NOUTNM2 3 /* inverted output of channel-2 modulo total */
+/* Macro to construct gate input configuration register value. */
+#define GAT_CONFIG(chan, src) ((((chan) & 3) << 3) | ((src) & 7))
+
+/*
+ * Summary of CLK_OUTNM1 and GAT_NOUTNM2 connections for PCI230 and PCI260:
+ *
+ * Channel's Channel's
+ * clock input gate input
+ * Channel CLK_OUTNM1 GAT_NOUTNM2
+ * ------- ---------- -----------
+ * Z2-CT0 Z2-CT2-OUT /Z2-CT1-OUT
+ * Z2-CT1 Z2-CT0-OUT /Z2-CT2-OUT
+ * Z2-CT2 Z2-CT1-OUT /Z2-CT0-OUT
+ */
+
+/* Interrupt enables/status register values. */
+#define PCI230_INT_DISABLE 0
+#define PCI230_INT_PPI_C0 (1<<0)
+#define PCI230_INT_PPI_C3 (1<<1)
+#define PCI230_INT_ADC (1<<2)
+#define PCI230_INT_ZCLK_CT1 (1<<5)
+/* For PCI230+ hardware version 2 when DAC FIFO enabled. */
+#define PCI230P2_INT_DAC (1<<4)
+
+#define PCI230_TEST_BIT(val, n) ((val>>n)&1)
+ /* Assumes bits numbered with zero offset, ie. 0-15 */
+
+/* (Potentially) shared resources and their owners */
+enum {
+ RES_Z2CT0, /* Z2-CT0 */
+ RES_Z2CT1, /* Z2-CT1 */
+ RES_Z2CT2, /* Z2-CT2 */
+ NUM_RESOURCES /* Number of (potentially) shared resources. */
+};
+
+enum {
+ OWNER_NONE, /* Not owned */
+ OWNER_AICMD, /* Owned by AI command */
+ OWNER_AOCMD /* Owned by AO command */
+};
+
+/*
+ * Handy macros.
+ */
+
+/* Combine old and new bits. */
+#define COMBINE(old, new, mask) (((old) & ~(mask)) | ((new) & (mask)))
+
+/* A generic null function pointer value. */
+#define NULLFUNC 0
+
+/* Current CPU. XXX should this be hard_smp_processor_id()? */
+#define THISCPU smp_processor_id()
+
+/* State flags for atomic bit operations */
+#define AI_CMD_STARTED 0
+#define AO_CMD_STARTED 1
+
+/*
+ * Board descriptions for the two boards supported.
+ */
+
+struct pci230_board {
+ const char *name;
+ unsigned short id;
+ int ai_chans;
+ int ai_bits;
+ int ao_chans;
+ int ao_bits;
+ int have_dio;
+ unsigned int min_hwver; /* Minimum hardware version supported. */
+};
+static const struct pci230_board pci230_boards[] = {
+ {
+ name: "pci230+",
+ id: PCI_DEVICE_ID_PCI230,
+ ai_chans:16,
+ ai_bits: 16,
+ ao_chans:2,
+ ao_bits: 12,
+ have_dio:1,
+ min_hwver:1,
+ },
+ {
+ name: "pci260+",
+ id: PCI_DEVICE_ID_PCI260,
+ ai_chans:16,
+ ai_bits: 16,
+ ao_chans:0,
+ ao_bits: 0,
+ have_dio:0,
+ min_hwver:1,
+ },
+ {
+ name: "pci230",
+ id: PCI_DEVICE_ID_PCI230,
+ ai_chans:16,
+ ai_bits: 12,
+ ao_chans:2,
+ ao_bits: 12,
+ have_dio:1,
+ },
+ {
+ name: "pci260",
+ id: PCI_DEVICE_ID_PCI260,
+ ai_chans:16,
+ ai_bits: 12,
+ ao_chans:0,
+ ao_bits: 0,
+ have_dio:0,
+ },
+ {
+ name: "amplc_pci230", /* Wildcard matches any above */
+ id: PCI_DEVICE_ID_INVALID,
+ },
+};
+
+static DEFINE_PCI_DEVICE_TABLE(pci230_pci_table) = {
+ {PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_PCI230, PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, 0},
+ {PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_PCI260, PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, pci230_pci_table);
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define n_pci230_boards (sizeof(pci230_boards)/sizeof(pci230_boards[0]))
+#define thisboard ((const struct pci230_board *)dev->board_ptr)
+
+/* 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 struct comedi_device struct. */
+struct pci230_private {
+ struct pci_dev *pci_dev;
+ spinlock_t isr_spinlock; /* Interrupt spin lock */
+ spinlock_t res_spinlock; /* Shared resources spin lock */
+ spinlock_t ai_stop_spinlock; /* Spin lock for stopping AI command */
+ spinlock_t ao_stop_spinlock; /* Spin lock for stopping AO command */
+ unsigned long state; /* State flags */
+ unsigned long iobase1; /* PCI230's I/O space 1 */
+ unsigned int ao_readback[2]; /* Used for AO readback */
+ unsigned int ai_scan_count; /* Number of analogue input scans
+ * remaining. */
+ unsigned int ai_scan_pos; /* Current position within analogue
+ * input scan */
+ unsigned int ao_scan_count; /* Number of analogue output scans
+ * remaining. */
+ int intr_cpuid; /* ID of CPU running interrupt routine. */
+ unsigned short hwver; /* Hardware version (for '+' models). */
+ unsigned short adccon; /* ADCCON register value. */
+ unsigned short daccon; /* DACCON register value. */
+ unsigned short adcfifothresh; /* ADC FIFO programmable interrupt
+ * level threshold (PCI230+/260+). */
+ unsigned short adcg; /* ADCG register value. */
+ unsigned char int_en; /* Interrupt enables bits. */
+ unsigned char ai_continuous; /* Flag set when cmd->stop_src ==
+ * TRIG_NONE - user chooses to stop
+ * continuous conversion by
+ * cancelation. */
+ unsigned char ao_continuous; /* Flag set when cmd->stop_src ==
+ * TRIG_NONE - user chooses to stop
+ * continuous conversion by
+ * cancelation. */
+ unsigned char ai_bipolar; /* Set if bipolar input range so we
+ * know to mangle it. */
+ unsigned char ao_bipolar; /* Set if bipolar output range so we
+ * know to mangle it. */
+ unsigned char ier; /* Copy of interrupt enables/status register. */
+ unsigned char intr_running; /* Flag set in interrupt routine. */
+ unsigned char res_owner[NUM_RESOURCES]; /* Shared resource owners. */
+};
+
+#define devpriv ((struct pci230_private *)dev->private)
+
+/* PCI230 clock source periods in ns */
+static const unsigned int pci230_timebase[8] = {
+ [CLK_10MHZ] = TIMEBASE_10MHZ,
+ [CLK_1MHZ] = TIMEBASE_1MHZ,
+ [CLK_100KHZ] = TIMEBASE_100KHZ,
+ [CLK_10KHZ] = TIMEBASE_10KHZ,
+ [CLK_1KHZ] = TIMEBASE_1KHZ,
+};
+
+/* PCI230 analogue input range table */
+static const struct comedi_lrange pci230_ai_range = { 7, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5)
+ }
+};
+
+/* PCI230 analogue gain bits for each input range. */
+static const unsigned char pci230_ai_gain[7] = { 0, 1, 2, 3, 1, 2, 3 };
+
+/* PCI230 adccon bipolar flag for each analogue input range. */
+static const unsigned char pci230_ai_bipolar[7] = { 1, 1, 1, 1, 0, 0, 0 };
+
+/* PCI230 analogue output range table */
+static const struct comedi_lrange pci230_ao_range = { 2, {
+ UNI_RANGE(10),
+ BIP_RANGE(10)
+ }
+};
+
+/* PCI230 daccon bipolar flag for each analogue output range. */
+static const unsigned char pci230_ao_bipolar[2] = { 0, 1 };
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int pci230_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pci230_detach(struct comedi_device * dev);
+static struct comedi_driver driver_amplc_pci230 = {
+ driver_name:"amplc_pci230",
+ module:THIS_MODULE,
+ attach:pci230_attach,
+ detach:pci230_detach,
+ board_name:&pci230_boards[0].name,
+ offset:sizeof(pci230_boards[0]),
+ num_names:sizeof(pci230_boards) / sizeof(pci230_boards[0]),
+};
+
+COMEDI_PCI_INITCLEANUP(driver_amplc_pci230, pci230_pci_table);
+
+static int pci230_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int pci230_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int pci230_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static void pci230_ct_setup_ns_mode(struct comedi_device * dev, unsigned int ct,
+ unsigned int mode, uint64_t ns, unsigned int round);
+static void pci230_ns_to_single_timer(unsigned int *ns, unsigned int round);
+static void pci230_cancel_ct(struct comedi_device * dev, unsigned int ct);
+static irqreturn_t pci230_interrupt(int irq, void *d PT_REGS_ARG);
+static int pci230_ao_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int pci230_ao_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int pci230_ao_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static void pci230_ao_stop(struct comedi_device * dev, struct comedi_subdevice * s);
+static void pci230_handle_ao_nofifo(struct comedi_device * dev, struct comedi_subdevice * s);
+static int pci230_handle_ao_fifo(struct comedi_device * dev, struct comedi_subdevice * s);
+static int pci230_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int pci230_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int pci230_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static void pci230_ai_stop(struct comedi_device * dev, struct comedi_subdevice * s);
+static void pci230_handle_ai(struct comedi_device * dev, struct comedi_subdevice * s);
+
+static short pci230_ai_read(struct comedi_device * dev)
+{
+ /* Read sample. */
+ short data = (short) inw(dev->iobase + PCI230_ADCDATA);
+
+ /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower
+ * four bits reserved for expansion). */
+ /* PCI230+ is 16 bit AI. */
+ data = data >> (16 - thisboard->ai_bits);
+
+ /* If a bipolar range was specified, mangle it (twos
+ * complement->straight binary). */
+ if (devpriv->ai_bipolar) {
+ data ^= 1 << (thisboard->ai_bits - 1);
+ }
+ return data;
+}
+
+static inline unsigned short pci230_ao_mangle_datum(struct comedi_device * dev,
+ short datum)
+{
+ /* If a bipolar range was specified, mangle it (straight binary->twos
+ * complement). */
+ if (devpriv->ao_bipolar) {
+ datum ^= 1 << (thisboard->ao_bits - 1);
+ }
+
+ /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower
+ * four bits reserved for expansion). */
+ /* PCI230+ is also 12 bit AO. */
+ datum <<= (16 - thisboard->ao_bits);
+ return (unsigned short)datum;
+}
+
+static inline void pci230_ao_write_nofifo(struct comedi_device * dev, short datum,
+ unsigned int chan)
+{
+ /* Store unmangled datum to be read back later. */
+ devpriv->ao_readback[chan] = datum;
+
+ /* Write mangled datum to appropriate DACOUT register. */
+ outw(pci230_ao_mangle_datum(dev, datum), dev->iobase + (((chan) == 0)
+ ? PCI230_DACOUT1 : PCI230_DACOUT2));
+}
+
+static inline void pci230_ao_write_fifo(struct comedi_device * dev, short datum,
+ unsigned int chan)
+{
+ /* Store unmangled datum to be read back later. */
+ devpriv->ao_readback[chan] = datum;
+
+ /* Write mangled datum to appropriate DACDATA register. */
+ outw(pci230_ao_mangle_datum(dev, datum),
+ dev->iobase + PCI230P2_DACDATA);
+}
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int pci230_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase1, iobase2;
+ /* PCI230's I/O spaces 1 and 2 respectively. */
+ struct pci_dev *pci_dev;
+ int i = 0, irq_hdl, rc;
+
+ printk("comedi%d: amplc_pci230: attach %s %d,%d\n", dev->minor,
+ thisboard->name, it->options[0], it->options[1]);
+
+ /* Allocate the private structure area using alloc_private().
+ * Macro defined in comedidev.h - memsets struct fields to 0. */
+ if ((alloc_private(dev, sizeof(struct pci230_private))) < 0) {
+ return -ENOMEM;
+ }
+ spin_lock_init(&devpriv->isr_spinlock);
+ spin_lock_init(&devpriv->res_spinlock);
+ spin_lock_init(&devpriv->ai_stop_spinlock);
+ spin_lock_init(&devpriv->ao_stop_spinlock);
+ /* Find card */
+ for (pci_dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pci_dev != NULL;
+ pci_dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) {
+ if (it->options[0] || it->options[1]) {
+ /* Match against bus/slot options. */
+ if (it->options[0] != pci_dev->bus->number ||
+ it->options[1] != PCI_SLOT(pci_dev->devfn))
+ continue;
+ }
+ if (pci_dev->vendor != PCI_VENDOR_ID_AMPLICON)
+ continue;
+ if (thisboard->id == PCI_DEVICE_ID_INVALID) {
+ /* The name was specified as "amplc_pci230" which is
+ * used to match any supported device. Replace the
+ * current dev->board_ptr with one that matches the
+ * PCI device ID. */
+ for (i = 0; i < n_pci230_boards; i++) {
+ if (pci_dev->device == pci230_boards[i].id) {
+ if (pci230_boards[i].min_hwver > 0) {
+ /* Check for a '+' model.
+ * First check length of
+ * registers. */
+ if (pci_resource_len(pci_dev, 3)
+ < 32) {
+ /* Not a '+' model. */
+ continue;
+ }
+ /* TODO: temporarily enable the
+ * PCI device and read the
+ * hardware version register.
+ * For now assume it's okay. */
+ }
+ /* Change board_ptr to matched board */
+ dev->board_ptr = &pci230_boards[i];
+ break;
+ }
+ }
+ if (i < n_pci230_boards)
+ break;
+ } else {
+ /* The name was specified as a specific device name.
+ * The current dev->board_ptr is correct. Check
+ * whether it matches the PCI device ID. */
+ if (thisboard->id == pci_dev->device) {
+ /* Check minimum hardware version. */
+ if (thisboard->min_hwver > 0) {
+ /* Looking for a '+' model. First
+ * check length of registers. */
+ if (pci_resource_len(pci_dev, 3) < 32) {
+ /* Not a '+' model. */
+ continue;
+ }
+ /* TODO: temporarily enable the PCI
+ * device and read the hardware version
+ * register. For now, assume it's
+ * okay. */
+ break;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ if (!pci_dev) {
+ printk("comedi%d: No %s card found\n", dev->minor,
+ thisboard->name);
+ return -EIO;
+ }
+ devpriv->pci_dev = pci_dev;
+
+ /*
+ * Initialize dev->board_name.
+ */
+ dev->board_name = thisboard->name;
+
+ /* Enable PCI device and reserve I/O spaces. */
+ if (comedi_pci_enable(pci_dev, "amplc_pci230") < 0) {
+ printk("comedi%d: failed to enable PCI device "
+ "and request regions\n", dev->minor);
+ return -EIO;
+ }
+
+ /* Read base addresses of the PCI230's two I/O regions from PCI
+ * configuration register. */
+ iobase1 = pci_resource_start(pci_dev, 2);
+ iobase2 = pci_resource_start(pci_dev, 3);
+
+ printk("comedi%d: %s I/O region 1 0x%04lx I/O region 2 0x%04lx\n",
+ dev->minor, dev->board_name, iobase1, iobase2);
+
+ devpriv->iobase1 = iobase1;
+ dev->iobase = iobase2;
+
+ /* Read bits of DACCON register - only the output range. */
+ devpriv->daccon = inw(dev->iobase + PCI230_DACCON) & PCI230_DAC_OR_MASK;
+
+ /* Read hardware version register and set extended function register
+ * if they exist. */
+ if (pci_resource_len(pci_dev, 3) >= 32) {
+ unsigned short extfunc = 0;
+
+ devpriv->hwver = inw(dev->iobase + PCI230P_HWVER);
+ if (devpriv->hwver < thisboard->min_hwver) {
+ printk("comedi%d: %s - bad hardware version "
+ "- got %u, need %u\n", dev->minor,
+ dev->board_name, devpriv->hwver,
+ thisboard->min_hwver);
+ return -EIO;
+ }
+ if (devpriv->hwver > 0) {
+ if (!thisboard->have_dio) {
+ /* No DIO ports. Route counters' external gates
+ * to the EXTTRIG signal (PCI260+ pin 17).
+ * (Otherwise, they would be routed to DIO
+ * inputs PC0, PC1 and PC2 which don't exist
+ * on PCI260[+].) */
+ extfunc |= PCI230P_EXTFUNC_GAT_EXTTRIG;
+ }
+ if ((thisboard->ao_chans > 0)
+ && (devpriv->hwver >= 2)) {
+ /* Enable DAC FIFO functionality. */
+ extfunc |= PCI230P2_EXTFUNC_DACFIFO;
+ }
+ }
+ outw(extfunc, dev->iobase + PCI230P_EXTFUNC);
+ if ((extfunc & PCI230P2_EXTFUNC_DACFIFO) != 0) {
+ /* Temporarily enable DAC FIFO, reset it and disable
+ * FIFO wraparound. */
+ outw(devpriv->daccon | PCI230P2_DAC_FIFO_EN
+ | PCI230P2_DAC_FIFO_RESET,
+ dev->iobase + PCI230_DACCON);
+ /* Clear DAC FIFO channel enable register. */
+ outw(0, dev->iobase + PCI230P2_DACEN);
+ /* Disable DAC FIFO. */
+ outw(devpriv->daccon, dev->iobase + PCI230_DACCON);
+ }
+ }
+
+ /* Disable board's interrupts. */
+ outb(0, devpriv->iobase1 + PCI230_INT_SCE);
+
+ /* Set ADC to a reasonable state. */
+ devpriv->adcg = 0;
+ devpriv->adccon = PCI230_ADC_TRIG_NONE | PCI230_ADC_IM_SE
+ | PCI230_ADC_IR_BIP;
+ outw(1 << 0, dev->iobase + PCI230_ADCEN);
+ outw(devpriv->adcg, dev->iobase + PCI230_ADCG);
+ outw(devpriv->adccon | PCI230_ADC_FIFO_RESET,
+ dev->iobase + PCI230_ADCCON);
+
+ /* Register the interrupt handler. */
+ irq_hdl = comedi_request_irq(devpriv->pci_dev->irq, pci230_interrupt,
+ IRQF_SHARED, "amplc_pci230", dev);
+ if (irq_hdl < 0) {
+ printk("comedi%d: unable to register irq, "
+ "commands will not be available %d\n", dev->minor,
+ devpriv->pci_dev->irq);
+ } else {
+ dev->irq = devpriv->pci_dev->irq;
+ printk("comedi%d: registered irq %u\n", dev->minor,
+ devpriv->pci_dev->irq);
+ }
+
+ /*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_subdevices(dev, 3) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ /* analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_GROUND;
+ s->n_chan = thisboard->ai_chans;
+ s->maxdata = (1 << thisboard->ai_bits) - 1;
+ s->range_table = &pci230_ai_range;
+ s->insn_read = &pci230_ai_rinsn;
+ s->len_chanlist = 256; /* but there are restrictions. */
+ /* Only register commands if the interrupt handler is installed. */
+ if (irq_hdl == 0) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->do_cmd = &pci230_ai_cmd;
+ s->do_cmdtest = &pci230_ai_cmdtest;
+ s->cancel = pci230_ai_cancel;
+ }
+
+ s = dev->subdevices + 1;
+ /* analog output subdevice */
+ if (thisboard->ao_chans > 0) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = thisboard->ao_chans;;
+ s->maxdata = (1 << thisboard->ao_bits) - 1;
+ s->range_table = &pci230_ao_range;
+ s->insn_write = &pci230_ao_winsn;
+ s->insn_read = &pci230_ao_rinsn;
+ s->len_chanlist = thisboard->ao_chans;
+ /* Only register commands if the interrupt handler is
+ * installed. */
+ if (irq_hdl == 0) {
+ dev->write_subdev = s;
+ s->subdev_flags |= SDF_CMD_WRITE;
+ s->do_cmd = &pci230_ao_cmd;
+ s->do_cmdtest = &pci230_ao_cmdtest;
+ s->cancel = pci230_ao_cancel;
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = dev->subdevices + 2;
+ /* digital i/o subdevice */
+ if (thisboard->have_dio) {
+ rc = subdev_8255_init(dev, s, NULL,
+ (devpriv->iobase1 + PCI230_PPI_X_BASE));
+ if (rc < 0)
+ return rc;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ printk("comedi%d: attached\n", dev->minor);
+
+ return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int pci230_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: amplc_pci230: remove\n", dev->minor);
+
+ if (dev->subdevices && thisboard->have_dio)
+ /* Clean up dio subdevice. */
+ subdev_8255_cleanup(dev, dev->subdevices + 2);
+
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+
+ if (devpriv) {
+ if (devpriv->pci_dev) {
+ if (dev->iobase) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ }
+ }
+
+ return 0;
+}
+
+static int get_resources(struct comedi_device * dev, unsigned int res_mask,
+ unsigned char owner)
+{
+ int ok;
+ unsigned int i;
+ unsigned int b;
+ unsigned int claimed;
+ unsigned long irqflags;
+
+ ok = 1;
+ claimed = 0;
+ comedi_spin_lock_irqsave(&devpriv->res_spinlock, irqflags);
+ for (b = 1, i = 0; (i < NUM_RESOURCES)
+ && (res_mask != 0); b <<= 1, i++) {
+ if ((res_mask & b) != 0) {
+ res_mask &= ~b;
+ if (devpriv->res_owner[i] == OWNER_NONE) {
+ devpriv->res_owner[i] = owner;
+ claimed |= b;
+ } else if (devpriv->res_owner[i] != owner) {
+ for (b = 1, i = 0; claimed != 0; b <<= 1, i++) {
+ if ((claimed & b) != 0) {
+ devpriv->res_owner[i]
+ = OWNER_NONE;
+ claimed &= ~b;
+ }
+ }
+ ok = 0;
+ break;
+ }
+ }
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->res_spinlock, irqflags);
+ return ok;
+}
+
+static inline int get_one_resource(struct comedi_device * dev, unsigned int resource,
+ unsigned char owner)
+{
+ return get_resources(dev, (1U << resource), owner);
+}
+
+static void put_resources(struct comedi_device * dev, unsigned int res_mask,
+ unsigned char owner)
+{
+ unsigned int i;
+ unsigned int b;
+ unsigned long irqflags;
+
+ comedi_spin_lock_irqsave(&devpriv->res_spinlock, irqflags);
+ for (b = 1, i = 0; (i < NUM_RESOURCES)
+ && (res_mask != 0); b <<= 1, i++) {
+ if ((res_mask & b) != 0) {
+ res_mask &= ~b;
+ if (devpriv->res_owner[i] == owner) {
+ devpriv->res_owner[i] = OWNER_NONE;
+ }
+ }
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->res_spinlock, irqflags);
+}
+
+static inline void put_one_resource(struct comedi_device * dev, unsigned int resource,
+ unsigned char owner)
+{
+ put_resources(dev, (1U << resource), owner);
+}
+
+static inline void put_all_resources(struct comedi_device * dev, unsigned char owner)
+{
+ put_resources(dev, (1U << NUM_RESOURCES) - 1, owner);
+}
+
+/*
+ * COMEDI_SUBD_AI instruction;
+ */
+static int pci230_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int n, i;
+ unsigned int chan, range, aref;
+ unsigned int gainshift;
+ unsigned int status;
+ unsigned short adccon, adcen;
+
+ /* Unpack channel and range. */
+ chan = CR_CHAN(insn->chanspec);
+ range = CR_RANGE(insn->chanspec);
+ aref = CR_AREF(insn->chanspec);
+ if (aref == AREF_DIFF) {
+ /* Differential. */
+ if (chan >= s->n_chan / 2) {
+ DPRINTK("comedi%d: amplc_pci230: ai_rinsn: "
+ "differential channel number out of range "
+ "0 to %u\n", dev->minor, (s->n_chan / 2) - 1);
+ return -EINVAL;
+ }
+ }
+
+ /* Use Z2-CT2 as a conversion trigger instead of the built-in
+ * software trigger, as otherwise triggering of differential channels
+ * doesn't work properly for some versions of PCI230/260. Also set
+ * FIFO mode because the ADC busy bit only works for software triggers.
+ */
+ adccon = PCI230_ADC_TRIG_Z2CT2 | PCI230_ADC_FIFO_EN;
+ /* Set Z2-CT2 output low to avoid any false triggers. */
+ i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, 2, I8254_MODE0);
+ devpriv->ai_bipolar = pci230_ai_bipolar[range];
+ if (aref == AREF_DIFF) {
+ /* Differential. */
+ gainshift = chan * 2;
+ if (devpriv->hwver == 0) {
+ /* Original PCI230/260 expects both inputs of the
+ * differential channel to be enabled. */
+ adcen = 3 << gainshift;
+ } else {
+ /* PCI230+/260+ expects only one input of the
+ * differential channel to be enabled. */
+ adcen = 1 << gainshift;
+ }
+ adccon |= PCI230_ADC_IM_DIF;
+ } else {
+ /* Single ended. */
+ adcen = 1 << chan;
+ gainshift = chan & ~1;
+ adccon |= PCI230_ADC_IM_SE;
+ }
+ devpriv->adcg = (devpriv->adcg & ~(3 << gainshift))
+ | (pci230_ai_gain[range] << gainshift);
+ if (devpriv->ai_bipolar) {
+ adccon |= PCI230_ADC_IR_BIP;
+ } else {
+ adccon |= PCI230_ADC_IR_UNI;
+ }
+
+ /* Enable only this channel in the scan list - otherwise by default
+ * we'll get one sample from each channel. */
+ outw(adcen, dev->iobase + PCI230_ADCEN);
+
+ /* Set gain for channel. */
+ outw(devpriv->adcg, dev->iobase + PCI230_ADCG);
+
+ /* Specify uni/bip, se/diff, conversion source, and reset FIFO. */
+ devpriv->adccon = adccon;
+ outw(adccon | PCI230_ADC_FIFO_RESET, dev->iobase + PCI230_ADCCON);
+
+ /* Convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ /* Trigger conversion by toggling Z2-CT2 output (finish with
+ * output high). */
+ i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, 2,
+ I8254_MODE0);
+ i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, 2,
+ I8254_MODE1);
+
+#define TIMEOUT 100
+ /* wait for conversion to end */
+ for (i = 0; i < TIMEOUT; i++) {
+ status = inw(dev->iobase + PCI230_ADCCON);
+ if (!(status & PCI230_ADC_FIFO_EMPTY))
+ break;
+ comedi_udelay(1);
+ }
+ if (i == TIMEOUT) {
+ /* rt_printk() should be used instead of printk()
+ * whenever the code can be called from real-time. */
+ rt_printk("timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* read data */
+ data[n] = pci230_ai_read(dev);
+ }
+
+ /* return the number of samples read/written */
+ return n;
+}
+
+/*
+ * COMEDI_SUBD_AO instructions;
+ */
+static int pci230_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan, range;
+
+ /* Unpack channel and range. */
+ chan = CR_CHAN(insn->chanspec);
+ range = CR_RANGE(insn->chanspec);
+
+ /* Set range - see analogue output range table; 0 => unipolar 10V,
+ * 1 => bipolar +/-10V range scale */
+ devpriv->ao_bipolar = pci230_ao_bipolar[range];
+ outw(range, dev->iobase + PCI230_DACCON);
+
+ /* Writing a list of values to an AO channel is probably not
+ * very useful, but that's how the interface is defined. */
+ for (i = 0; i < insn->n; i++) {
+ /* Write value to DAC and store it. */
+ pci230_ao_write_nofifo(dev, data[i], chan);
+ }
+
+ /* return the number of samples read/written */
+ return i;
+}
+
+/* AO subdevices should have a read insn as well as a write insn.
+ * Usually this means copying a value stored in devpriv. */
+static int pci230_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = devpriv->ao_readback[chan];
+
+ return i;
+}
+
+static int pci230_ao_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ unsigned int tmp;
+
+ /* cmdtest tests a particular command to see if it is valid.
+ * Using the cmdtest ioctl, a user can create a valid cmd
+ * and then have it executes by the cmd ioctl.
+ *
+ * cmdtest returns 1,2,3,4 or 0, depending on which tests
+ * the command passes. */
+
+ /* Step 1: make sure trigger sources are trivially valid.
+ * "invalid source" returned by comedilib to user mode process
+ * if this fails. */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_INT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ if ((thisboard->min_hwver > 0) && (devpriv->hwver >= 2)) {
+ /*
+ * For PCI230+ hardware version 2 onwards, allow external
+ * trigger from EXTTRIG/EXTCONVCLK input (PCI230+ pin 25).
+ *
+ * FIXME: The permitted scan_begin_src values shouldn't depend
+ * on devpriv->hwver (the detected card's actual hardware
+ * version). They should only depend on thisboard->min_hwver
+ * (the static capabilities of the configured card). To fix
+ * it, a new card model, e.g. "pci230+2" would have to be
+ * defined with min_hwver set to 2. It doesn't seem worth it
+ * for this alone. At the moment, please consider
+ * scan_begin_src==TRIG_EXT support to be a bonus rather than a
+ * guarantee!
+ */
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_INT | TRIG_EXT;
+ } else {
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_INT;
+ }
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* Step 2: make sure trigger sources are unique and mutually compatible
+ * "source conflict" returned by comedilib to user mode process
+ * if this fails. */
+
+ /* these tests are true if more than one _src bit is set */
+ if ((cmd->start_src & (cmd->start_src - 1)) != 0)
+ err++;
+ if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
+ err++;
+ if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
+ err++;
+ if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
+ err++;
+ if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* Step 3: make sure arguments are trivially compatible.
+ * "invalid argument" returned by comedilib to user mode process
+ * if this fails. */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+#define MAX_SPEED_AO 8000 /* 8000 ns => 125 kHz */
+#define MIN_SPEED_AO 4294967295u /* 4294967295ns = 4.29s */
+ /*- Comedi limit due to unsigned int cmd. Driver limit
+ * = 2^16 (16bit * counter) * 1000000ns (1kHz onboard
+ * clock) = 65.536s */
+
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ if (cmd->scan_begin_arg < MAX_SPEED_AO) {
+ cmd->scan_begin_arg = MAX_SPEED_AO;
+ err++;
+ }
+ if (cmd->scan_begin_arg > MIN_SPEED_AO) {
+ cmd->scan_begin_arg = MIN_SPEED_AO;
+ err++;
+ }
+ break;
+ case TRIG_EXT:
+ /* External trigger - for PCI230+ hardware version 2 onwards. */
+ /* Trigger number must be 0. */
+ if ((cmd->scan_begin_arg & ~CR_FLAGS_MASK) != 0) {
+ cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0,
+ ~CR_FLAGS_MASK);
+ err++;
+ }
+ /* The only flags allowed are CR_EDGE and CR_INVERT. The
+ * CR_EDGE flag is ignored. */
+ if ((cmd->scan_begin_arg
+ & (CR_FLAGS_MASK & ~(CR_EDGE | CR_INVERT))) !=
+ 0) {
+ cmd->scan_begin_arg =
+ COMBINE(cmd->scan_begin_arg, 0,
+ CR_FLAGS_MASK & ~(CR_EDGE | CR_INVERT));
+ err++;
+ }
+ break;
+ default:
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ break;
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_NONE) {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments.
+ * "argument conflict" returned by comedilib to user mode process
+ * if this fails. */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ pci230_ns_to_single_timer(&cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists. */
+
+ if (cmd->chanlist && cmd->chanlist_len > 0) {
+ enum {
+ seq_err = (1 << 0),
+ range_err = (1 << 1)
+ };
+ unsigned int errors;
+ unsigned int n;
+ unsigned int chan, prev_chan;
+ unsigned int range, first_range;
+
+ prev_chan = CR_CHAN(cmd->chanlist[0]);
+ first_range = CR_RANGE(cmd->chanlist[0]);
+ errors = 0;
+ for (n = 1; n < cmd->chanlist_len; n++) {
+ chan = CR_CHAN(cmd->chanlist[n]);
+ range = CR_RANGE(cmd->chanlist[n]);
+ /* Channel numbers must strictly increase. */
+ if (chan < prev_chan) {
+ errors |= seq_err;
+ }
+ /* Ranges must be the same. */
+ if (range != first_range) {
+ errors |= range_err;
+ }
+ prev_chan = chan;
+ }
+ if (errors != 0) {
+ err++;
+ if ((errors & seq_err) != 0) {
+ DPRINTK("comedi%d: amplc_pci230: ao_cmdtest: "
+ "channel numbers must increase\n",
+ dev->minor);
+ }
+ if ((errors & range_err) != 0) {
+ DPRINTK("comedi%d: amplc_pci230: ao_cmdtest: "
+ "channels must have the same range\n",
+ dev->minor);
+ }
+ }
+ }
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int pci230_ao_inttrig_scan_begin(struct comedi_device * dev,
+ struct comedi_subdevice * s, unsigned int trig_num)
+{
+ unsigned long irqflags;
+
+ if (trig_num != 0)
+ return -EINVAL;
+
+ comedi_spin_lock_irqsave(&devpriv->ao_stop_spinlock, irqflags);
+ if (test_bit(AO_CMD_STARTED, &devpriv->state)) {
+ /* Perform scan. */
+ if (devpriv->hwver < 2) {
+ /* Not using DAC FIFO. */
+ comedi_spin_unlock_irqrestore(&devpriv->
+ ao_stop_spinlock, irqflags);
+ pci230_handle_ao_nofifo(dev, s);
+ comedi_event(dev, s);
+ } else {
+ /* Using DAC FIFO. */
+ /* Read DACSWTRIG register to trigger conversion. */
+ inw(dev->iobase + PCI230P2_DACSWTRIG);
+ comedi_spin_unlock_irqrestore(&devpriv->
+ ao_stop_spinlock, irqflags);
+ }
+ /* Delay. Should driver be responsible for this? */
+ /* XXX TODO: See if DAC busy bit can be used. */
+ comedi_udelay(8);
+ }
+
+ return 1;
+}
+
+static void pci230_ao_start(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned long irqflags;
+
+ set_bit(AO_CMD_STARTED, &devpriv->state);
+ if (!devpriv->ao_continuous && (devpriv->ao_scan_count == 0)) {
+ /* An empty acquisition! */
+ async->events |= COMEDI_CB_EOA;
+ pci230_ao_stop(dev, s);
+ comedi_event(dev, s);
+ } else {
+ if (devpriv->hwver >= 2) {
+ /* Using DAC FIFO. */
+ unsigned short scantrig;
+ int run;
+
+ /* Preload FIFO data. */
+ run = pci230_handle_ao_fifo(dev, s);
+ comedi_event(dev, s);
+ if (!run) {
+ /* Stopped. */
+ return;
+ }
+ /* Set scan trigger source. */
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ scantrig = PCI230P2_DAC_TRIG_Z2CT1;
+ break;
+ case TRIG_EXT:
+ /* Trigger on EXTTRIG/EXTCONVCLK pin. */
+ if ((cmd->scan_begin_arg & CR_INVERT) == 0) {
+ /* +ve edge */
+ scantrig = PCI230P2_DAC_TRIG_EXTP;
+ } else {
+ /* -ve edge */
+ scantrig = PCI230P2_DAC_TRIG_EXTN;
+ }
+ break;
+ case TRIG_INT:
+ scantrig = PCI230P2_DAC_TRIG_SW;
+ break;
+ default:
+ /* Shouldn't get here. */
+ scantrig = PCI230P2_DAC_TRIG_NONE;
+ break;
+ }
+ devpriv->daccon = (devpriv->daccon
+ & ~PCI230P2_DAC_TRIG_MASK) | scantrig;
+ outw(devpriv->daccon, dev->iobase + PCI230_DACCON);
+
+ }
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ if (devpriv->hwver < 2) {
+ /* Not using DAC FIFO. */
+ /* Enable CT1 timer interrupt. */
+ comedi_spin_lock_irqsave(&devpriv->isr_spinlock,
+ irqflags);
+ devpriv->int_en |= PCI230_INT_ZCLK_CT1;
+ devpriv->ier |= PCI230_INT_ZCLK_CT1;
+ outb(devpriv->ier,
+ devpriv->iobase1 + PCI230_INT_SCE);
+ comedi_spin_unlock_irqrestore(&devpriv->
+ isr_spinlock, irqflags);
+ }
+ /* Set CT1 gate high to start counting. */
+ outb(GAT_CONFIG(1, GAT_VCC),
+ devpriv->iobase1 + PCI230_ZGAT_SCE);
+ break;
+ case TRIG_INT:
+ async->inttrig = pci230_ao_inttrig_scan_begin;
+ break;
+ }
+ if (devpriv->hwver >= 2) {
+ /* Using DAC FIFO. Enable DAC FIFO interrupt. */
+ comedi_spin_lock_irqsave(&devpriv->isr_spinlock,
+ irqflags);
+ devpriv->int_en |= PCI230P2_INT_DAC;
+ devpriv->ier |= PCI230P2_INT_DAC;
+ outb(devpriv->ier, devpriv->iobase1 + PCI230_INT_SCE);
+ comedi_spin_unlock_irqrestore(&devpriv->isr_spinlock,
+ irqflags);
+ }
+ }
+}
+
+static int pci230_ao_inttrig_start(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trig_num)
+{
+ if (trig_num != 0)
+ return -EINVAL;
+
+ s->async->inttrig = NULLFUNC;
+ pci230_ao_start(dev, s);
+
+ return 1;
+}
+
+static int pci230_ao_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned short daccon;
+ unsigned int range;
+
+ /* Get the command. */
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* Claim Z2-CT1. */
+ if (!get_one_resource(dev, RES_Z2CT1, OWNER_AOCMD)) {
+ return -EBUSY;
+ }
+ }
+
+ /* Get number of scans required. */
+ if (cmd->stop_src == TRIG_COUNT) {
+ devpriv->ao_scan_count = cmd->stop_arg;
+ devpriv->ao_continuous = 0;
+ } else {
+ /* TRIG_NONE, user calls cancel. */
+ devpriv->ao_scan_count = 0;
+ devpriv->ao_continuous = 1;
+ }
+
+ /* Set range - see analogue output range table; 0 => unipolar 10V,
+ * 1 => bipolar +/-10V range scale */
+ range = CR_RANGE(cmd->chanlist[0]);
+ devpriv->ao_bipolar = pci230_ao_bipolar[range];
+ daccon = devpriv->ao_bipolar ? PCI230_DAC_OR_BIP : PCI230_DAC_OR_UNI;
+ /* Use DAC FIFO for hardware version 2 onwards. */
+ if (devpriv->hwver >= 2) {
+ unsigned short dacen;
+ unsigned int i;
+
+ dacen = 0;
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ dacen |= 1 << CR_CHAN(cmd->chanlist[i]);
+ }
+ /* Set channel scan list. */
+ outw(dacen, dev->iobase + PCI230P2_DACEN);
+ /*
+ * Enable DAC FIFO.
+ * Set DAC scan source to 'none'.
+ * Set DAC FIFO interrupt trigger level to 'not half full'.
+ * Reset DAC FIFO and clear underrun.
+ *
+ * N.B. DAC FIFO interrupts are currently disabled.
+ */
+ daccon |= PCI230P2_DAC_FIFO_EN | PCI230P2_DAC_FIFO_RESET
+ | PCI230P2_DAC_FIFO_UNDERRUN_CLEAR
+ | PCI230P2_DAC_TRIG_NONE | PCI230P2_DAC_INT_FIFO_NHALF;
+ }
+
+ /* Set DACCON. */
+ outw(daccon, dev->iobase + PCI230_DACCON);
+ /* Preserve most of DACCON apart from write-only, transient bits. */
+ devpriv->daccon = daccon
+ & ~(PCI230P2_DAC_FIFO_RESET | PCI230P2_DAC_FIFO_UNDERRUN_CLEAR);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* Set the counter timer 1 to the specified scan frequency. */
+ /* cmd->scan_begin_arg is sampling period in ns */
+ /* gate it off for now. */
+ outb(GAT_CONFIG(1, GAT_GND),
+ devpriv->iobase1 + PCI230_ZGAT_SCE);
+ pci230_ct_setup_ns_mode(dev, 1, I8254_MODE3,
+ cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
+ }
+
+ /* N.B. cmd->start_src == TRIG_INT */
+ s->async->inttrig = pci230_ao_inttrig_start;
+
+ return 0;
+}
+
+static int pci230_ai_check_scan_period(struct comedi_cmd * cmd)
+{
+ unsigned int min_scan_period, chanlist_len;
+ int err = 0;
+
+ chanlist_len = cmd->chanlist_len;
+ if (cmd->chanlist_len == 0) {
+ chanlist_len = 1;
+ }
+ min_scan_period = chanlist_len * cmd->convert_arg;
+ if ((min_scan_period < chanlist_len)
+ || (min_scan_period < cmd->convert_arg)) {
+ /* Arithmetic overflow. */
+ min_scan_period = UINT_MAX;
+ err++;
+ }
+ if (cmd->scan_begin_arg < min_scan_period) {
+ cmd->scan_begin_arg = min_scan_period;
+ err++;
+ }
+
+ return !err;
+}
+
+static int pci230_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ unsigned int tmp;
+
+ /* cmdtest tests a particular command to see if it is valid.
+ * Using the cmdtest ioctl, a user can create a valid cmd
+ * and then have it executes by the cmd ioctl.
+ *
+ * cmdtest returns 1,2,3,4,5 or 0, depending on which tests
+ * the command passes. */
+
+ /* Step 1: make sure trigger sources are trivially valid.
+ * "invalid source" returned by comedilib to user mode process
+ * if this fails. */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_INT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ /* Unfortunately, we cannot trigger a scan off an external source
+ * on the PCI260 board, since it uses the PPIC0 (DIO) input, which
+ * isn't present on the PCI260. For PCI260+ we can use the
+ * EXTTRIG/EXTCONVCLK input on pin 17 instead. */
+ if ((thisboard->have_dio) || (thisboard->min_hwver > 0)) {
+ cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER | TRIG_INT
+ | TRIG_EXT;
+ } else {
+ cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER | TRIG_INT;
+ }
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER | TRIG_INT | TRIG_EXT;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* Step 2: make sure trigger sources are unique and mutually compatible
+ * "source conflict" returned by comedilib to user mode process
+ * if this fails. */
+
+ /* these tests are true if more than one _src bit is set */
+ if ((cmd->start_src & (cmd->start_src - 1)) != 0)
+ err++;
+ if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
+ err++;
+ if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
+ err++;
+ if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
+ err++;
+ if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
+ err++;
+
+ /* If scan_begin_src is not TRIG_FOLLOW, then a monostable will be
+ * set up to generate a fixed number of timed conversion pulses. */
+ if ((cmd->scan_begin_src != TRIG_FOLLOW)
+ && (cmd->convert_src != TRIG_TIMER))
+ err++;
+
+ if (err)
+ return 2;
+
+ /* Step 3: make sure arguments are trivially compatible.
+ * "invalid argument" returned by comedilib to user mode process
+ * if this fails. */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+#define MAX_SPEED_AI_SE 3200 /* PCI230 SE: 3200 ns => 312.5 kHz */
+#define MAX_SPEED_AI_DIFF 8000 /* PCI230 DIFF: 8000 ns => 125 kHz */
+#define MAX_SPEED_AI_PLUS 4000 /* PCI230+: 4000 ns => 250 kHz */
+#define MIN_SPEED_AI 4294967295u /* 4294967295ns = 4.29s */
+ /*- Comedi limit due to unsigned int cmd. Driver limit
+ * = 2^16 (16bit * counter) * 1000000ns (1kHz onboard
+ * clock) = 65.536s */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ unsigned int max_speed_ai;
+
+ if (devpriv->hwver == 0) {
+ /* PCI230 or PCI260. Max speed depends whether
+ * single-ended or pseudo-differential. */
+ if (cmd->chanlist && (cmd->chanlist_len > 0)) {
+ /* Peek analogue reference of first channel. */
+ if (CR_AREF(cmd->chanlist[0]) == AREF_DIFF) {
+ max_speed_ai = MAX_SPEED_AI_DIFF;
+ } else {
+ max_speed_ai = MAX_SPEED_AI_SE;
+ }
+ } else {
+ /* No channel list. Assume single-ended. */
+ max_speed_ai = MAX_SPEED_AI_SE;
+ }
+ } else {
+ /* PCI230+ or PCI260+. */
+ max_speed_ai = MAX_SPEED_AI_PLUS;
+ }
+
+ if (cmd->convert_arg < max_speed_ai) {
+ cmd->convert_arg = max_speed_ai;
+ err++;
+ }
+ if (cmd->convert_arg > MIN_SPEED_AI) {
+ cmd->convert_arg = MIN_SPEED_AI;
+ err++;
+ }
+ } else if (cmd->convert_src == TRIG_EXT) {
+ /*
+ * external trigger
+ *
+ * convert_arg == (CR_EDGE | 0)
+ * => trigger on +ve edge.
+ * convert_arg == (CR_EDGE | CR_INVERT | 0)
+ * => trigger on -ve edge.
+ */
+ if ((cmd->convert_arg & CR_FLAGS_MASK) != 0) {
+ /* Trigger number must be 0. */
+ if ((cmd->convert_arg & ~CR_FLAGS_MASK) != 0) {
+ cmd->convert_arg = COMBINE(cmd->convert_arg, 0,
+ ~CR_FLAGS_MASK);
+ err++;
+ }
+ /* The only flags allowed are CR_INVERT and CR_EDGE.
+ * CR_EDGE is required. */
+ if ((cmd->convert_arg & (CR_FLAGS_MASK & ~CR_INVERT))
+ != CR_EDGE) {
+ /* Set CR_EDGE, preserve CR_INVERT. */
+ cmd->convert_arg =
+ COMBINE(cmd->start_arg, (CR_EDGE | 0),
+ CR_FLAGS_MASK & ~CR_INVERT);
+ err++;
+ }
+ } else {
+ /* Backwards compatibility with previous versions. */
+ /* convert_arg == 0 => trigger on -ve edge. */
+ /* convert_arg == 1 => trigger on +ve edge. */
+ if (cmd->convert_arg > 1) {
+ /* Default to trigger on +ve edge. */
+ cmd->convert_arg = 1;
+ err++;
+ }
+ }
+ } else {
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ if (cmd->stop_src == TRIG_NONE) {
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (cmd->scan_begin_src == TRIG_EXT) {
+ /* external "trigger" to begin each scan
+ * scan_begin_arg==0 => use PPC0 input -> gate of CT0 -> gate
+ * of CT2 (sample convert trigger is CT2) */
+ if ((cmd->scan_begin_arg & ~CR_FLAGS_MASK) != 0) {
+ cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0,
+ ~CR_FLAGS_MASK);
+ err++;
+ }
+ /* The only flag allowed is CR_EDGE, which is ignored. */
+ if ((cmd->scan_begin_arg & CR_FLAGS_MASK & ~CR_EDGE) != 0) {
+ cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0,
+ CR_FLAGS_MASK & ~CR_EDGE);
+ err++;
+ }
+ } else if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* N.B. cmd->convert_arg is also TRIG_TIMER */
+ if (!pci230_ai_check_scan_period(cmd)) {
+ err++;
+ }
+ } else {
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments.
+ * "argument conflict" returned by comedilib to user mode process
+ * if this fails. */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ pci230_ns_to_single_timer(&cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->convert_arg)
+ err++;
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* N.B. cmd->convert_arg is also TRIG_TIMER */
+ tmp = cmd->scan_begin_arg;
+ pci230_ns_to_single_timer(&cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (!pci230_ai_check_scan_period(cmd)) {
+ /* Was below minimum required. Round up. */
+ pci230_ns_to_single_timer(&cmd->scan_begin_arg,
+ TRIG_ROUND_UP);
+ pci230_ai_check_scan_period(cmd);
+ }
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists. */
+
+ if (cmd->chanlist && cmd->chanlist_len > 0) {
+ enum {
+ seq_err = 1 << 0,
+ rangepair_err = 1 << 1,
+ polarity_err = 1 << 2,
+ aref_err = 1 << 3,
+ diffchan_err = 1 << 4,
+ buggy_chan0_err = 1 << 5
+ };
+ unsigned int errors;
+ unsigned int chan, prev_chan;
+ unsigned int range, prev_range;
+ unsigned int polarity, prev_polarity;
+ unsigned int aref, prev_aref;
+ unsigned int subseq_len;
+ unsigned int n;
+
+ subseq_len = 0;
+ errors = 0;
+ prev_chan = prev_aref = prev_range = prev_polarity = 0;
+ for (n = 0; n < cmd->chanlist_len; n++) {
+ chan = CR_CHAN(cmd->chanlist[n]);
+ range = CR_RANGE(cmd->chanlist[n]);
+ aref = CR_AREF(cmd->chanlist[n]);
+ polarity = pci230_ai_bipolar[range];
+ /* Only the first half of the channels are available if
+ * differential. (These are remapped in software. In
+ * hardware, only the even channels are available.) */
+ if ((aref == AREF_DIFF)
+ && (chan >= (s->n_chan / 2))) {
+ errors |= diffchan_err;
+ }
+ if (n > 0) {
+ /* Channel numbers must strictly increase or
+ * subsequence must repeat exactly. */
+ if ((chan <= prev_chan)
+ && (subseq_len == 0)) {
+ subseq_len = n;
+ }
+ if ((subseq_len > 0)
+ && (cmd->chanlist[n] !=
+ cmd->chanlist[n %
+ subseq_len])) {
+ errors |= seq_err;
+ }
+ /* Channels must have same AREF. */
+ if (aref != prev_aref) {
+ errors |= aref_err;
+ }
+ /* Channel ranges must have same polarity. */
+ if (polarity != prev_polarity) {
+ errors |= polarity_err;
+ }
+ /* Single-ended channel pairs must have same
+ * range. */
+ if ((aref != AREF_DIFF)
+ && (((chan ^ prev_chan) & ~1) == 0)
+ && (range != prev_range)) {
+ errors |= rangepair_err;
+ }
+ }
+ prev_chan = chan;
+ prev_range = range;
+ prev_aref = aref;
+ prev_polarity = polarity;
+ }
+ if (subseq_len == 0) {
+ /* Subsequence is whole sequence. */
+ subseq_len = n;
+ }
+ /* If channel list is a repeating subsequence, need a whole
+ * number of repeats. */
+ if ((n % subseq_len) != 0) {
+ errors |= seq_err;
+ }
+ if ((devpriv->hwver > 0) && (devpriv->hwver < 4)) {
+ /*
+ * Buggy PCI230+ or PCI260+ requires channel 0 to be
+ * (first) in the sequence if the sequence contains
+ * more than one channel. Hardware versions 1 and 2
+ * have the bug. There is no hardware version 3.
+ *
+ * Actually, there are two firmwares that report
+ * themselves as hardware version 1 (the boards
+ * have different ADC chips with slightly different
+ * timing requirements, which was supposed to be
+ * invisible to software). The first one doesn't
+ * seem to have the bug, but the second one
+ * does, and we can't tell them apart!
+ */
+ if ((subseq_len > 1)
+ && (CR_CHAN(cmd->chanlist[0]) != 0)) {
+ errors |= buggy_chan0_err;
+ }
+ }
+ if (errors != 0) {
+ err++;
+ if ((errors & seq_err) != 0) {
+ DPRINTK("comedi%d: amplc_pci230: ai_cmdtest: "
+ "channel numbers must increase or "
+ "sequence must repeat exactly\n",
+ dev->minor);
+ }
+ if ((errors & rangepair_err) != 0) {
+ DPRINTK("comedi%d: amplc_pci230: ai_cmdtest: "
+ "single-ended channel pairs must "
+ "have the same range\n", dev->minor);
+ }
+ if ((errors & polarity_err) != 0) {
+ DPRINTK("comedi%d: amplc_pci230: ai_cmdtest: "
+ "channel sequence ranges must be all "
+ "bipolar or all unipolar\n",
+ dev->minor);
+ }
+ if ((errors & aref_err) != 0) {
+ DPRINTK("comedi%d: amplc_pci230: ai_cmdtest: "
+ "channel sequence analogue references "
+ "must be all the same (single-ended "
+ "or differential)\n", dev->minor);
+ }
+ if ((errors & diffchan_err) != 0) {
+ DPRINTK("comedi%d: amplc_pci230: ai_cmdtest: "
+ "differential channel number out of "
+ "range 0 to %u\n", dev->minor,
+ (s->n_chan / 2) - 1);
+ }
+ if ((errors & buggy_chan0_err) != 0) {
+ /* Use printk instead of DPRINTK here. */
+ printk("comedi: comedi%d: amplc_pci230: "
+ "ai_cmdtest: Buggy PCI230+/260+ "
+ "h/w version %u requires first channel "
+ "of multi-channel sequence to be 0 "
+ "(corrected in h/w version 4)\n",
+ dev->minor, devpriv->hwver);
+ }
+ }
+ }
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static void pci230_ai_update_fifo_trigger_level(struct comedi_device * dev,
+ struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int scanlen = cmd->scan_end_arg;
+ unsigned int wake;
+ unsigned short triglev;
+ unsigned short adccon;
+
+ if ((cmd->flags & TRIG_WAKE_EOS) != 0) {
+ /* Wake at end of scan. */
+ wake = scanlen - devpriv->ai_scan_pos;
+ } else {
+ if (devpriv->ai_continuous
+ || (devpriv->ai_scan_count
+ >= PCI230_ADC_FIFOLEVEL_HALFFULL)
+ || (scanlen >= PCI230_ADC_FIFOLEVEL_HALFFULL)) {
+ wake = PCI230_ADC_FIFOLEVEL_HALFFULL;
+ } else {
+ wake = (devpriv->ai_scan_count * scanlen)
+ - devpriv->ai_scan_pos;
+ }
+ }
+ if (wake >= PCI230_ADC_FIFOLEVEL_HALFFULL) {
+ triglev = PCI230_ADC_INT_FIFO_HALF;
+ } else {
+ if ((wake > 1) && (devpriv->hwver > 0)) {
+ /* PCI230+/260+ programmable FIFO interrupt level. */
+ if (devpriv->adcfifothresh != wake) {
+ devpriv->adcfifothresh = wake;
+ outw(wake, dev->iobase + PCI230P_ADCFFTH);
+ }
+ triglev = PCI230P_ADC_INT_FIFO_THRESH;
+ } else {
+ triglev = PCI230_ADC_INT_FIFO_NEMPTY;
+ }
+ }
+ adccon = (devpriv->adccon & ~PCI230_ADC_INT_FIFO_MASK) | triglev;
+ if (adccon != devpriv->adccon) {
+ devpriv->adccon = adccon;
+ outw(adccon, dev->iobase + PCI230_ADCCON);
+ }
+}
+
+static int pci230_ai_inttrig_convert(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trig_num)
+{
+ unsigned long irqflags;
+
+ if (trig_num != 0)
+ return -EINVAL;
+
+ comedi_spin_lock_irqsave(&devpriv->ai_stop_spinlock, irqflags);
+ if (test_bit(AI_CMD_STARTED, &devpriv->state)) {
+ unsigned int delayus;
+
+ /* Trigger conversion by toggling Z2-CT2 output. Finish
+ * with output high. */
+ i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, 2,
+ I8254_MODE0);
+ i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, 2,
+ I8254_MODE1);
+ /* Delay. Should driver be responsible for this? An
+ * alternative would be to wait until conversion is complete,
+ * but we can't tell when it's complete because the ADC busy
+ * bit has a different meaning when FIFO enabled (and when
+ * FIFO not enabled, it only works for software triggers). */
+ if (((devpriv->adccon & PCI230_ADC_IM_MASK)
+ == PCI230_ADC_IM_DIF)
+ && (devpriv->hwver == 0)) {
+ /* PCI230/260 in differential mode */
+ delayus = 8;
+ } else {
+ /* single-ended or PCI230+/260+ */
+ delayus = 4;
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->ai_stop_spinlock,
+ irqflags);
+ comedi_udelay(delayus);
+ } else {
+ comedi_spin_unlock_irqrestore(&devpriv->ai_stop_spinlock,
+ irqflags);
+ }
+
+ return 1;
+}
+
+static int pci230_ai_inttrig_scan_begin(struct comedi_device * dev,
+ struct comedi_subdevice * s, unsigned int trig_num)
+{
+ unsigned long irqflags;
+ unsigned char zgat;
+
+ if (trig_num != 0)
+ return -EINVAL;
+
+ comedi_spin_lock_irqsave(&devpriv->ai_stop_spinlock, irqflags);
+ if (test_bit(AI_CMD_STARTED, &devpriv->state)) {
+ /* Trigger scan by waggling CT0 gate source. */
+ zgat = GAT_CONFIG(0, GAT_GND);
+ outb(zgat, devpriv->iobase1 + PCI230_ZGAT_SCE);
+ zgat = GAT_CONFIG(0, GAT_VCC);
+ outb(zgat, devpriv->iobase1 + PCI230_ZGAT_SCE);
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->ai_stop_spinlock, irqflags);
+
+ return 1;
+}
+
+static void pci230_ai_start(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long irqflags;
+ unsigned short conv;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+
+ set_bit(AI_CMD_STARTED, &devpriv->state);
+ if (!devpriv->ai_continuous && (devpriv->ai_scan_count == 0)) {
+ /* An empty acquisition! */
+ async->events |= COMEDI_CB_EOA;
+ pci230_ai_stop(dev, s);
+ comedi_event(dev, s);
+ } else {
+ /* Enable ADC FIFO trigger level interrupt. */
+ comedi_spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ devpriv->int_en |= PCI230_INT_ADC;
+ devpriv->ier |= PCI230_INT_ADC;
+ outb(devpriv->ier, devpriv->iobase1 + PCI230_INT_SCE);
+ comedi_spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
+
+ /* Update conversion trigger source which is currently set
+ * to CT2 output, which is currently stuck high. */
+ switch (cmd->convert_src) {
+ default:
+ conv = PCI230_ADC_TRIG_NONE;
+ break;
+ case TRIG_TIMER:
+ /* Using CT2 output. */
+ conv = PCI230_ADC_TRIG_Z2CT2;
+ break;
+ case TRIG_EXT:
+ if ((cmd->convert_arg & CR_EDGE) != 0) {
+ if ((cmd->convert_arg & CR_INVERT) == 0) {
+ /* Trigger on +ve edge. */
+ conv = PCI230_ADC_TRIG_EXTP;
+ } else {
+ /* Trigger on -ve edge. */
+ conv = PCI230_ADC_TRIG_EXTN;
+ }
+ } else {
+ /* Backwards compatibility. */
+ if (cmd->convert_arg != 0) {
+ /* Trigger on +ve edge. */
+ conv = PCI230_ADC_TRIG_EXTP;
+ } else {
+ /* Trigger on -ve edge. */
+ conv = PCI230_ADC_TRIG_EXTN;
+ }
+ }
+ break;
+ case TRIG_INT:
+ /* Use CT2 output for software trigger due to problems
+ * in differential mode on PCI230/260. */
+ conv = PCI230_ADC_TRIG_Z2CT2;
+ break;
+ }
+ devpriv->adccon = (devpriv->adccon & ~PCI230_ADC_TRIG_MASK)
+ | conv;
+ outw(devpriv->adccon, dev->iobase + PCI230_ADCCON);
+ if (cmd->convert_src == TRIG_INT) {
+ async->inttrig = pci230_ai_inttrig_convert;
+ }
+ /* Update FIFO interrupt trigger level, which is currently
+ * set to "full". */
+ pci230_ai_update_fifo_trigger_level(dev, s);
+ if (cmd->convert_src == TRIG_TIMER) {
+ /* Update timer gates. */
+ unsigned char zgat;
+
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ /* Conversion timer CT2 needs to be gated by
+ * inverted output of monostable CT2. */
+ zgat = GAT_CONFIG(2, GAT_NOUTNM2);
+ } else {
+ /* Conversion timer CT2 needs to be gated on
+ * continuously. */
+ zgat = GAT_CONFIG(2, GAT_VCC);
+ }
+ outb(zgat, devpriv->iobase1 + PCI230_ZGAT_SCE);
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ /* Set monostable CT0 trigger source. */
+ switch (cmd->scan_begin_src) {
+ default:
+ zgat = GAT_CONFIG(0, GAT_VCC);
+ break;
+ case TRIG_EXT:
+ /*
+ * For CT0 on PCI230, the external
+ * trigger (gate) signal comes from
+ * PPC0, which is channel 16 of the DIO
+ * subdevice. The application needs to
+ * configure this as an input in order
+ * to use it as an external scan
+ * trigger.
+ */
+ zgat = GAT_CONFIG(0, GAT_EXT);
+ break;
+ case TRIG_TIMER:
+ /*
+ * Monostable CT0 triggered by rising
+ * edge on inverted output of CT1
+ * (falling edge on CT1).
+ */
+ zgat = GAT_CONFIG(0, GAT_NOUTNM2);
+ break;
+ case TRIG_INT:
+ /*
+ * Monostable CT0 is triggered by
+ * inttrig function waggling the CT0
+ * gate source.
+ */
+ zgat = GAT_CONFIG(0, GAT_VCC);
+ break;
+ }
+ outb(zgat, devpriv->iobase1 + PCI230_ZGAT_SCE);
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ /* Scan period timer CT1 needs to be
+ * gated on to start counting. */
+ zgat = GAT_CONFIG(1, GAT_VCC);
+ outb(zgat, devpriv->iobase1
+ + PCI230_ZGAT_SCE);
+ break;
+ case TRIG_INT:
+ async->inttrig =
+ pci230_ai_inttrig_scan_begin;
+ break;
+ }
+ }
+ } else if (cmd->convert_src != TRIG_INT) {
+ /* No longer need Z2-CT2. */
+ put_one_resource(dev, RES_Z2CT2, OWNER_AICMD);
+ }
+ }
+}
+
+static int pci230_ai_inttrig_start(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trig_num)
+{
+ if (trig_num != 0)
+ return -EINVAL;
+
+ s->async->inttrig = NULLFUNC;
+ pci230_ai_start(dev, s);
+
+ return 1;
+}
+
+static int pci230_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned int i, chan, range, diff;
+ unsigned int res_mask;
+ unsigned short adccon, adcen;
+ unsigned char zgat;
+
+ /* Get the command. */
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+
+ /*
+ * Determine which shared resources are needed.
+ */
+ res_mask = 0;
+ /* Need Z2-CT2 to supply a conversion trigger source at a high
+ * logic level, even if not doing timed conversions. */
+ res_mask |= (1U << RES_Z2CT2);
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ /* Using Z2-CT0 monostable to gate Z2-CT2 conversion timer */
+ res_mask |= (1U << RES_Z2CT0);
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* Using Z2-CT1 for scan frequency */
+ res_mask |= (1U << RES_Z2CT1);
+ }
+ }
+ /* Claim resources. */
+ if (!get_resources(dev, res_mask, OWNER_AICMD)) {
+ return -EBUSY;
+ }
+
+ /* Get number of scans required. */
+ if (cmd->stop_src == TRIG_COUNT) {
+ devpriv->ai_scan_count = cmd->stop_arg;
+ devpriv->ai_continuous = 0;
+ } else {
+ /* TRIG_NONE, user calls cancel. */
+ devpriv->ai_scan_count = 0;
+ devpriv->ai_continuous = 1;
+ }
+ devpriv->ai_scan_pos = 0; /* Position within scan. */
+
+ /* Steps;
+ * - Set channel scan list.
+ * - Set channel gains.
+ * - Enable and reset FIFO, specify uni/bip, se/diff, and set
+ * start conversion source to point to something at a high logic
+ * level (we use the output of counter/timer 2 for this purpose.
+ * - PAUSE to allow things to settle down.
+ * - Reset the FIFO again because it needs resetting twice and there
+ * may have been a false conversion trigger on some versions of
+ * PCI230/260 due to the start conversion source being set to a
+ * high logic level.
+ * - Enable ADC FIFO level interrupt.
+ * - Set actual conversion trigger source and FIFO interrupt trigger
+ * level.
+ * - If convert_src is TRIG_TIMER, set up the timers.
+ */
+
+ adccon = PCI230_ADC_FIFO_EN;
+ adcen = 0;
+
+ if (CR_AREF(cmd->chanlist[0]) == AREF_DIFF) {
+ /* Differential - all channels must be differential. */
+ diff = 1;
+ adccon |= PCI230_ADC_IM_DIF;
+ } else {
+ /* Single ended - all channels must be single-ended. */
+ diff = 0;
+ adccon |= PCI230_ADC_IM_SE;
+ }
+
+ range = CR_RANGE(cmd->chanlist[0]);
+ devpriv->ai_bipolar = pci230_ai_bipolar[range];
+ if (devpriv->ai_bipolar) {
+ adccon |= PCI230_ADC_IR_BIP;
+ } else {
+ adccon |= PCI230_ADC_IR_UNI;
+ }
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int gainshift;
+
+ chan = CR_CHAN(cmd->chanlist[i]);
+ range = CR_RANGE(cmd->chanlist[i]);
+ if (diff) {
+ gainshift = 2 * chan;
+ if (devpriv->hwver == 0) {
+ /* Original PCI230/260 expects both inputs of
+ * the differential channel to be enabled. */
+ adcen |= 3 << gainshift;
+ } else {
+ /* PCI230+/260+ expects only one input of the
+ * differential channel to be enabled. */
+ adcen |= 1 << gainshift;
+ }
+ } else {
+ gainshift = (chan & ~1);
+ adcen |= 1 << chan;
+ }
+ devpriv->adcg = (devpriv->adcg & ~(3 << gainshift))
+ | (pci230_ai_gain[range] << gainshift);
+ }
+
+ /* Set channel scan list. */
+ outw(adcen, dev->iobase + PCI230_ADCEN);
+
+ /* Set channel gains. */
+ outw(devpriv->adcg, dev->iobase + PCI230_ADCG);
+
+ /* Set counter/timer 2 output high for use as the initial start
+ * conversion source. */
+ i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, 2, I8254_MODE1);
+
+ /* Temporarily use CT2 output as conversion trigger source and
+ * temporarily set FIFO interrupt trigger level to 'full'. */
+ adccon |= PCI230_ADC_INT_FIFO_FULL | PCI230_ADC_TRIG_Z2CT2;
+
+ /* Enable and reset FIFO, specify FIFO trigger level full, specify
+ * uni/bip, se/diff, and temporarily set the start conversion source
+ * to CT2 output. Note that CT2 output is currently high, and this
+ * will produce a false conversion trigger on some versions of the
+ * PCI230/260, but that will be dealt with later. */
+ devpriv->adccon = adccon;
+ outw(adccon | PCI230_ADC_FIFO_RESET, dev->iobase + PCI230_ADCCON);
+
+ /* Delay */
+ /* Failure to include this will result in the first few channels'-worth
+ * of data being corrupt, normally manifesting itself by large negative
+ * voltages. It seems the board needs time to settle between the first
+ * FIFO reset (above) and the second FIFO reset (below). Setting the
+ * channel gains and scan list _before_ the first FIFO reset also
+ * helps, though only slightly. */
+ comedi_udelay(25);
+
+ /* Reset FIFO again. */
+ outw(adccon | PCI230_ADC_FIFO_RESET, dev->iobase + PCI230_ADCCON);
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ /* Set up CT2 as conversion timer, but gate it off for now.
+ * Note, counter/timer output 2 can be monitored on the
+ * connector: PCI230 pin 21, PCI260 pin 18. */
+ zgat = GAT_CONFIG(2, GAT_GND);
+ outb(zgat, devpriv->iobase1 + PCI230_ZGAT_SCE);
+ /* Set counter/timer 2 to the specified conversion period. */
+ pci230_ct_setup_ns_mode(dev, 2, I8254_MODE3, cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ /*
+ * Set up monostable on CT0 output for scan timing. A
+ * rising edge on the trigger (gate) input of CT0 will
+ * trigger the monostable, causing its output to go low
+ * for the configured period. The period depends on
+ * the conversion period and the number of conversions
+ * in the scan.
+ *
+ * Set the trigger high before setting up the
+ * monostable to stop it triggering. The trigger
+ * source will be changed later.
+ */
+ zgat = GAT_CONFIG(0, GAT_VCC);
+ outb(zgat, devpriv->iobase1 + PCI230_ZGAT_SCE);
+ pci230_ct_setup_ns_mode(dev, 0, I8254_MODE1,
+ ((uint64_t) cmd->convert_arg
+ * cmd->scan_end_arg), TRIG_ROUND_UP);
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /*
+ * Monostable on CT0 will be triggered by
+ * output of CT1 at configured scan frequency.
+ *
+ * Set up CT1 but gate it off for now.
+ */
+ zgat = GAT_CONFIG(1, GAT_GND);
+ outb(zgat, devpriv->iobase1 + PCI230_ZGAT_SCE);
+ pci230_ct_setup_ns_mode(dev, 1, I8254_MODE3,
+ cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ }
+ }
+ }
+
+ if (cmd->start_src == TRIG_INT) {
+ s->async->inttrig = pci230_ai_inttrig_start;
+ } else {
+ /* TRIG_NOW */
+ pci230_ai_start(dev, s);
+ }
+
+ return 0;
+}
+
+static unsigned int divide_ns(uint64_t ns, unsigned int timebase,
+ unsigned int round_mode)
+{
+ uint64_t div;
+ unsigned int rem;
+
+ div = ns;
+ rem = do_div(div, timebase);
+ round_mode &= TRIG_ROUND_MASK;
+ switch (round_mode) {
+ default:
+ case TRIG_ROUND_NEAREST:
+ div += (rem + (timebase / 2)) / timebase;
+ break;
+ case TRIG_ROUND_DOWN:
+ break;
+ case TRIG_ROUND_UP:
+ div += (rem + timebase - 1) / timebase;
+ break;
+ }
+ return div > UINT_MAX ? UINT_MAX : (unsigned int)div;
+}
+
+/* Given desired period in ns, returns the required internal clock source
+ * and gets the initial count. */
+static unsigned int pci230_choose_clk_count(uint64_t ns, unsigned int *count,
+ unsigned int round_mode)
+{
+ unsigned int clk_src, cnt;
+
+ for (clk_src = CLK_10MHZ;; clk_src++) {
+ cnt = divide_ns(ns, pci230_timebase[clk_src], round_mode);
+ if ((cnt <= 65536) || (clk_src == CLK_1KHZ)) {
+ break;
+ }
+ }
+ *count = cnt;
+ return clk_src;
+}
+
+static void pci230_ns_to_single_timer(unsigned int *ns, unsigned int round)
+{
+ unsigned int count;
+ unsigned int clk_src;
+
+ clk_src = pci230_choose_clk_count(*ns, &count, round);
+ *ns = count * pci230_timebase[clk_src];
+ return;
+}
+
+static void pci230_ct_setup_ns_mode(struct comedi_device * dev, unsigned int ct,
+ unsigned int mode, uint64_t ns, unsigned int round)
+{
+ unsigned int clk_src;
+ unsigned int count;
+
+ /* Set mode. */
+ i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, ct, mode);
+ /* Determine clock source and count. */
+ clk_src = pci230_choose_clk_count(ns, &count, round);
+ /* Program clock source. */
+ outb(CLK_CONFIG(ct, clk_src), devpriv->iobase1 + PCI230_ZCLK_SCE);
+ /* Set initial count. */
+ if (count >= 65536) {
+ count = 0;
+ }
+ i8254_write(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, ct, count);
+}
+
+static void pci230_cancel_ct(struct comedi_device * dev, unsigned int ct)
+{
+ i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, ct,
+ I8254_MODE1);
+ /* Counter ct, 8254 mode 1, initial count not written. */
+}
+
+/* Interrupt handler */
+static irqreturn_t pci230_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ unsigned char status_int, valid_status_int;
+ struct comedi_device *dev = (struct comedi_device *) d;
+ struct comedi_subdevice *s;
+ unsigned long irqflags;
+
+ /* Read interrupt status/enable register. */
+ status_int = inb(devpriv->iobase1 + PCI230_INT_STAT);
+
+ if (status_int == PCI230_INT_DISABLE) {
+ return IRQ_NONE;
+ }
+
+ comedi_spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ valid_status_int = devpriv->int_en & status_int;
+ /* Disable triggered interrupts.
+ * (Only those interrupts that need re-enabling, are, later in the
+ * handler). */
+ devpriv->ier = devpriv->int_en & ~status_int;
+ outb(devpriv->ier, devpriv->iobase1 + PCI230_INT_SCE);
+ devpriv->intr_running = 1;
+ devpriv->intr_cpuid = THISCPU;
+ comedi_spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
+
+ /*
+ * Check the source of interrupt and handle it.
+ * The PCI230 can cope with concurrent ADC, DAC, PPI C0 and C3
+ * interrupts. However, at present (Comedi-0.7.60) does not allow
+ * concurrent execution of commands, instructions or a mixture of the
+ * two.
+ */
+
+ if ((valid_status_int & PCI230_INT_ZCLK_CT1) != 0) {
+ s = dev->write_subdev;
+ pci230_handle_ao_nofifo(dev, s);
+ comedi_event(dev, s);
+ }
+
+ if ((valid_status_int & PCI230P2_INT_DAC) != 0) {
+ s = dev->write_subdev;
+ pci230_handle_ao_fifo(dev, s);
+ comedi_event(dev, s);
+ }
+
+ if ((valid_status_int & PCI230_INT_ADC) != 0) {
+ s = dev->read_subdev;
+ pci230_handle_ai(dev, s);
+ comedi_event(dev, s);
+ }
+
+ /* Reenable interrupts. */
+ comedi_spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ if (devpriv->ier != devpriv->int_en) {
+ devpriv->ier = devpriv->int_en;
+ outb(devpriv->ier, devpriv->iobase1 + PCI230_INT_SCE);
+ }
+ devpriv->intr_running = 0;
+ comedi_spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
+
+ return IRQ_HANDLED;
+}
+
+static void pci230_handle_ao_nofifo(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ short data;
+ int i, ret;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+
+ if (!devpriv->ao_continuous && (devpriv->ao_scan_count == 0)) {
+ return;
+ }
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ /* Read sample from Comedi's circular buffer. */
+ ret = comedi_buf_get(s->async, &data);
+ if (ret == 0) {
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ pci230_ao_stop(dev, s);
+ comedi_error(dev, "AO buffer underrun");
+ return;
+ }
+ /* Write value to DAC. */
+ pci230_ao_write_nofifo(dev, data, CR_CHAN(cmd->chanlist[i]));
+ }
+
+ async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
+ if (!devpriv->ao_continuous) {
+ devpriv->ao_scan_count--;
+ if (devpriv->ao_scan_count == 0) {
+ /* End of acquisition. */
+ async->events |= COMEDI_CB_EOA;
+ pci230_ao_stop(dev, s);
+ }
+ }
+}
+
+/* Loads DAC FIFO (if using it) from buffer. */
+/* Returns 0 if AO finished due to completion or error, 1 if still going. */
+static int pci230_handle_ao_fifo(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int num_scans;
+ unsigned int room;
+ unsigned short dacstat;
+ unsigned int i, n;
+ unsigned int bytes_per_scan;
+ unsigned int events = 0;
+ int running;
+
+ /* Get DAC FIFO status. */
+ dacstat = inw(dev->iobase + PCI230_DACCON);
+
+ /* Determine number of scans available in buffer. */
+ bytes_per_scan = cmd->chanlist_len * sizeof(short);
+ num_scans = comedi_buf_read_n_available(async) / bytes_per_scan;
+ if (!devpriv->ao_continuous) {
+ /* Fixed number of scans. */
+ if (num_scans > devpriv->ao_scan_count) {
+ num_scans = devpriv->ao_scan_count;
+ }
+ if (devpriv->ao_scan_count == 0) {
+ /* End of acquisition. */
+ events |= COMEDI_CB_EOA;
+ }
+ }
+ if (events == 0) {
+ /* Check for FIFO underrun. */
+ if ((dacstat & PCI230P2_DAC_FIFO_UNDERRUN_LATCHED) != 0) {
+ comedi_error(dev, "AO FIFO underrun");
+ events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR;
+ }
+ /* Check for buffer underrun if FIFO less than half full
+ * (otherwise there will be loads of "DAC FIFO not half full"
+ * interrupts). */
+ if ((num_scans == 0)
+ && ((dacstat & PCI230P2_DAC_FIFO_HALF) == 0)) {
+ comedi_error(dev, "AO buffer underrun");
+ events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR;
+ }
+ }
+ if (events == 0) {
+ /* Determine how much room is in the FIFO (in samples). */
+ if ((dacstat & PCI230P2_DAC_FIFO_FULL) != 0) {
+ room = PCI230P2_DAC_FIFOROOM_FULL;
+ } else if ((dacstat & PCI230P2_DAC_FIFO_HALF) != 0) {
+ room = PCI230P2_DAC_FIFOROOM_HALFTOFULL;
+ } else if ((dacstat & PCI230P2_DAC_FIFO_EMPTY) != 0) {
+ room = PCI230P2_DAC_FIFOROOM_EMPTY;
+ } else {
+ room = PCI230P2_DAC_FIFOROOM_ONETOHALF;
+ }
+ /* Convert room to number of scans that can be added. */
+ room /= cmd->chanlist_len;
+ /* Determine number of scans to process. */
+ if (num_scans > room) {
+ num_scans = room;
+ }
+ /* Process scans. */
+ for (n = 0; n < num_scans; n++) {
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ short datum;
+
+ comedi_buf_get(async, &datum);
+ pci230_ao_write_fifo(dev, datum,
+ CR_CHAN(cmd->chanlist[i]));
+ }
+ }
+ events |= COMEDI_CB_EOS | COMEDI_CB_BLOCK;
+ if (!devpriv->ao_continuous) {
+ devpriv->ao_scan_count -= num_scans;
+ if (devpriv->ao_scan_count == 0) {
+ /* All data for the command has been written
+ * to FIFO. Set FIFO interrupt trigger level
+ * to 'empty'. */
+ devpriv->daccon = (devpriv->daccon
+ & ~PCI230P2_DAC_INT_FIFO_MASK)
+ | PCI230P2_DAC_INT_FIFO_EMPTY;
+ outw(devpriv->daccon,
+ dev->iobase + PCI230_DACCON);
+ }
+ }
+ /* Check if FIFO underrun occurred while writing to FIFO. */
+ dacstat = inw(dev->iobase + PCI230_DACCON);
+ if ((dacstat & PCI230P2_DAC_FIFO_UNDERRUN_LATCHED) != 0) {
+ comedi_error(dev, "AO FIFO underrun");
+ events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR;
+ }
+ }
+ if ((events & (COMEDI_CB_EOA | COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW))
+ != 0) {
+ /* Stopping AO due to completion or error. */
+ pci230_ao_stop(dev, s);
+ running = 0;
+ } else {
+ running = 1;
+ }
+ async->events |= events;
+ return running;
+}
+
+static void pci230_handle_ai(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned int events = 0;
+ unsigned int status_fifo;
+ unsigned int i;
+ unsigned int todo;
+ unsigned int fifoamount;
+ struct comedi_async *async = s->async;
+ unsigned int scanlen = async->cmd.scan_end_arg;
+
+ /* Determine number of samples to read. */
+ if (devpriv->ai_continuous) {
+ todo = PCI230_ADC_FIFOLEVEL_HALFFULL;
+ } else if (devpriv->ai_scan_count == 0) {
+ todo = 0;
+ } else if ((devpriv->ai_scan_count > PCI230_ADC_FIFOLEVEL_HALFFULL)
+ || (scanlen > PCI230_ADC_FIFOLEVEL_HALFFULL)) {
+ todo = PCI230_ADC_FIFOLEVEL_HALFFULL;
+ } else {
+ todo = (devpriv->ai_scan_count * scanlen)
+ - devpriv->ai_scan_pos;
+ if (todo > PCI230_ADC_FIFOLEVEL_HALFFULL) {
+ todo = PCI230_ADC_FIFOLEVEL_HALFFULL;
+ }
+ }
+
+ if (todo == 0) {
+ return;
+ }
+
+ fifoamount = 0;
+ for (i = 0; i < todo; i++) {
+ if (fifoamount == 0) {
+ /* Read FIFO state. */
+ status_fifo = inw(dev->iobase + PCI230_ADCCON);
+
+ if ((status_fifo & PCI230_ADC_FIFO_FULL_LATCHED) != 0) {
+ /* Report error otherwise FIFO overruns will go
+ * unnoticed by the caller. */
+ comedi_error(dev, "AI FIFO overrun");
+ events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR;
+ break;
+ } else if ((status_fifo & PCI230_ADC_FIFO_EMPTY) != 0) {
+ /* FIFO empty. */
+ break;
+ } else if ((status_fifo & PCI230_ADC_FIFO_HALF) != 0) {
+ /* FIFO half full. */
+ fifoamount = PCI230_ADC_FIFOLEVEL_HALFFULL;
+ } else {
+ /* FIFO not empty. */
+ if (devpriv->hwver > 0) {
+ /* Read PCI230+/260+ ADC FIFO level. */
+ fifoamount = inw(dev->iobase
+ + PCI230P_ADCFFLEV);
+ if (fifoamount == 0) {
+ /* Shouldn't happen. */
+ break;
+ }
+ } else {
+ fifoamount = 1;
+ }
+ }
+ }
+
+ /* Read sample and store in Comedi's circular buffer. */
+ if (comedi_buf_put(async, pci230_ai_read(dev)) == 0) {
+ events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW;
+ comedi_error(dev, "AI buffer overflow");
+ break;
+ }
+ fifoamount--;
+ devpriv->ai_scan_pos++;
+ if (devpriv->ai_scan_pos == scanlen) {
+ /* End of scan. */
+ devpriv->ai_scan_pos = 0;
+ devpriv->ai_scan_count--;
+ async->events |= COMEDI_CB_EOS;
+ }
+ }
+
+ if (!devpriv->ai_continuous && (devpriv->ai_scan_count == 0)) {
+ /* End of acquisition. */
+ events |= COMEDI_CB_EOA;
+ } else {
+ /* More samples required, tell Comedi to block. */
+ events |= COMEDI_CB_BLOCK;
+ }
+ async->events |= events;
+
+ if ((async->events & (COMEDI_CB_EOA | COMEDI_CB_ERROR |
+ COMEDI_CB_OVERFLOW)) != 0) {
+ /* disable hardware conversions */
+ pci230_ai_stop(dev, s);
+ } else {
+ /* update FIFO interrupt trigger level */
+ pci230_ai_update_fifo_trigger_level(dev, s);
+ }
+}
+
+static void pci230_ao_stop(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long irqflags;
+ unsigned char intsrc;
+ int started;
+ struct comedi_cmd *cmd;
+
+ comedi_spin_lock_irqsave(&devpriv->ao_stop_spinlock, irqflags);
+ started = test_and_clear_bit(AO_CMD_STARTED, &devpriv->state);
+ comedi_spin_unlock_irqrestore(&devpriv->ao_stop_spinlock, irqflags);
+ if (!started) {
+ return;
+ }
+
+ cmd = &s->async->cmd;
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* Stop scan rate generator. */
+ pci230_cancel_ct(dev, 1);
+ }
+
+ /* Determine interrupt source. */
+ if (devpriv->hwver < 2) {
+ /* Not using DAC FIFO. Using CT1 interrupt. */
+ intsrc = PCI230_INT_ZCLK_CT1;
+ } else {
+ /* Using DAC FIFO interrupt. */
+ intsrc = PCI230P2_INT_DAC;
+ }
+ /* Disable interrupt and wait for interrupt routine to finish running
+ * unless we are called from the interrupt routine. */
+ comedi_spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ devpriv->int_en &= ~intsrc;
+ while (devpriv->intr_running && devpriv->intr_cpuid != THISCPU) {
+ comedi_spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
+ comedi_spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ }
+ if (devpriv->ier != devpriv->int_en) {
+ devpriv->ier = devpriv->int_en;
+ outb(devpriv->ier, devpriv->iobase1 + PCI230_INT_SCE);
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
+
+ if (devpriv->hwver >= 2) {
+ /* Using DAC FIFO. Reset FIFO, clear underrun error,
+ * disable FIFO. */
+ devpriv->daccon &= PCI230_DAC_OR_MASK;
+ outw(devpriv->daccon | PCI230P2_DAC_FIFO_RESET
+ | PCI230P2_DAC_FIFO_UNDERRUN_CLEAR,
+ dev->iobase + PCI230_DACCON);
+ }
+
+ /* Release resources. */
+ put_all_resources(dev, OWNER_AOCMD);
+}
+
+static int pci230_ao_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ pci230_ao_stop(dev, s);
+ return 0;
+}
+
+static void pci230_ai_stop(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long irqflags;
+ struct comedi_cmd *cmd;
+ int started;
+
+ comedi_spin_lock_irqsave(&devpriv->ai_stop_spinlock, irqflags);
+ started = test_and_clear_bit(AI_CMD_STARTED, &devpriv->state);
+ comedi_spin_unlock_irqrestore(&devpriv->ai_stop_spinlock, irqflags);
+ if (!started) {
+ return;
+ }
+
+ cmd = &s->async->cmd;
+ if (cmd->convert_src == TRIG_TIMER) {
+ /* Stop conversion rate generator. */
+ pci230_cancel_ct(dev, 2);
+ }
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ /* Stop scan period monostable. */
+ pci230_cancel_ct(dev, 0);
+ }
+
+ comedi_spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ /* Disable ADC interrupt and wait for interrupt routine to finish
+ * running unless we are called from the interrupt routine. */
+ devpriv->int_en &= ~PCI230_INT_ADC;
+ while (devpriv->intr_running && devpriv->intr_cpuid != THISCPU) {
+ comedi_spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
+ comedi_spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ }
+ if (devpriv->ier != devpriv->int_en) {
+ devpriv->ier = devpriv->int_en;
+ outb(devpriv->ier, devpriv->iobase1 + PCI230_INT_SCE);
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
+
+ /* Reset FIFO, disable FIFO and set start conversion source to none.
+ * Keep se/diff and bip/uni settings */
+ devpriv->adccon = (devpriv->adccon & (PCI230_ADC_IR_MASK
+ | PCI230_ADC_IM_MASK)) | PCI230_ADC_TRIG_NONE;
+ outw(devpriv->adccon | PCI230_ADC_FIFO_RESET,
+ dev->iobase + PCI230_ADCCON);
+
+ /* Release resources. */
+ put_all_resources(dev, OWNER_AICMD);
+}
+
+static int pci230_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ pci230_ai_stop(dev, s);
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/c6xdigio.c b/drivers/staging/comedi/drivers/c6xdigio.c
new file mode 100644
index 000000000000..2efffb14610d
--- /dev/null
+++ b/drivers/staging/comedi/drivers/c6xdigio.c
@@ -0,0 +1,517 @@
+/*
+ comedi/drivers/c6xdigio.c
+
+ Hardware driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card.
+ (http://robot0.ge.uiuc.edu/~spong/mecha/)
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999 Dan Block
+
+ 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.
+
+ */
+/*
+Driver: c6xdigio
+Description: Mechatronic Systems Inc. C6x_DIGIO DSP daughter card
+Author: Dan Block
+Status: unknown
+Devices: [Mechatronic Systems Inc.] C6x_DIGIO DSP daughter card (c6xdigio)
+Updated: Sun Nov 20 20:18:34 EST 2005
+
+This driver will not work with a 2.4 kernel.
+http://robot0.ge.uiuc.edu/~spong/mecha/
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <linux/pnp.h>
+
+#include "../comedidev.h"
+
+static u8 ReadByteFromHwPort(unsigned long addr)
+{
+ u8 result = inb(addr);
+ return result;
+}
+
+static void WriteByteToHwPort(unsigned long addr, u8 val)
+{
+ outb_p(val, addr);
+}
+
+#define C6XDIGIO_SIZE 3
+
+/*
+ * port offsets
+ */
+#define C6XDIGIO_PARALLEL_DATA 0
+#define C6XDIGIO_PARALLEL_STATUS 1
+#define C6XDIGIO_PARALLEL_CONTROL 2
+struct pwmbitstype {
+ unsigned sb0:2;
+ unsigned sb1:2;
+ unsigned sb2:2;
+ unsigned sb3:2;
+ unsigned sb4:2;
+};
+union pwmcmdtype {
+ unsigned cmd; // assuming here that int is 32bit
+ struct pwmbitstype bits;
+};
+struct encbitstype {
+ unsigned sb0:3;
+ unsigned sb1:3;
+ unsigned sb2:3;
+ unsigned sb3:3;
+ unsigned sb4:3;
+ unsigned sb5:3;
+ unsigned sb6:3;
+ unsigned sb7:3;
+};
+union encvaluetype {
+ unsigned value;
+ struct encbitstype bits;
+};
+
+#define C6XDIGIO_TIME_OUT 20
+
+static int c6xdigio_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int c6xdigio_detach(struct comedi_device * dev);
+struct comedi_driver driver_c6xdigio = {
+ driver_name:"c6xdigio",
+ module:THIS_MODULE,
+ attach:c6xdigio_attach,
+ detach:c6xdigio_detach,
+};
+
+static void C6X_pwmInit(unsigned long baseAddr)
+{
+ int timeout = 0;
+
+//printk("Inside C6X_pwmInit\n");
+
+ WriteByteToHwPort(baseAddr, 0x70);
+ while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
+ && (timeout < C6XDIGIO_TIME_OUT)) {
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr, 0x74);
+ timeout = 0;
+ while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
+ && (timeout < C6XDIGIO_TIME_OUT)) {
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr, 0x70);
+ timeout = 0;
+ while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
+ && (timeout < C6XDIGIO_TIME_OUT)) {
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr, 0x0);
+ timeout = 0;
+ while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
+ && (timeout < C6XDIGIO_TIME_OUT)) {
+ timeout++;
+ }
+
+}
+
+static void C6X_pwmOutput(unsigned long baseAddr, unsigned channel, int value)
+{
+ unsigned ppcmd;
+ union pwmcmdtype pwm;
+ int timeout = 0;
+ unsigned tmp;
+
+ //printk("Inside C6X_pwmOutput\n");
+
+ pwm.cmd = value;
+ if (pwm.cmd > 498)
+ pwm.cmd = 498;
+ if (pwm.cmd < 2)
+ pwm.cmd = 2;
+
+ if (channel == 0) {
+ ppcmd = 0x28;
+ } else { // if channel == 1
+ ppcmd = 0x30;
+ } /* endif */
+
+ WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb0);
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb1 + 0x4);
+ timeout = 0;
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb2);
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb3 + 0x4);
+ timeout = 0;
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb4);
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr, 0x0);
+ timeout = 0;
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+
+}
+
+static int C6X_encInput(unsigned long baseAddr, unsigned channel)
+{
+ unsigned ppcmd;
+ union encvaluetype enc;
+ int timeout = 0;
+ int tmp;
+
+ //printk("Inside C6X_encInput\n");
+
+ enc.value = 0;
+ if (channel == 0) {
+ ppcmd = 0x48;
+ } else {
+ ppcmd = 0x50;
+ }
+ WriteByteToHwPort(baseAddr, ppcmd);
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+
+ enc.bits.sb0 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr, ppcmd + 0x4);
+ timeout = 0;
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+ enc.bits.sb1 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr, ppcmd);
+ timeout = 0;
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+ enc.bits.sb2 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr, ppcmd + 0x4);
+ timeout = 0;
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+ enc.bits.sb3 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr, ppcmd);
+ timeout = 0;
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+ enc.bits.sb4 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr, ppcmd + 0x4);
+ timeout = 0;
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+ enc.bits.sb5 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr, ppcmd);
+ timeout = 0;
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+ enc.bits.sb6 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr, ppcmd + 0x4);
+ timeout = 0;
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+ enc.bits.sb7 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr, ppcmd);
+ timeout = 0;
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr, 0x0);
+ timeout = 0;
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+ tmp = ReadByteFromHwPort(baseAddr + 1);
+ timeout++;
+ }
+
+ return (enc.value ^ 0x800000);
+}
+
+static void C6X_encResetAll(unsigned long baseAddr)
+{
+ unsigned timeout = 0;
+
+//printk("Inside C6X_encResetAll\n");
+
+ WriteByteToHwPort(baseAddr, 0x68);
+ while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
+ && (timeout < C6XDIGIO_TIME_OUT)) {
+ timeout++;
+ }
+ WriteByteToHwPort(baseAddr, 0x6C);
+ timeout = 0;
+ while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
+ && (timeout < C6XDIGIO_TIME_OUT)) {
+ timeout++;
+ }
+ WriteByteToHwPort(baseAddr, 0x68);
+ timeout = 0;
+ while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
+ && (timeout < C6XDIGIO_TIME_OUT)) {
+ timeout++;
+ }
+ WriteByteToHwPort(baseAddr, 0x0);
+ timeout = 0;
+ while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
+ && (timeout < C6XDIGIO_TIME_OUT)) {
+ timeout++;
+ }
+}
+
+static int c6xdigio_pwmo_insn_read(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ printk("c6xdigio_pwmo_insn_read %x\n", insn->n);
+ return insn->n;
+}
+
+static int c6xdigio_pwmo_insn_write(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ // printk("c6xdigio_pwmo_insn_write %x\n", insn->n);
+ for (i = 0; i < insn->n; i++) {
+ C6X_pwmOutput(dev->iobase, chan, data[i]);
+ /* devpriv->ao_readback[chan] = data[i]; */
+ }
+ return i;
+}
+
+//static int c6xdigio_ei_init_insn_read(struct comedi_device *dev,
+// struct comedi_subdevice *s,
+// struct comedi_insn *insn,
+// unsigned int *data)
+//{
+// printk("c6xdigio_ei_init_insn_read %x\n", insn->n);
+// return insn->n;
+//}
+
+//static int c6xdigio_ei_init_insn_write(struct comedi_device *dev,
+// struct comedi_subdevice *s,
+// struct comedi_insn *insn,
+// unsigned int *data)
+//{
+// int i;
+// int chan = CR_CHAN(insn->chanspec);
+//
+// C6X_encResetAll( dev->iobase );
+//
+// return insn->n;
+//}
+
+static int c6xdigio_ei_insn_read(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ // printk("c6xdigio_ei__insn_read %x\n", insn->n);
+ int n;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (n = 0; n < insn->n; n++) {
+ data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff);
+ }
+
+ return n;
+}
+
+static void board_init(struct comedi_device * dev)
+{
+
+ //printk("Inside board_init\n");
+
+ C6X_pwmInit(dev->iobase);
+ C6X_encResetAll(dev->iobase);
+
+}
+
+//static void board_halt(struct comedi_device *dev) {
+// C6X_pwmInit(dev->iobase);
+//}
+
+/*
+ options[0] - I/O port
+ options[1] - irq
+ options[2] - number of encoder chips installed
+ */
+
+static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
+ /* Standard LPT Printer Port */
+ {.id = "PNP0400",.driver_data = 0},
+ /* ECP Printer Port */
+ {.id = "PNP0401",.driver_data = 0},
+ {}
+};
+
+static struct pnp_driver c6xdigio_pnp_driver = {
+ .name = "c6xdigio",
+ .id_table = c6xdigio_pnp_tbl,
+};
+
+static int c6xdigio_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int result = 0;
+ unsigned long iobase;
+ unsigned int irq;
+ struct comedi_subdevice *s;
+
+ iobase = it->options[0];
+ printk("comedi%d: c6xdigio: 0x%04lx\n", dev->minor, iobase);
+ if (!request_region(iobase, C6XDIGIO_SIZE, "c6xdigio")) {
+ printk("comedi%d: I/O port conflict\n", dev->minor);
+ return -EIO;
+ }
+ dev->iobase = iobase;
+ dev->board_name = "c6xdigio";
+
+ result = alloc_subdevices(dev, 2); // 3 with encoder_init write
+ if (result < 0)
+ return result;
+
+ // Make sure that PnP ports gets activated
+ pnp_register_driver(&c6xdigio_pnp_driver);
+
+ irq = it->options[1];
+ if (irq > 0) {
+ printk("comedi%d: irq = %u ignored\n", dev->minor, irq);
+ } else if (irq == 0) {
+ printk("comedi%d: no irq\n", dev->minor);
+ }
+
+ s = dev->subdevices + 0;
+ /* pwm output subdevice */
+ s->type = COMEDI_SUBD_AO; // Not sure what to put here
+ s->subdev_flags = SDF_WRITEABLE;
+ s->n_chan = 2;
+ /* s->trig[0] = c6xdigio_pwmo; */
+ s->insn_read = c6xdigio_pwmo_insn_read;
+ s->insn_write = c6xdigio_pwmo_insn_write;
+ s->maxdata = 500;
+ s->range_table = &range_bipolar10; // A suitable lie
+
+ s = dev->subdevices + 1;
+ /* encoder (counter) subdevice */
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
+ s->n_chan = 2;
+ /* s->trig[0] = c6xdigio_ei; */
+ s->insn_read = c6xdigio_ei_insn_read;
+ s->maxdata = 0xffffff;
+ s->range_table = &range_unknown;
+
+ // s = dev->subdevices + 2;
+ // /* pwm output subdevice */
+ // s->type = COMEDI_SUBD_COUNTER; // Not sure what to put here
+ // s->subdev_flags = SDF_WRITEABLE;
+ // s->n_chan = 1;
+ // /* s->trig[0] = c6xdigio_ei_init; */
+ // s->insn_read = c6xdigio_ei_init_insn_read;
+ // s->insn_write = c6xdigio_ei_init_insn_write;
+ // s->maxdata = 0xFFFF; // Really just a don't care
+ // s->range_table = &range_unknown; // Not sure what to put here
+
+ // I will call this init anyway but more than likely the DSP board will not be connect
+ // when device driver is loaded.
+ board_init(dev);
+
+ return 0;
+}
+
+static int c6xdigio_detach(struct comedi_device * dev)
+{
+// board_halt(dev); // may not need this
+
+ printk("comedi%d: c6xdigio: remove\n", dev->minor);
+
+ if (dev->iobase) {
+ release_region(dev->iobase, C6XDIGIO_SIZE);
+ }
+ if (dev->irq) {
+ free_irq(dev->irq, dev);
+ } // Not using IRQ so I am not sure if I need this
+ pnp_unregister_driver(&c6xdigio_pnp_driver);
+
+ return 0;
+}
+
+COMEDI_INITCLEANUP(driver_c6xdigio);
diff --git a/drivers/staging/comedi/drivers/cb_das16_cs.c b/drivers/staging/comedi/drivers/cb_das16_cs.c
new file mode 100644
index 000000000000..0bfe4c954eb5
--- /dev/null
+++ b/drivers/staging/comedi/drivers/cb_das16_cs.c
@@ -0,0 +1,976 @@
+/*
+ comedi/drivers/das16cs.c
+ Driver for Computer Boards PC-CARD DAS16/16.
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000, 2001, 2002 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: cb_das16_cs
+Description: Computer Boards PC-CARD DAS16/16
+Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs), PC-CARD DAS16/16-AO
+Author: ds
+Updated: Mon, 04 Nov 2002 20:04:21 -0800
+Status: experimental
+
+
+*/
+
+#include "../comedidev.h"
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+#include "8253.h"
+
+#define DAS16CS_SIZE 18
+
+#define DAS16CS_ADC_DATA 0
+#define DAS16CS_DIO_MUX 2
+#define DAS16CS_MISC1 4
+#define DAS16CS_MISC2 6
+#define DAS16CS_CTR0 8
+#define DAS16CS_CTR1 10
+#define DAS16CS_CTR2 12
+#define DAS16CS_CTR_CONTROL 14
+#define DAS16CS_DIO 16
+
+struct das16cs_board {
+ const char *name;
+ int device_id;
+ int n_ao_chans;
+};
+static const struct das16cs_board das16cs_boards[] = {
+ {
+ device_id:0x0000,/* unknown */
+ name: "PC-CARD DAS16/16",
+ n_ao_chans:0,
+ },
+ {
+ device_id:0x0039,
+ name: "PC-CARD DAS16/16-AO",
+ n_ao_chans:2,
+ },
+ {
+ device_id:0x4009,
+ name: "PCM-DAS16s/16",
+ n_ao_chans:0,
+ },
+};
+
+#define n_boards (sizeof(das16cs_boards)/sizeof(das16cs_boards[0]))
+#define thisboard ((const struct das16cs_board *)dev->board_ptr)
+
+struct das16cs_private {
+ struct pcmcia_device *link;
+
+ unsigned int ao_readback[2];
+ unsigned short status1;
+ unsigned short status2;
+};
+#define devpriv ((struct das16cs_private *)dev->private)
+
+static int das16cs_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int das16cs_detach(struct comedi_device * dev);
+static struct comedi_driver driver_das16cs = {
+ driver_name:"cb_das16_cs",
+ module:THIS_MODULE,
+ attach:das16cs_attach,
+ detach:das16cs_detach,
+};
+
+static struct pcmcia_device *cur_dev = NULL;
+
+static const struct comedi_lrange das16cs_ai_range = { 4, {
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ RANGE(-2.5, 2.5),
+ RANGE(-1.25, 1.25),
+ }
+};
+
+static irqreturn_t das16cs_interrupt(int irq, void *d PT_REGS_ARG);
+static int das16cs_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das16cs_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int das16cs_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int das16cs_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das16cs_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das16cs_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das16cs_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das16cs_timer_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das16cs_timer_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int get_prodid(struct comedi_device * dev, struct pcmcia_device *link)
+{
+ tuple_t tuple;
+ u_short buf[128];
+ int prodid = 0;
+
+ tuple.TupleData = (cisdata_t *) buf;
+ tuple.TupleOffset = 0;
+ tuple.TupleDataMax = 255;
+ tuple.DesiredTuple = CISTPL_MANFID;
+ tuple.Attributes = TUPLE_RETURN_COMMON;
+ if ((pcmcia_get_first_tuple(link, &tuple) == 0) &&
+ (pcmcia_get_tuple_data(link, &tuple) == 0)) {
+ prodid = le16_to_cpu(buf[1]);
+ }
+
+ return prodid;
+}
+
+static const struct das16cs_board *das16cs_probe(struct comedi_device * dev,
+ struct pcmcia_device *link)
+{
+ int id;
+ int i;
+
+ id = get_prodid(dev, link);
+
+ for (i = 0; i < n_boards; i++) {
+ if (das16cs_boards[i].device_id == id) {
+ return das16cs_boards + i;
+ }
+ }
+
+ printk("unknown board!\n");
+
+ return NULL;
+}
+
+static int das16cs_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct pcmcia_device *link;
+ struct comedi_subdevice *s;
+ int ret;
+ int i;
+
+ printk("comedi%d: cb_das16_cs: ", dev->minor);
+
+ link = cur_dev; /* XXX hack */
+ if (!link)
+ return -EIO;
+
+ dev->iobase = link->io.BasePort1;
+ printk("I/O base=0x%04lx ", dev->iobase);
+
+ printk("fingerprint:\n");
+ for (i = 0; i < 48; i += 2) {
+ printk("%04x ", inw(dev->iobase + i));
+ }
+ printk("\n");
+
+ ret = comedi_request_irq(link->irq.AssignedIRQ, das16cs_interrupt,
+ IRQF_SHARED, "cb_das16_cs", dev);
+ if (ret < 0) {
+ return ret;
+ }
+ dev->irq = link->irq.AssignedIRQ;
+ printk("irq=%u ", dev->irq);
+
+ dev->board_ptr = das16cs_probe(dev, link);
+ if (!dev->board_ptr)
+ return -EIO;
+
+ dev->board_name = thisboard->name;
+
+ if (alloc_private(dev, sizeof(struct das16cs_private)) < 0)
+ return -ENOMEM;
+
+ if (alloc_subdevices(dev, 4) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ dev->read_subdev = s;
+ /* analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
+ s->n_chan = 16;
+ s->maxdata = 0xffff;
+ s->range_table = &das16cs_ai_range;
+ s->len_chanlist = 16;
+ s->insn_read = das16cs_ai_rinsn;
+ s->do_cmd = das16cs_ai_cmd;
+ s->do_cmdtest = das16cs_ai_cmdtest;
+
+ s = dev->subdevices + 1;
+ /* analog output subdevice */
+ if (thisboard->n_ao_chans) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = thisboard->n_ao_chans;
+ s->maxdata = 0xffff;
+ s->range_table = &range_bipolar10;
+ s->insn_write = &das16cs_ao_winsn;
+ s->insn_read = &das16cs_ao_rinsn;
+ }
+
+ s = dev->subdevices + 2;
+ /* digital i/o subdevice */
+ if (1) {
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das16cs_dio_insn_bits;
+ s->insn_config = das16cs_dio_insn_config;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = dev->subdevices + 3;
+ /* timer subdevice */
+ if (0) {
+ s->type = COMEDI_SUBD_TIMER;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 1;
+ s->maxdata = 0xff;
+ s->range_table = &range_unknown;
+ s->insn_read = das16cs_timer_insn_read;
+ s->insn_config = das16cs_timer_insn_config;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ printk("attached\n");
+
+ return 1;
+}
+
+static int das16cs_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: das16cs: remove\n", dev->minor);
+
+ if (dev->irq) {
+ comedi_free_irq(dev->irq, dev);
+ }
+
+ return 0;
+}
+
+static irqreturn_t das16cs_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ //struct comedi_device *dev = d;
+ return IRQ_HANDLED;
+}
+
+/*
+ * "instructions" read/write data in "one-shot" or "software-triggered"
+ * mode.
+ */
+static int das16cs_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int to;
+ int aref;
+ int range;
+ int chan;
+ static int range_bits[] = { 0x800, 0x000, 0x100, 0x200 };
+
+ chan = CR_CHAN(insn->chanspec);
+ aref = CR_AREF(insn->chanspec);
+ range = CR_RANGE(insn->chanspec);
+
+ outw(chan, dev->iobase + 2);
+
+ devpriv->status1 &= ~0xf320;
+ devpriv->status1 |= (aref == AREF_DIFF) ? 0 : 0x0020;
+ outw(devpriv->status1, dev->iobase + 4);
+
+ devpriv->status2 &= ~0xff00;
+ devpriv->status2 |= range_bits[range];
+ outw(devpriv->status2, dev->iobase + 6);
+
+ for (i = 0; i < insn->n; i++) {
+ outw(0, dev->iobase);
+
+#define TIMEOUT 1000
+ for (to = 0; to < TIMEOUT; to++) {
+ if (inw(dev->iobase + 4) & 0x0080)
+ break;
+ }
+ if (to == TIMEOUT) {
+ printk("cb_das16_cs: ai timeout\n");
+ return -ETIME;
+ }
+ data[i] = (unsigned short)inw(dev->iobase + 0);
+ }
+
+ return i;
+}
+
+static int das16cs_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ return -EINVAL;
+}
+
+static int das16cs_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* cmdtest tests a particular command to see if it is valid.
+ * Using the cmdtest ioctl, a user can create a valid cmd
+ * and then have it executes by the cmd ioctl.
+ *
+ * cmdtest returns 1,2,3,4 or 0, depending on which tests
+ * the command passes. */
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ /* note that mutual compatiblity is not an issue here */
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_EXT)
+ err++;
+ if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+#define MAX_SPEED 10000 /* in nanoseconds */
+#define MIN_SPEED 1000000000 /* in nanoseconds */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->scan_begin_arg < MAX_SPEED) {
+ cmd->scan_begin_arg = MAX_SPEED;
+ err++;
+ }
+ if (cmd->scan_begin_arg > MIN_SPEED) {
+ cmd->scan_begin_arg = MIN_SPEED;
+ err++;
+ }
+ } else {
+ /* external trigger */
+ /* should be level/edge, hi/lo specification here */
+ /* should specify multiple external triggers */
+ if (cmd->scan_begin_arg > 9) {
+ cmd->scan_begin_arg = 9;
+ err++;
+ }
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < MAX_SPEED) {
+ cmd->convert_arg = MAX_SPEED;
+ err++;
+ }
+ if (cmd->convert_arg > MIN_SPEED) {
+ cmd->convert_arg = MIN_SPEED;
+ err++;
+ }
+ } else {
+ /* external trigger */
+ /* see above */
+ if (cmd->convert_arg > 9) {
+ cmd->convert_arg = 9;
+ err++;
+ }
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (cmd->stop_arg > 0x00ffffff) {
+ cmd->stop_arg = 0x00ffffff;
+ err++;
+ }
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ unsigned int div1, div2;
+
+ tmp = cmd->scan_begin_arg;
+ i8253_cascade_ns_to_timer(100, &div1, &div2,
+ &cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ unsigned int div1, div2;
+
+ tmp = cmd->convert_arg;
+ i8253_cascade_ns_to_timer(100, &div1, &div2,
+ &cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->convert_arg)
+ err++;
+ if (cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->scan_begin_arg <
+ cmd->convert_arg * cmd->scan_end_arg) {
+ cmd->scan_begin_arg =
+ cmd->convert_arg * cmd->scan_end_arg;
+ err++;
+ }
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int das16cs_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+ unsigned short status1;
+ unsigned short d;
+ int bit;
+
+ for (i = 0; i < insn->n; i++) {
+ devpriv->ao_readback[chan] = data[i];
+ d = data[i];
+
+ outw(devpriv->status1, dev->iobase + 4);
+ comedi_udelay(1);
+
+ status1 = devpriv->status1 & ~0xf;
+ if (chan)
+ status1 |= 0x0001;
+ else
+ status1 |= 0x0008;
+
+/* printk("0x%04x\n",status1);*/
+ outw(status1, dev->iobase + 4);
+ comedi_udelay(1);
+
+ for (bit = 15; bit >= 0; bit--) {
+ int b = (d >> bit) & 0x1;
+ b <<= 1;
+/* printk("0x%04x\n",status1 | b | 0x0000);*/
+ outw(status1 | b | 0x0000, dev->iobase + 4);
+ comedi_udelay(1);
+/* printk("0x%04x\n",status1 | b | 0x0004);*/
+ outw(status1 | b | 0x0004, dev->iobase + 4);
+ comedi_udelay(1);
+ }
+/* make high both DAC0CS and DAC1CS to load
+ new data and update analog output*/
+ outw(status1 | 0x9, dev->iobase + 4);
+ }
+
+ return i;
+}
+
+/* AO subdevices should have a read insn as well as a write insn.
+ * Usually this means copying a value stored in devpriv. */
+static int das16cs_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = devpriv->ao_readback[chan];
+
+ return i;
+}
+
+/* DIO devices are slightly special. Although it is possible to
+ * implement the insn_read/insn_write interface, it is much more
+ * useful to applications if you implement the insn_bits interface.
+ * This allows packed reading/writing of the DIO channels. The
+ * comedi core can convert between insn_bits and insn_read/write */
+static int das16cs_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+
+ outw(s->state, dev->iobase + 16);
+ }
+
+ /* on return, data[1] contains the value of the digital
+ * input and output lines. */
+ data[1] = inw(dev->iobase + 16);
+
+ return 2;
+}
+
+static int das16cs_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+ int bits;
+
+ if (chan < 4)
+ bits = 0x0f;
+ else
+ bits = 0xf0;
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= bits;
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= bits;
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (s->
+ io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ devpriv->status2 &= ~0x00c0;
+ devpriv->status2 |= (s->io_bits & 0xf0) ? 0x0080 : 0;
+ devpriv->status2 |= (s->io_bits & 0x0f) ? 0x0040 : 0;
+
+ outw(devpriv->status2, dev->iobase + 6);
+
+ return insn->n;
+}
+
+static int das16cs_timer_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ return -EINVAL;
+}
+
+static int das16cs_timer_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ return -EINVAL;
+}
+
+/* PCMCIA stuff */
+
+/*======================================================================
+
+ The following pcmcia code for the pcm-das08 is adapted from the
+ dummy_cs.c driver of the Linux PCMCIA Card Services package.
+
+ The initial developer of the original code is David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+======================================================================*/
+
+/*
+ All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
+ you do not define PCMCIA_DEBUG at all, all the debug code will be
+ left out. If you compile with PCMCIA_DEBUG=0, the debug code will
+ be present but disabled -- but it can then be enabled for specific
+ modules at load time with a 'pc_debug=#' option to insmod.
+*/
+#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0644);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+ "cb_das16_cs.c pcmcia code (David Schleef), modified from dummy_cs.c 1.31 2001/08/24 12:13:13 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+static void das16cs_pcmcia_config(struct pcmcia_device *link);
+static void das16cs_pcmcia_release(struct pcmcia_device *link);
+static int das16cs_pcmcia_suspend(struct pcmcia_device *p_dev);
+static int das16cs_pcmcia_resume(struct pcmcia_device *p_dev);
+
+/*
+ The attach() and detach() entry points are used to create and destroy
+ "instances" of the driver, where each instance represents everything
+ needed to manage one actual PCMCIA card.
+*/
+
+static int das16cs_pcmcia_attach(struct pcmcia_device *);
+static void das16cs_pcmcia_detach(struct pcmcia_device *);
+
+/*
+ You'll also need to prototype all the functions that will actually
+ be used to talk to your device. See 'memory_cs' for a good example
+ of a fully self-sufficient driver; the other drivers rely more or
+ less on other parts of the kernel.
+*/
+
+/*
+ The dev_info variable is the "key" that is used to match up this
+ device driver with appropriate cards, through the card configuration
+ database.
+*/
+
+static dev_info_t dev_info = "cb_das16_cs";
+
+struct local_info_t {
+ struct pcmcia_device *link;
+ dev_node_t node;
+ int stop;
+ struct bus_operations *bus;
+};
+
+/*======================================================================
+
+ das16cs_pcmcia_attach() creates an "instance" of the driver, allocating
+ local data structures for one device. The device is registered
+ with Card Services.
+
+ The dev_link structure is initialized, but we don't actually
+ configure the card at this point -- we wait until we receive a
+ card insertion event.
+
+======================================================================*/
+
+static int das16cs_pcmcia_attach(struct pcmcia_device *link)
+{
+ struct local_info_t *local;
+
+ DEBUG(0, "das16cs_pcmcia_attach()\n");
+
+ /* Allocate space for private device-specific data */
+ local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
+ if (!local)
+ return -ENOMEM;
+ local->link = link;
+ link->priv = local;
+
+ /* Initialize the pcmcia_device structure */
+ /* Interrupt setup */
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Handler = NULL;
+
+ link->conf.Attributes = 0;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ cur_dev = link;
+
+ das16cs_pcmcia_config(link);
+
+ return 0;
+} /* das16cs_pcmcia_attach */
+
+static void das16cs_pcmcia_detach(struct pcmcia_device *link)
+{
+ DEBUG(0, "das16cs_pcmcia_detach(0x%p)\n", link);
+
+ if (link->dev_node) {
+ ((struct local_info_t *) link->priv)->stop = 1;
+ das16cs_pcmcia_release(link);
+ }
+ /* This points to the parent struct local_info_t struct */
+ if (link->priv)
+ kfree(link->priv);
+} /* das16cs_pcmcia_detach */
+
+static void das16cs_pcmcia_config(struct pcmcia_device *link)
+{
+ struct local_info_t *dev = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ int last_fn, last_ret;
+ u_char buf[64];
+ cistpl_cftable_entry_t dflt = { 0 };
+
+ DEBUG(0, "das16cs_pcmcia_config(0x%p)\n", link);
+
+ /*
+ This reads the card's CONFIG tuple to find its configuration
+ registers.
+ */
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ tuple.Attributes = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.TupleOffset = 0;
+ last_fn = GetFirstTuple;
+ if ((last_ret = pcmcia_get_first_tuple(link, &tuple)) != 0)
+ goto cs_failed;
+ last_fn = GetTupleData;
+ if ((last_ret = pcmcia_get_tuple_data(link, &tuple)) != 0)
+ goto cs_failed;
+ last_fn = ParseTuple;
+ if ((last_ret = pcmcia_parse_tuple(link, &tuple, &parse)) != 0)
+ goto cs_failed;
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+
+ /*
+ In this loop, we scan the CIS for configuration table entries,
+ each of which describes a valid card configuration, including
+ voltage, IO window, memory window, and interrupt settings.
+
+ We make no assumptions about the card to be configured: we use
+ just the information available in the CIS. In an ideal world,
+ this would work for any PCMCIA card, but it requires a complete
+ and accurate CIS. In practice, a driver usually "knows" most of
+ these things without consulting the CIS, and most client drivers
+ will only use the CIS to fill in implementation-defined details.
+ */
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ last_fn = GetFirstTuple;
+ if ((last_ret = pcmcia_get_first_tuple(link, &tuple)) != 0)
+ goto cs_failed;
+ while (1) {
+ cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
+ if (pcmcia_get_tuple_data(link, &tuple))
+ goto next_entry;
+ if (pcmcia_parse_tuple(link, &tuple, &parse))
+ goto next_entry;
+
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+ dflt = *cfg;
+ if (cfg->index == 0)
+ goto next_entry;
+ link->conf.ConfigIndex = cfg->index;
+
+ /* Does this card need audio output? */
+/* if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ link->conf.Attributes |= CONF_ENABLE_SPKR;
+ link->conf.Status = CCSR_AUDIO_ENA;
+ }
+*/
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
+ link->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ link->io.NumPorts1 = link->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ link->io.BasePort1 = io->win[0].base;
+ link->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ link->io.Attributes2 = link->io.Attributes1;
+ link->io.BasePort2 = io->win[1].base;
+ link->io.NumPorts2 = io->win[1].len;
+ }
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(link, &link->io))
+ goto next_entry;
+ }
+
+ /* If we got this far, we're cool! */
+ break;
+
+ next_entry:
+ last_fn = GetNextTuple;
+ if ((last_ret = pcmcia_get_next_tuple(link, &tuple)) != 0)
+ goto cs_failed;
+ }
+
+ /*
+ Allocate an interrupt line. Note that this does not assign a
+ handler to the interrupt, unless the 'Handler' member of the
+ irq structure is initialized.
+ */
+ if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+ last_fn = RequestIRQ;
+ if ((last_ret = pcmcia_request_irq(link, &link->irq)) != 0)
+ goto cs_failed;
+ }
+ /*
+ This actually configures the PCMCIA socket -- setting up
+ the I/O windows and the interrupt mapping, and putting the
+ card and host interface into "Memory and IO" mode.
+ */
+ last_fn = RequestConfiguration;
+ if ((last_ret = pcmcia_request_configuration(link, &link->conf)) != 0)
+ goto cs_failed;
+
+ /*
+ At this point, the dev_node_t structure(s) need to be
+ initialized and arranged in a linked list at link->dev.
+ */
+ sprintf(dev->node.dev_name, "cb_das16_cs");
+ dev->node.major = dev->node.minor = 0;
+ link->dev_node = &dev->node;
+
+ /* Finally, report what we've done */
+ printk(KERN_INFO "%s: index 0x%02x",
+ dev->node.dev_name, link->conf.ConfigIndex);
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ printk(", irq %u", link->irq.AssignedIRQ);
+ if (link->io.NumPorts1)
+ printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+ link->io.BasePort1 + link->io.NumPorts1 - 1);
+ if (link->io.NumPorts2)
+ printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+ link->io.BasePort2 + link->io.NumPorts2 - 1);
+ printk("\n");
+
+ return;
+
+ cs_failed:
+ cs_error(link, last_fn, last_ret);
+ das16cs_pcmcia_release(link);
+} /* das16cs_pcmcia_config */
+
+static void das16cs_pcmcia_release(struct pcmcia_device *link)
+{
+ DEBUG(0, "das16cs_pcmcia_release(0x%p)\n", link);
+ pcmcia_disable_device(link);
+} /* das16cs_pcmcia_release */
+
+static int das16cs_pcmcia_suspend(struct pcmcia_device *link)
+{
+ struct local_info_t *local = link->priv;
+
+ /* Mark the device as stopped, to block IO until later */
+ local->stop = 1;
+
+ return 0;
+} /* das16cs_pcmcia_suspend */
+
+static int das16cs_pcmcia_resume(struct pcmcia_device *link)
+{
+ struct local_info_t *local = link->priv;
+
+ local->stop = 0;
+ return 0;
+} /* das16cs_pcmcia_resume */
+
+/*====================================================================*/
+
+static struct pcmcia_device_id das16cs_id_table[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039),
+ PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009),
+ PCMCIA_DEVICE_NULL
+};
+
+MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table);
+
+struct pcmcia_driver das16cs_driver = {
+ .probe = das16cs_pcmcia_attach,
+ .remove = das16cs_pcmcia_detach,
+ .suspend = das16cs_pcmcia_suspend,
+ .resume = das16cs_pcmcia_resume,
+ .id_table = das16cs_id_table,
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = dev_info,
+ },
+};
+
+static int __init init_das16cs_pcmcia_cs(void)
+{
+ DEBUG(0, "%s\n", version);
+ pcmcia_register_driver(&das16cs_driver);
+ return 0;
+}
+
+static void __exit exit_das16cs_pcmcia_cs(void)
+{
+ DEBUG(0, "das16cs_pcmcia_cs: unloading\n");
+ pcmcia_unregister_driver(&das16cs_driver);
+}
+
+int __init init_module(void)
+{
+ int ret;
+
+ ret = init_das16cs_pcmcia_cs();
+ if (ret < 0)
+ return ret;
+
+ return comedi_driver_register(&driver_das16cs);
+}
+
+void __exit cleanup_module(void)
+{
+ exit_das16cs_pcmcia_cs();
+ comedi_driver_unregister(&driver_das16cs);
+}
+
+#else
+COMEDI_INITCLEANUP(driver_das16cs);
+#endif //CONFIG_PCMCIA
diff --git a/drivers/staging/comedi/drivers/cb_pcidas.c b/drivers/staging/comedi/drivers/cb_pcidas.c
new file mode 100644
index 000000000000..fcc551651367
--- /dev/null
+++ b/drivers/staging/comedi/drivers/cb_pcidas.c
@@ -0,0 +1,1831 @@
+/*
+ comedi/drivers/cb_pcidas.c
+
+ Developed by Ivan Martinez and Frank Mori Hess, with valuable help from
+ David Schleef and the rest of the Comedi developers comunity.
+
+ Copyright (C) 2001-2003 Ivan Martinez <imr@oersted.dtu.dk>
+ Copyright (C) 2001,2002 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+************************************************************************
+*/
+/*
+Driver: cb_pcidas
+Description: MeasurementComputing PCI-DAS series with the AMCC S5933 PCI controller
+Author: Ivan Martinez <imr@oersted.dtu.dk>,
+ Frank Mori Hess <fmhess@users.sourceforge.net>
+Updated: 2003-3-11
+Devices: [Measurement Computing] PCI-DAS1602/16 (cb_pcidas),
+ PCI-DAS1602/16jr, PCI-DAS1602/12, PCI-DAS1200, PCI-DAS1200jr,
+ PCI-DAS1000, PCI-DAS1001, PCI_DAS1002
+
+Status:
+ There are many reports of the driver being used with most of the
+ supported cards. Despite no detailed log is maintained, it can
+ be said that the driver is quite tested and stable.
+
+ The boards may be autocalibrated using the comedi_calibrate
+ utility.
+
+Configuration options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first supported
+ PCI device found will be used.
+
+For commands, the scanned channels must be consecutive
+(i.e. 4-5-6-7, 2-3-4,...), and must all have the same
+range and aref.
+*/
+/*
+
+TODO:
+
+analog triggering on 1602 series
+*/
+
+#include "../comedidev.h"
+#include <linux/delay.h>
+
+#include "8253.h"
+#include "8255.h"
+#include "amcc_s5933.h"
+#include "comedi_pci.h"
+#include "comedi_fc.h"
+
+#undef CB_PCIDAS_DEBUG // disable debugging code
+//#define CB_PCIDAS_DEBUG // enable debugging code
+
+// PCI vendor number of ComputerBoards/MeasurementComputing
+#define PCI_VENDOR_ID_CB 0x1307
+#define TIMER_BASE 100 // 10MHz master clock
+#define AI_BUFFER_SIZE 1024 // maximum fifo size of any supported board
+#define AO_BUFFER_SIZE 1024 // maximum fifo size of any supported board
+#define NUM_CHANNELS_8800 8
+#define NUM_CHANNELS_7376 1
+#define NUM_CHANNELS_8402 2
+#define NUM_CHANNELS_DAC08 1
+
+/* PCI-DAS base addresses */
+
+// indices of base address regions
+#define S5933_BADRINDEX 0
+#define CONT_STAT_BADRINDEX 1
+#define ADC_FIFO_BADRINDEX 2
+#define PACER_BADRINDEX 3
+#define AO_BADRINDEX 4
+// sizes of io regions
+#define CONT_STAT_SIZE 10
+#define ADC_FIFO_SIZE 4
+#define PACER_SIZE 12
+#define AO_SIZE 4
+
+/* Control/Status registers */
+#define INT_ADCFIFO 0 // INTERRUPT / ADC FIFO register
+#define INT_EOS 0x1 // interrupt end of scan
+#define INT_FHF 0x2 // interrupt fifo half full
+#define INT_FNE 0x3 // interrupt fifo not empty
+#define INT_MASK 0x3 // mask of interrupt select bits
+#define INTE 0x4 // interrupt enable
+#define DAHFIE 0x8 // dac half full interrupt enable
+#define EOAIE 0x10 // end of aquisition interrupt enable
+#define DAHFI 0x20 // dac half full read status / write interrupt clear
+#define EOAI 0x40 // read end of acq. interrupt status / write clear
+#define INT 0x80 // read interrupt status / write clear
+#define EOBI 0x200 // read end of burst interrupt status
+#define ADHFI 0x400 // read half-full interrupt status
+#define ADNEI 0x800 // read fifo not empty interrupt latch status
+#define ADNE 0x1000 // read, fifo not empty (realtime, not latched) status
+#define DAEMIE 0x1000 // write, dac empty interrupt enable
+#define LADFUL 0x2000 // read fifo overflow / write clear
+#define DAEMI 0x4000 // dac fifo empty interrupt status / write clear
+
+#define ADCMUX_CONT 2 // ADC CHANNEL MUX AND CONTROL register
+#define BEGIN_SCAN(x) ((x) & 0xf)
+#define END_SCAN(x) (((x) & 0xf) << 4)
+#define GAIN_BITS(x) (((x) & 0x3) << 8)
+#define UNIP 0x800 // Analog front-end unipolar for range
+#define SE 0x400 // Inputs in single-ended mode
+#define PACER_MASK 0x3000 // pacer source bits
+#define PACER_INT 0x1000 // internal pacer
+#define PACER_EXT_FALL 0x2000 // external falling edge
+#define PACER_EXT_RISE 0x3000 // external rising edge
+#define EOC 0x4000 // adc not busy
+
+#define TRIG_CONTSTAT 4 // TRIGGER CONTROL/STATUS register
+#define SW_TRIGGER 0x1 // software start trigger
+#define EXT_TRIGGER 0x2 // external start trigger
+#define ANALOG_TRIGGER 0x3 // external analog trigger
+#define TRIGGER_MASK 0x3 // mask of bits that determine start trigger
+#define TGEN 0x10 // enable external start trigger
+#define BURSTE 0x20 // burst mode enable
+#define XTRCL 0x80 // clear external trigger
+
+#define CALIBRATION_REG 6 // CALIBRATION register
+#define SELECT_8800_BIT 0x100 // select 8800 caldac
+#define SELECT_TRIMPOT_BIT 0x200 // select ad7376 trim pot
+#define SELECT_DAC08_BIT 0x400 // select dac08 caldac
+#define CAL_SRC_BITS(x) (((x) & 0x7) << 11)
+#define CAL_EN_BIT 0x4000 // read calibration source instead of analog input channel 0
+#define SERIAL_DATA_IN_BIT 0x8000 // serial data stream going to 8800 and 7376
+
+#define DAC_CSR 0x8 // dac control and status register
+enum dac_csr_bits {
+ DACEN = 0x2, // dac enable
+ DAC_MODE_UPDATE_BOTH = 0x80, // update both dacs when dac0 is written
+};
+static inline unsigned int DAC_RANGE(unsigned int channel, unsigned int range)
+{
+ return (range & 0x3) << (8 + 2 * (channel & 0x1));
+}
+static inline unsigned int DAC_RANGE_MASK(unsigned int channel)
+{
+ return 0x3 << (8 + 2 * (channel & 0x1));
+};
+
+// bits for 1602 series only
+enum dac_csr_bits_1602 {
+ DAC_EMPTY = 0x1, // dac fifo empty, read, write clear
+ DAC_START = 0x4, // start/arm dac fifo operations
+ DAC_PACER_MASK = 0x18, // bits that set dac pacer source
+ DAC_PACER_INT = 0x8, // dac internal pacing
+ DAC_PACER_EXT_FALL = 0x10, // dac external pacing, falling edge
+ DAC_PACER_EXT_RISE = 0x18, // dac external pacing, rising edge
+};
+static inline unsigned int DAC_CHAN_EN(unsigned int channel)
+{
+ return 1 << (5 + (channel & 0x1)); // enable channel 0 or 1
+};
+
+/* analog input fifo */
+#define ADCDATA 0 // ADC DATA register
+#define ADCFIFOCLR 2 // ADC FIFO CLEAR
+
+// pacer, counter, dio registers
+#define ADC8254 0
+#define DIO_8255 4
+#define DAC8254 8
+
+// analog output registers for 100x, 1200 series
+static inline unsigned int DAC_DATA_REG(unsigned int channel)
+{
+ return 2 * (channel & 0x1);
+}
+
+/* analog output registers for 1602 series*/
+#define DACDATA 0 // DAC DATA register
+#define DACFIFOCLR 2 // DAC FIFO CLEAR
+
+// bit in hexadecimal representation of range index that indicates unipolar input range
+#define IS_UNIPOLAR 0x4
+// analog input ranges for most boards
+static const struct comedi_lrange cb_pcidas_ranges = {
+ 8,
+ {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+// pci-das1001 input ranges
+static const struct comedi_lrange cb_pcidas_alt_ranges = {
+ 8,
+ {
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.01),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01)
+ }
+};
+
+// analog output ranges
+static const struct comedi_lrange cb_pcidas_ao_ranges = {
+ 4,
+ {
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(10),
+ }
+};
+
+enum trimpot_model {
+ AD7376,
+ AD8402,
+};
+
+struct cb_pcidas_board {
+ const char *name;
+ unsigned short device_id;
+ int ai_se_chans; // Inputs in single-ended mode
+ int ai_diff_chans; // Inputs in differential mode
+ int ai_bits; // analog input resolution
+ int ai_speed; // fastest conversion period in ns
+ int ao_nchan; // number of analog out channels
+ int has_ao_fifo; // analog output has fifo
+ int ao_scan_speed; // analog output speed for 1602 series (for a scan, not conversion)
+ int fifo_size; // number of samples fifo can hold
+ const struct comedi_lrange *ranges;
+ enum trimpot_model trimpot;
+ unsigned has_dac08:1;
+};
+
+static const struct cb_pcidas_board cb_pcidas_boards[] = {
+ {
+ name: "pci-das1602/16",
+ device_id:0x1,
+ ai_se_chans:16,
+ ai_diff_chans:8,
+ ai_bits: 16,
+ ai_speed:5000,
+ ao_nchan:2,
+ has_ao_fifo:1,
+ ao_scan_speed:10000,
+ fifo_size:512,
+ ranges: &cb_pcidas_ranges,
+ trimpot: AD8402,
+ has_dac08:1,
+ },
+ {
+ name: "pci-das1200",
+ device_id:0xF,
+ ai_se_chans:16,
+ ai_diff_chans:8,
+ ai_bits: 12,
+ ai_speed:3200,
+ ao_nchan:2,
+ has_ao_fifo:0,
+ fifo_size:1024,
+ ranges: &cb_pcidas_ranges,
+ trimpot: AD7376,
+ has_dac08:0,
+ },
+ {
+ name: "pci-das1602/12",
+ device_id:0x10,
+ ai_se_chans:16,
+ ai_diff_chans:8,
+ ai_bits: 12,
+ ai_speed:3200,
+ ao_nchan:2,
+ has_ao_fifo:1,
+ ao_scan_speed:4000,
+ fifo_size:1024,
+ ranges: &cb_pcidas_ranges,
+ trimpot: AD7376,
+ has_dac08:0,
+ },
+ {
+ name: "pci-das1200/jr",
+ device_id:0x19,
+ ai_se_chans:16,
+ ai_diff_chans:8,
+ ai_bits: 12,
+ ai_speed:3200,
+ ao_nchan:0,
+ has_ao_fifo:0,
+ fifo_size:1024,
+ ranges: &cb_pcidas_ranges,
+ trimpot: AD7376,
+ has_dac08:0,
+ },
+ {
+ name: "pci-das1602/16/jr",
+ device_id:0x1C,
+ ai_se_chans:16,
+ ai_diff_chans:8,
+ ai_bits: 16,
+ ai_speed:5000,
+ ao_nchan:0,
+ has_ao_fifo:0,
+ fifo_size:512,
+ ranges: &cb_pcidas_ranges,
+ trimpot: AD8402,
+ has_dac08:1,
+ },
+ {
+ name: "pci-das1000",
+ device_id:0x4C,
+ ai_se_chans:16,
+ ai_diff_chans:8,
+ ai_bits: 12,
+ ai_speed:4000,
+ ao_nchan:0,
+ has_ao_fifo:0,
+ fifo_size:1024,
+ ranges: &cb_pcidas_ranges,
+ trimpot: AD7376,
+ has_dac08:0,
+ },
+ {
+ name: "pci-das1001",
+ device_id:0x1a,
+ ai_se_chans:16,
+ ai_diff_chans:8,
+ ai_bits: 12,
+ ai_speed:6800,
+ ao_nchan:2,
+ has_ao_fifo:0,
+ fifo_size:1024,
+ ranges: &cb_pcidas_alt_ranges,
+ trimpot: AD7376,
+ has_dac08:0,
+ },
+ {
+ name: "pci-das1002",
+ device_id:0x1b,
+ ai_se_chans:16,
+ ai_diff_chans:8,
+ ai_bits: 12,
+ ai_speed:6800,
+ ao_nchan:2,
+ has_ao_fifo:0,
+ fifo_size:1024,
+ ranges: &cb_pcidas_ranges,
+ trimpot: AD7376,
+ has_dac08:0,
+ },
+};
+
+// Number of boards in cb_pcidas_boards
+#define N_BOARDS (sizeof(cb_pcidas_boards) / sizeof(struct cb_pcidas_board))
+
+static DEFINE_PCI_DEVICE_TABLE(cb_pcidas_pci_table) = {
+ {PCI_VENDOR_ID_CB, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CB, 0x000f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CB, 0x0010, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CB, 0x0019, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CB, 0x001c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CB, 0x004c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CB, 0x001a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CB, 0x001b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, cb_pcidas_pci_table);
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct cb_pcidas_board *)dev->board_ptr)
+
+/* 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 struct comedi_device struct. */
+struct cb_pcidas_private {
+ /* would be useful for a PCI device */
+ struct pci_dev *pci_dev;
+ // base addresses
+ unsigned long s5933_config;
+ unsigned long control_status;
+ unsigned long adc_fifo;
+ unsigned long pacer_counter_dio;
+ unsigned long ao_registers;
+ // divisors of master clock for analog input pacing
+ unsigned int divisor1;
+ unsigned int divisor2;
+ volatile unsigned int count; // number of analog input samples remaining
+ volatile unsigned int adc_fifo_bits; // bits to write to interupt/adcfifo register
+ volatile unsigned int s5933_intcsr_bits; // bits to write to amcc s5933 interrupt control/status register
+ volatile unsigned int ao_control_bits; // bits to write to ao control and status register
+ short ai_buffer[AI_BUFFER_SIZE];
+ short ao_buffer[AO_BUFFER_SIZE];
+ // divisors of master clock for analog output pacing
+ unsigned int ao_divisor1;
+ unsigned int ao_divisor2;
+ volatile unsigned int ao_count; // number of analog output samples remaining
+ int ao_value[2]; // remember what the analog outputs are set to, to allow readback
+ unsigned int caldac_value[NUM_CHANNELS_8800]; // for readback of caldac
+ unsigned int trimpot_value[NUM_CHANNELS_8402]; // for readback of trimpot
+ unsigned int dac08_value;
+ unsigned int calibration_source;
+};
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((struct cb_pcidas_private *)dev->private)
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int cb_pcidas_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int cb_pcidas_detach(struct comedi_device * dev);
+static struct comedi_driver driver_cb_pcidas = {
+ driver_name:"cb_pcidas",
+ module:THIS_MODULE,
+ attach:cb_pcidas_attach,
+ detach:cb_pcidas_detach,
+};
+
+static int cb_pcidas_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ai_config_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int cb_pcidas_ao_nofifo_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int cb_pcidas_ao_fifo_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int cb_pcidas_ao_readback_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int cb_pcidas_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int cb_pcidas_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int cb_pcidas_ao_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int cb_pcidas_ao_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *subdev,
+ unsigned int trig_num);
+static int cb_pcidas_ao_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static irqreturn_t cb_pcidas_interrupt(int irq, void *d PT_REGS_ARG);
+static void handle_ao_interrupt(struct comedi_device * dev, unsigned int status);
+static int cb_pcidas_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static int cb_pcidas_ao_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static void cb_pcidas_load_counters(struct comedi_device * dev, unsigned int *ns,
+ int round_flags);
+static int eeprom_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int caldac_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int caldac_write_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int trimpot_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int cb_pcidas_trimpot_write(struct comedi_device * dev, unsigned int channel,
+ unsigned int value);
+static int trimpot_write_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dac08_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dac08_write(struct comedi_device * dev, unsigned int value);
+static int dac08_write_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int caldac_8800_write(struct comedi_device * dev, unsigned int address,
+ uint8_t value);
+static int trimpot_7376_write(struct comedi_device * dev, uint8_t value);
+static int trimpot_8402_write(struct comedi_device * dev, unsigned int channel,
+ uint8_t value);
+static int nvram_read(struct comedi_device * dev, unsigned int address,
+ uint8_t * data);
+
+static inline unsigned int cal_enable_bits(struct comedi_device * dev)
+{
+ return CAL_EN_BIT | CAL_SRC_BITS(devpriv->calibration_source);
+}
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board.
+ */
+static int cb_pcidas_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ struct pci_dev *pcidev;
+ int index;
+ int i;
+
+ printk("comedi%d: cb_pcidas: ", dev->minor);
+
+/*
+ * Allocate the private structure area.
+ */
+ if (alloc_private(dev, sizeof(struct cb_pcidas_private)) < 0)
+ return -ENOMEM;
+
+/*
+ * Probe the device to determine what device in the series it is.
+ */
+ printk("\n");
+
+ for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pcidev != NULL;
+ pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
+ // is it not a computer boards card?
+ if (pcidev->vendor != PCI_VENDOR_ID_CB)
+ continue;
+ // loop through cards supported by this driver
+ for (index = 0; index < N_BOARDS; index++) {
+ if (cb_pcidas_boards[index].device_id != pcidev->device)
+ continue;
+ // was a particular bus/slot requested?
+ if (it->options[0] || it->options[1]) {
+ // are we on the wrong bus/slot?
+ if (pcidev->bus->number != it->options[0] ||
+ PCI_SLOT(pcidev->devfn) !=
+ it->options[1]) {
+ continue;
+ }
+ }
+ devpriv->pci_dev = pcidev;
+ dev->board_ptr = cb_pcidas_boards + index;
+ goto found;
+ }
+ }
+
+ printk("No supported ComputerBoards/MeasurementComputing card found on "
+ "requested position\n");
+ return -EIO;
+
+ found:
+
+ printk("Found %s on bus %i, slot %i\n", cb_pcidas_boards[index].name,
+ pcidev->bus->number, PCI_SLOT(pcidev->devfn));
+
+ /*
+ * Enable PCI device and reserve I/O ports.
+ */
+ if (comedi_pci_enable(pcidev, "cb_pcidas")) {
+ printk(" Failed to enable PCI device and request regions\n");
+ return -EIO;
+ }
+ /*
+ * Initialize devpriv->control_status and devpriv->adc_fifo to point to
+ * their base address.
+ */
+ devpriv->s5933_config =
+ pci_resource_start(devpriv->pci_dev, S5933_BADRINDEX);
+ devpriv->control_status =
+ pci_resource_start(devpriv->pci_dev, CONT_STAT_BADRINDEX);
+ devpriv->adc_fifo =
+ pci_resource_start(devpriv->pci_dev, ADC_FIFO_BADRINDEX);
+ devpriv->pacer_counter_dio =
+ pci_resource_start(devpriv->pci_dev, PACER_BADRINDEX);
+ if (thisboard->ao_nchan) {
+ devpriv->ao_registers =
+ pci_resource_start(devpriv->pci_dev, AO_BADRINDEX);
+ }
+ // disable and clear interrupts on amcc s5933
+ outl(INTCSR_INBOX_INTR_STATUS,
+ devpriv->s5933_config + AMCC_OP_REG_INTCSR);
+
+ // get irq
+ if (comedi_request_irq(devpriv->pci_dev->irq, cb_pcidas_interrupt,
+ IRQF_SHARED, "cb_pcidas", dev)) {
+ printk(" unable to allocate irq %d\n", devpriv->pci_dev->irq);
+ return -EINVAL;
+ }
+ dev->irq = devpriv->pci_dev->irq;
+
+ //Initialize dev->board_name
+ dev->board_name = thisboard->name;
+
+/*
+ * Allocate the subdevice structures.
+ */
+ if (alloc_subdevices(dev, 7) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ /* analog input subdevice */
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
+ /* WARNING: Number of inputs in differential mode is ignored */
+ s->n_chan = thisboard->ai_se_chans;
+ s->len_chanlist = thisboard->ai_se_chans;
+ s->maxdata = (1 << thisboard->ai_bits) - 1;
+ s->range_table = thisboard->ranges;
+ s->insn_read = cb_pcidas_ai_rinsn;
+ s->insn_config = ai_config_insn;
+ s->do_cmd = cb_pcidas_ai_cmd;
+ s->do_cmdtest = cb_pcidas_ai_cmdtest;
+ s->cancel = cb_pcidas_cancel;
+
+ /* analog output subdevice */
+ s = dev->subdevices + 1;
+ if (thisboard->ao_nchan) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = thisboard->ao_nchan;
+ // analog out resolution is the same as analog input resolution, so use ai_bits
+ s->maxdata = (1 << thisboard->ai_bits) - 1;
+ s->range_table = &cb_pcidas_ao_ranges;
+ s->insn_read = cb_pcidas_ao_readback_insn;
+ if (thisboard->has_ao_fifo) {
+ dev->write_subdev = s;
+ s->subdev_flags |= SDF_CMD_WRITE;
+ s->insn_write = cb_pcidas_ao_fifo_winsn;
+ s->do_cmdtest = cb_pcidas_ao_cmdtest;
+ s->do_cmd = cb_pcidas_ao_cmd;
+ s->cancel = cb_pcidas_ao_cancel;
+ } else {
+ s->insn_write = cb_pcidas_ao_nofifo_winsn;
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* 8255 */
+ s = dev->subdevices + 2;
+ subdev_8255_init(dev, s, NULL, devpriv->pacer_counter_dio + DIO_8255);
+
+ // serial EEPROM,
+ s = dev->subdevices + 3;
+ s->type = COMEDI_SUBD_MEMORY;
+ s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
+ s->n_chan = 256;
+ s->maxdata = 0xff;
+ s->insn_read = eeprom_read_insn;
+
+ // 8800 caldac
+ s = dev->subdevices + 4;
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = NUM_CHANNELS_8800;
+ s->maxdata = 0xff;
+ s->insn_read = caldac_read_insn;
+ s->insn_write = caldac_write_insn;
+ for (i = 0; i < s->n_chan; i++)
+ caldac_8800_write(dev, i, s->maxdata / 2);
+
+ // trim potentiometer
+ s = dev->subdevices + 5;
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ if (thisboard->trimpot == AD7376) {
+ s->n_chan = NUM_CHANNELS_7376;
+ s->maxdata = 0x7f;
+ } else {
+ s->n_chan = NUM_CHANNELS_8402;
+ s->maxdata = 0xff;
+ }
+ s->insn_read = trimpot_read_insn;
+ s->insn_write = trimpot_write_insn;
+ for (i = 0; i < s->n_chan; i++)
+ cb_pcidas_trimpot_write(dev, i, s->maxdata / 2);
+
+ // dac08 caldac
+ s = dev->subdevices + 6;
+ if (thisboard->has_dac08) {
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = NUM_CHANNELS_DAC08;
+ s->insn_read = dac08_read_insn;
+ s->insn_write = dac08_write_insn;
+ s->maxdata = 0xff;
+ dac08_write(dev, s->maxdata / 2);
+ } else
+ s->type = COMEDI_SUBD_UNUSED;
+
+ // make sure mailbox 4 is empty
+ inl(devpriv->s5933_config + AMCC_OP_REG_IMB4);
+ /* Set bits to enable incoming mailbox interrupts on amcc s5933. */
+ devpriv->s5933_intcsr_bits =
+ INTCSR_INBOX_BYTE(3) | INTCSR_INBOX_SELECT(3) |
+ INTCSR_INBOX_FULL_INT;
+ // clear and enable interrupt on amcc s5933
+ outl(devpriv->s5933_intcsr_bits | INTCSR_INBOX_INTR_STATUS,
+ devpriv->s5933_config + AMCC_OP_REG_INTCSR);
+
+ return 1;
+}
+
+/*
+ * cb_pcidas_detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int cb_pcidas_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: cb_pcidas: remove\n", dev->minor);
+
+ if (devpriv) {
+ if (devpriv->s5933_config) {
+ // disable and clear interrupts on amcc s5933
+ outl(INTCSR_INBOX_INTR_STATUS,
+ devpriv->s5933_config + AMCC_OP_REG_INTCSR);
+#ifdef CB_PCIDAS_DEBUG
+ rt_printk("detaching, incsr is 0x%x\n",
+ inl(devpriv->s5933_config +
+ AMCC_OP_REG_INTCSR));
+#endif
+ }
+ }
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+ if (dev->subdevices)
+ subdev_8255_cleanup(dev, dev->subdevices + 2);
+ if (devpriv && devpriv->pci_dev) {
+ if (devpriv->s5933_config) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ }
+
+ return 0;
+}
+
+/*
+ * "instructions" read/write data in "one-shot" or "software-triggered"
+ * mode.
+ */
+static int cb_pcidas_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, i;
+ unsigned int bits;
+ static const int timeout = 10000;
+ int channel;
+ // enable calibration input if appropriate
+ if (insn->chanspec & CR_ALT_SOURCE) {
+ outw(cal_enable_bits(dev),
+ devpriv->control_status + CALIBRATION_REG);
+ channel = 0;
+ } else {
+ outw(0, devpriv->control_status + CALIBRATION_REG);
+ channel = CR_CHAN(insn->chanspec);
+ }
+ // set mux limits and gain
+ bits = BEGIN_SCAN(channel) |
+ END_SCAN(channel) | GAIN_BITS(CR_RANGE(insn->chanspec));
+ // set unipolar/bipolar
+ if (CR_RANGE(insn->chanspec) & IS_UNIPOLAR)
+ bits |= UNIP;
+ // set singleended/differential
+ if (CR_AREF(insn->chanspec) != AREF_DIFF)
+ bits |= SE;
+ outw(bits, devpriv->control_status + ADCMUX_CONT);
+
+ /* clear fifo */
+ outw(0, devpriv->adc_fifo + ADCFIFOCLR);
+
+ /* convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion */
+ outw(0, devpriv->adc_fifo + ADCDATA);
+
+ /* wait for conversion to end */
+ /* return -ETIMEDOUT if there is a timeout */
+ for (i = 0; i < timeout; i++) {
+ if (inw(devpriv->control_status + ADCMUX_CONT) & EOC)
+ break;
+ }
+ if (i == timeout)
+ return -ETIMEDOUT;
+
+ /* read data */
+ data[n] = inw(devpriv->adc_fifo + ADCDATA);
+ }
+
+ /* return the number of samples read/written */
+ return n;
+}
+
+static int ai_config_calibration_source(struct comedi_device * dev, unsigned int * data)
+{
+ static const int num_calibration_sources = 8;
+ unsigned int source = data[1];
+
+ if (source >= num_calibration_sources) {
+ printk("invalid calibration source: %i\n", source);
+ return -EINVAL;
+ }
+
+ devpriv->calibration_source = source;
+
+ return 2;
+}
+
+static int ai_config_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int id = data[0];
+
+ switch (id) {
+ case INSN_CONFIG_ALT_SOURCE:
+ return ai_config_calibration_source(dev, data);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return -EINVAL;
+}
+
+// analog output insn for pcidas-1000 and 1200 series
+static int cb_pcidas_ao_nofifo_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int channel;
+ unsigned long flags;
+
+ // set channel and range
+ channel = CR_CHAN(insn->chanspec);
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->ao_control_bits &=
+ ~DAC_MODE_UPDATE_BOTH & ~DAC_RANGE_MASK(channel);
+ devpriv->ao_control_bits |=
+ DACEN | DAC_RANGE(channel, CR_RANGE(insn->chanspec));
+ outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ // remember value for readback
+ devpriv->ao_value[channel] = data[0];
+ // send data
+ outw(data[0], devpriv->ao_registers + DAC_DATA_REG(channel));
+
+ return 1;
+}
+
+// analog output insn for pcidas-1602 series
+static int cb_pcidas_ao_fifo_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int channel;
+ unsigned long flags;
+
+ // clear dac fifo
+ outw(0, devpriv->ao_registers + DACFIFOCLR);
+
+ // set channel and range
+ channel = CR_CHAN(insn->chanspec);
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->ao_control_bits &=
+ ~DAC_CHAN_EN(0) & ~DAC_CHAN_EN(1) & ~DAC_RANGE_MASK(channel) &
+ ~DAC_PACER_MASK;
+ devpriv->ao_control_bits |=
+ DACEN | DAC_RANGE(channel,
+ CR_RANGE(insn->chanspec)) | DAC_CHAN_EN(channel) | DAC_START;
+ outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ // remember value for readback
+ devpriv->ao_value[channel] = data[0];
+ // send data
+ outw(data[0], devpriv->ao_registers + DACDATA);
+
+ return 1;
+}
+
+// analog output readback insn
+// XXX loses track of analog output value back after an analog ouput command is executed
+static int cb_pcidas_ao_readback_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = devpriv->ao_value[CR_CHAN(insn->chanspec)];
+
+ return 1;
+}
+
+static int eeprom_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ uint8_t nvram_data;
+ int retval;
+
+ retval = nvram_read(dev, CR_CHAN(insn->chanspec), &nvram_data);
+ if (retval < 0)
+ return retval;
+
+ data[0] = nvram_data;
+
+ return 1;
+}
+
+static int caldac_write_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ const unsigned int channel = CR_CHAN(insn->chanspec);
+
+ return caldac_8800_write(dev, channel, data[0]);
+}
+
+static int caldac_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = devpriv->caldac_value[CR_CHAN(insn->chanspec)];
+
+ return 1;
+}
+
+/* 1602/16 pregain offset */
+static int dac08_write(struct comedi_device * dev, unsigned int value)
+{
+ if (devpriv->dac08_value == value)
+ return 1;
+
+ devpriv->dac08_value = value;
+
+ outw(cal_enable_bits(dev) | (value & 0xff),
+ devpriv->control_status + CALIBRATION_REG);
+ comedi_udelay(1);
+ outw(cal_enable_bits(dev) | SELECT_DAC08_BIT | (value & 0xff),
+ devpriv->control_status + CALIBRATION_REG);
+ comedi_udelay(1);
+ outw(cal_enable_bits(dev) | (value & 0xff),
+ devpriv->control_status + CALIBRATION_REG);
+ comedi_udelay(1);
+
+ return 1;
+}
+
+static int dac08_write_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ return dac08_write(dev, data[0]);
+}
+
+static int dac08_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = devpriv->dac08_value;
+
+ return 1;
+}
+
+static int cb_pcidas_trimpot_write(struct comedi_device * dev,
+ unsigned int channel, unsigned int value)
+{
+ if (devpriv->trimpot_value[channel] == value)
+ return 1;
+
+ devpriv->trimpot_value[channel] = value;
+ switch (thisboard->trimpot) {
+ case AD7376:
+ trimpot_7376_write(dev, value);
+ break;
+ case AD8402:
+ trimpot_8402_write(dev, channel, value);
+ break;
+ default:
+ comedi_error(dev, "driver bug?");
+ return -1;
+ break;
+ }
+
+ return 1;
+}
+
+static int trimpot_write_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int channel = CR_CHAN(insn->chanspec);
+
+ return cb_pcidas_trimpot_write(dev, channel, data[0]);
+}
+
+static int trimpot_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int channel = CR_CHAN(insn->chanspec);
+
+ data[0] = devpriv->trimpot_value[channel];
+
+ return 1;
+}
+
+static int cb_pcidas_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+ int i, gain, start_chan;
+
+ /* cmdtest tests a particular command to see if it is valid.
+ * Using the cmdtest ioctl, a user can create a valid cmd
+ * and then have it executes by the cmd ioctl.
+ *
+ * cmdtest returns 1,2,3,4 or 0, depending on which tests
+ * the command passes. */
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_EXT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER | TRIG_NOW | TRIG_EXT;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
+ err++;
+ if (cmd->scan_begin_src != TRIG_FOLLOW &&
+ cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_EXT)
+ err++;
+ if (cmd->convert_src != TRIG_TIMER &&
+ cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ // make sure trigger sources are compatible with each other
+ if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW)
+ err++;
+ if (cmd->scan_begin_src != TRIG_FOLLOW && cmd->convert_src != TRIG_NOW)
+ err++;
+ if (cmd->start_src == TRIG_EXT &&
+ (cmd->convert_src == TRIG_EXT
+ || cmd->scan_begin_src == TRIG_EXT))
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->scan_begin_arg <
+ thisboard->ai_speed * cmd->chanlist_len) {
+ cmd->scan_begin_arg =
+ thisboard->ai_speed * cmd->chanlist_len;
+ err++;
+ }
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < thisboard->ai_speed) {
+ cmd->convert_arg = thisboard->ai_speed;
+ err++;
+ }
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_NONE) {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE,
+ &(devpriv->divisor1), &(devpriv->divisor2),
+ &(cmd->scan_begin_arg), cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE,
+ &(devpriv->divisor1), &(devpriv->divisor2),
+ &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->convert_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ // check channel/gain list against card's limitations
+ if (cmd->chanlist) {
+ gain = CR_RANGE(cmd->chanlist[0]);
+ start_chan = CR_CHAN(cmd->chanlist[0]);
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ if (CR_CHAN(cmd->chanlist[i]) !=
+ (start_chan + i) % s->n_chan) {
+ comedi_error(dev,
+ "entries in chanlist must be consecutive channels, counting upwards\n");
+ err++;
+ }
+ if (CR_RANGE(cmd->chanlist[i]) != gain) {
+ comedi_error(dev,
+ "entries in chanlist must all have the same gain\n");
+ err++;
+ }
+ }
+ }
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int cb_pcidas_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int bits;
+ unsigned long flags;
+
+ // make sure CAL_EN_BIT is disabled
+ outw(0, devpriv->control_status + CALIBRATION_REG);
+ // initialize before settings pacer source and count values
+ outw(0, devpriv->control_status + TRIG_CONTSTAT);
+ // clear fifo
+ outw(0, devpriv->adc_fifo + ADCFIFOCLR);
+
+ // set mux limits, gain and pacer source
+ bits = BEGIN_SCAN(CR_CHAN(cmd->chanlist[0])) |
+ END_SCAN(CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1])) |
+ GAIN_BITS(CR_RANGE(cmd->chanlist[0]));
+ // set unipolar/bipolar
+ if (CR_RANGE(cmd->chanlist[0]) & IS_UNIPOLAR)
+ bits |= UNIP;
+ // set singleended/differential
+ if (CR_AREF(cmd->chanlist[0]) != AREF_DIFF)
+ bits |= SE;
+ // set pacer source
+ if (cmd->convert_src == TRIG_EXT || cmd->scan_begin_src == TRIG_EXT)
+ bits |= PACER_EXT_RISE;
+ else
+ bits |= PACER_INT;
+ outw(bits, devpriv->control_status + ADCMUX_CONT);
+
+#ifdef CB_PCIDAS_DEBUG
+ rt_printk("comedi: sent 0x%x to adcmux control\n", bits);
+#endif
+
+ // load counters
+ if (cmd->convert_src == TRIG_TIMER)
+ cb_pcidas_load_counters(dev, &cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ else if (cmd->scan_begin_src == TRIG_TIMER)
+ cb_pcidas_load_counters(dev, &cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+
+ // set number of conversions
+ if (cmd->stop_src == TRIG_COUNT) {
+ devpriv->count = cmd->chanlist_len * cmd->stop_arg;
+ }
+ // enable interrupts
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->adc_fifo_bits |= INTE;
+ devpriv->adc_fifo_bits &= ~INT_MASK;
+ if (cmd->flags & TRIG_WAKE_EOS) {
+ if (cmd->convert_src == TRIG_NOW && cmd->chanlist_len > 1)
+ devpriv->adc_fifo_bits |= INT_EOS; // interrupt end of burst
+ else
+ devpriv->adc_fifo_bits |= INT_FNE; // interrupt fifo not empty
+ } else {
+ devpriv->adc_fifo_bits |= INT_FHF; //interrupt fifo half full
+ }
+#ifdef CB_PCIDAS_DEBUG
+ rt_printk("comedi: adc_fifo_bits are 0x%x\n", devpriv->adc_fifo_bits);
+#endif
+ // enable (and clear) interrupts
+ outw(devpriv->adc_fifo_bits | EOAI | INT | LADFUL,
+ devpriv->control_status + INT_ADCFIFO);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ // set start trigger and burst mode
+ bits = 0;
+ if (cmd->start_src == TRIG_NOW)
+ bits |= SW_TRIGGER;
+ else if (cmd->start_src == TRIG_EXT)
+ bits |= EXT_TRIGGER | TGEN | XTRCL;
+ else {
+ comedi_error(dev, "bug!");
+ return -1;
+ }
+ if (cmd->convert_src == TRIG_NOW && cmd->chanlist_len > 1)
+ bits |= BURSTE;
+ outw(bits, devpriv->control_status + TRIG_CONTSTAT);
+#ifdef CB_PCIDAS_DEBUG
+ rt_printk("comedi: sent 0x%x to trig control\n", bits);
+#endif
+
+ return 0;
+}
+
+static int cb_pcidas_ao_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* cmdtest tests a particular command to see if it is valid.
+ * Using the cmdtest ioctl, a user can create a valid cmd
+ * and then have it executes by the cmd ioctl.
+ *
+ * cmdtest returns 1,2,3,4 or 0, depending on which tests
+ * the command passes. */
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_INT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_EXT)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->scan_begin_arg < thisboard->ao_scan_speed) {
+ cmd->scan_begin_arg = thisboard->ao_scan_speed;
+ err++;
+ }
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_NONE) {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE,
+ &(devpriv->ao_divisor1), &(devpriv->ao_divisor2),
+ &(cmd->scan_begin_arg), cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ // check channel/gain list against card's limitations
+ if (cmd->chanlist && cmd->chanlist_len > 1) {
+ if (CR_CHAN(cmd->chanlist[0]) != 0 ||
+ CR_CHAN(cmd->chanlist[1]) != 1) {
+ comedi_error(dev,
+ "channels must be ordered channel 0, channel 1 in chanlist\n");
+ err++;
+ }
+ }
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int cb_pcidas_ao_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int i;
+ unsigned long flags;
+
+ // set channel limits, gain
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ // enable channel
+ devpriv->ao_control_bits |=
+ DAC_CHAN_EN(CR_CHAN(cmd->chanlist[i]));
+ // set range
+ devpriv->ao_control_bits |= DAC_RANGE(CR_CHAN(cmd->chanlist[i]),
+ CR_RANGE(cmd->chanlist[i]));
+ }
+
+ // disable analog out before settings pacer source and count values
+ outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ // clear fifo
+ outw(0, devpriv->ao_registers + DACFIFOCLR);
+
+ // load counters
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE,
+ &(devpriv->ao_divisor1), &(devpriv->ao_divisor2),
+ &(cmd->scan_begin_arg), cmd->flags);
+
+ /* Write the values of ctr1 and ctr2 into counters 1 and 2 */
+ i8254_load(devpriv->pacer_counter_dio + DAC8254, 0, 1,
+ devpriv->ao_divisor1, 2);
+ i8254_load(devpriv->pacer_counter_dio + DAC8254, 0, 2,
+ devpriv->ao_divisor2, 2);
+ }
+ // set number of conversions
+ if (cmd->stop_src == TRIG_COUNT) {
+ devpriv->ao_count = cmd->chanlist_len * cmd->stop_arg;
+ }
+ // set pacer source
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ devpriv->ao_control_bits |= DAC_PACER_INT;
+ break;
+ case TRIG_EXT:
+ devpriv->ao_control_bits |= DAC_PACER_EXT_RISE;
+ break;
+ default:
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ comedi_error(dev, "error setting dac pacer source");
+ return -1;
+ break;
+ }
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ async->inttrig = cb_pcidas_ao_inttrig;
+
+ return 0;
+}
+
+static int cb_pcidas_ao_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ unsigned int num_bytes, num_points = thisboard->fifo_size;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned long flags;
+
+ if (trig_num != 0)
+ return -EINVAL;
+
+ // load up fifo
+ if (cmd->stop_src == TRIG_COUNT && devpriv->ao_count < num_points)
+ num_points = devpriv->ao_count;
+
+ num_bytes = cfc_read_array_from_buffer(s, devpriv->ao_buffer,
+ num_points * sizeof(short));
+ num_points = num_bytes / sizeof(short);
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ devpriv->ao_count -= num_points;
+ }
+ // write data to board's fifo
+ outsw(devpriv->ao_registers + DACDATA, devpriv->ao_buffer, num_bytes);
+
+ // enable dac half-full and empty interrupts
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->adc_fifo_bits |= DAEMIE | DAHFIE;
+#ifdef CB_PCIDAS_DEBUG
+ rt_printk("comedi: adc_fifo_bits are 0x%x\n", devpriv->adc_fifo_bits);
+#endif
+ // enable and clear interrupts
+ outw(devpriv->adc_fifo_bits | DAEMI | DAHFI,
+ devpriv->control_status + INT_ADCFIFO);
+
+ // start dac
+ devpriv->ao_control_bits |= DAC_START | DACEN | DAC_EMPTY;
+ outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR);
+#ifdef CB_PCIDAS_DEBUG
+ rt_printk("comedi: sent 0x%x to dac control\n",
+ devpriv->ao_control_bits);
+#endif
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ async->inttrig = NULL;
+
+ return 0;
+}
+
+static irqreturn_t cb_pcidas_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = (struct comedi_device *) d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async;
+ int status, s5933_status;
+ int half_fifo = thisboard->fifo_size / 2;
+ unsigned int num_samples, i;
+ static const int timeout = 10000;
+ unsigned long flags;
+
+ if (dev->attached == 0) {
+ return IRQ_NONE;
+ }
+
+ async = s->async;
+ async->events = 0;
+
+ s5933_status = inl(devpriv->s5933_config + AMCC_OP_REG_INTCSR);
+#ifdef CB_PCIDAS_DEBUG
+ rt_printk("intcsr 0x%x\n", s5933_status);
+ rt_printk("mbef 0x%x\n", inl(devpriv->s5933_config + AMCC_OP_REG_MBEF));
+#endif
+
+ if ((INTCSR_INTR_ASSERTED & s5933_status) == 0)
+ return IRQ_NONE;
+
+ // make sure mailbox 4 is empty
+ inl_p(devpriv->s5933_config + AMCC_OP_REG_IMB4);
+ // clear interrupt on amcc s5933
+ outl(devpriv->s5933_intcsr_bits | INTCSR_INBOX_INTR_STATUS,
+ devpriv->s5933_config + AMCC_OP_REG_INTCSR);
+
+ status = inw(devpriv->control_status + INT_ADCFIFO);
+#ifdef CB_PCIDAS_DEBUG
+ if ((status & (INT | EOAI | LADFUL | DAHFI | DAEMI)) == 0) {
+ comedi_error(dev, "spurious interrupt");
+ }
+#endif
+
+ // check for analog output interrupt
+ if (status & (DAHFI | DAEMI)) {
+ handle_ao_interrupt(dev, status);
+ }
+ // check for analog input interrupts
+ // if fifo half-full
+ if (status & ADHFI) {
+ // read data
+ num_samples = half_fifo;
+ if (async->cmd.stop_src == TRIG_COUNT &&
+ num_samples > devpriv->count) {
+ num_samples = devpriv->count;
+ }
+ insw(devpriv->adc_fifo + ADCDATA, devpriv->ai_buffer,
+ num_samples);
+ cfc_write_array_to_buffer(s, devpriv->ai_buffer,
+ num_samples * sizeof(short));
+ devpriv->count -= num_samples;
+ if (async->cmd.stop_src == TRIG_COUNT && devpriv->count == 0) {
+ async->events |= COMEDI_CB_EOA;
+ cb_pcidas_cancel(dev, s);
+ }
+ // clear half-full interrupt latch
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ outw(devpriv->adc_fifo_bits | INT,
+ devpriv->control_status + INT_ADCFIFO);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ // else if fifo not empty
+ } else if (status & (ADNEI | EOBI)) {
+ for (i = 0; i < timeout; i++) {
+ // break if fifo is empty
+ if ((ADNE & inw(devpriv->control_status +
+ INT_ADCFIFO)) == 0)
+ break;
+ cfc_write_to_buffer(s, inw(devpriv->adc_fifo));
+ if (async->cmd.stop_src == TRIG_COUNT && --devpriv->count == 0) { /* end of acquisition */
+ cb_pcidas_cancel(dev, s);
+ async->events |= COMEDI_CB_EOA;
+ break;
+ }
+ }
+ // clear not-empty interrupt latch
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ outw(devpriv->adc_fifo_bits | INT,
+ devpriv->control_status + INT_ADCFIFO);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ } else if (status & EOAI) {
+ comedi_error(dev,
+ "bug! encountered end of aquisition interrupt?");
+ // clear EOA interrupt latch
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ outw(devpriv->adc_fifo_bits | EOAI,
+ devpriv->control_status + INT_ADCFIFO);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ }
+ //check for fifo overflow
+ if (status & LADFUL) {
+ comedi_error(dev, "fifo overflow");
+ // clear overflow interrupt latch
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ outw(devpriv->adc_fifo_bits | LADFUL,
+ devpriv->control_status + INT_ADCFIFO);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ cb_pcidas_cancel(dev, s);
+ async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ }
+
+ comedi_event(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+static void handle_ao_interrupt(struct comedi_device * dev, unsigned int status)
+{
+ struct comedi_subdevice *s = dev->write_subdev;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int half_fifo = thisboard->fifo_size / 2;
+ unsigned int num_points;
+ unsigned int flags;
+
+ async->events = 0;
+
+ if (status & DAEMI) {
+ // clear dac empty interrupt latch
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ outw(devpriv->adc_fifo_bits | DAEMI,
+ devpriv->control_status + INT_ADCFIFO);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ if (inw(devpriv->ao_registers + DAC_CSR) & DAC_EMPTY) {
+ if (cmd->stop_src == TRIG_NONE ||
+ (cmd->stop_src == TRIG_COUNT
+ && devpriv->ao_count)) {
+ comedi_error(dev, "dac fifo underflow");
+ cb_pcidas_ao_cancel(dev, s);
+ async->events |= COMEDI_CB_ERROR;
+ }
+ async->events |= COMEDI_CB_EOA;
+ }
+ } else if (status & DAHFI) {
+ unsigned int num_bytes;
+
+ // figure out how many points we are writing to fifo
+ num_points = half_fifo;
+ if (cmd->stop_src == TRIG_COUNT &&
+ devpriv->ao_count < num_points)
+ num_points = devpriv->ao_count;
+ num_bytes =
+ cfc_read_array_from_buffer(s, devpriv->ao_buffer,
+ num_points * sizeof(short));
+ num_points = num_bytes / sizeof(short);
+
+ if (async->cmd.stop_src == TRIG_COUNT) {
+ devpriv->ao_count -= num_points;
+ }
+ // write data to board's fifo
+ outsw(devpriv->ao_registers + DACDATA, devpriv->ao_buffer,
+ num_points);
+ // clear half-full interrupt latch
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ outw(devpriv->adc_fifo_bits | DAHFI,
+ devpriv->control_status + INT_ADCFIFO);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ }
+
+ comedi_event(dev, s);
+}
+
+/* cancel analog input command */
+static int cb_pcidas_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ // disable interrupts
+ devpriv->adc_fifo_bits &= ~INTE & ~EOAIE;
+ outw(devpriv->adc_fifo_bits, devpriv->control_status + INT_ADCFIFO);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ // disable start trigger source and burst mode
+ outw(0, devpriv->control_status + TRIG_CONTSTAT);
+ // software pacer source
+ outw(0, devpriv->control_status + ADCMUX_CONT);
+
+ return 0;
+}
+
+/* cancel analog output command */
+static int cb_pcidas_ao_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ // disable interrupts
+ devpriv->adc_fifo_bits &= ~DAHFIE & ~DAEMIE;
+ outw(devpriv->adc_fifo_bits, devpriv->control_status + INT_ADCFIFO);
+
+ // disable output
+ devpriv->ao_control_bits &= ~DACEN & ~DAC_PACER_MASK;
+ outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return 0;
+}
+
+static void cb_pcidas_load_counters(struct comedi_device * dev, unsigned int *ns,
+ int rounding_flags)
+{
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
+ &(devpriv->divisor2), ns, rounding_flags & TRIG_ROUND_MASK);
+
+ /* Write the values of ctr1 and ctr2 into counters 1 and 2 */
+ i8254_load(devpriv->pacer_counter_dio + ADC8254, 0, 1,
+ devpriv->divisor1, 2);
+ i8254_load(devpriv->pacer_counter_dio + ADC8254, 0, 2,
+ devpriv->divisor2, 2);
+}
+
+static void write_calibration_bitstream(struct comedi_device * dev,
+ unsigned int register_bits, unsigned int bitstream,
+ unsigned int bitstream_length)
+{
+ static const int write_delay = 1;
+ unsigned int bit;
+
+ for (bit = 1 << (bitstream_length - 1); bit; bit >>= 1) {
+ if (bitstream & bit)
+ register_bits |= SERIAL_DATA_IN_BIT;
+ else
+ register_bits &= ~SERIAL_DATA_IN_BIT;
+ comedi_udelay(write_delay);
+ outw(register_bits, devpriv->control_status + CALIBRATION_REG);
+ }
+}
+
+static int caldac_8800_write(struct comedi_device * dev, unsigned int address,
+ uint8_t value)
+{
+ static const int num_caldac_channels = 8;
+ static const int bitstream_length = 11;
+ unsigned int bitstream = ((address & 0x7) << 8) | value;
+ static const int caldac_8800_comedi_udelay = 1;
+
+ if (address >= num_caldac_channels) {
+ comedi_error(dev, "illegal caldac channel");
+ return -1;
+ }
+
+ if (value == devpriv->caldac_value[address])
+ return 1;
+
+ devpriv->caldac_value[address] = value;
+
+ write_calibration_bitstream(dev, cal_enable_bits(dev), bitstream,
+ bitstream_length);
+
+ comedi_udelay(caldac_8800_comedi_udelay);
+ outw(cal_enable_bits(dev) | SELECT_8800_BIT,
+ devpriv->control_status + CALIBRATION_REG);
+ comedi_udelay(caldac_8800_comedi_udelay);
+ outw(cal_enable_bits(dev), devpriv->control_status + CALIBRATION_REG);
+
+ return 1;
+}
+
+static int trimpot_7376_write(struct comedi_device * dev, uint8_t value)
+{
+ static const int bitstream_length = 7;
+ unsigned int bitstream = value & 0x7f;
+ unsigned int register_bits;
+ static const int ad7376_comedi_udelay = 1;
+
+ register_bits = cal_enable_bits(dev) | SELECT_TRIMPOT_BIT;
+ comedi_udelay(ad7376_comedi_udelay);
+ outw(register_bits, devpriv->control_status + CALIBRATION_REG);
+
+ write_calibration_bitstream(dev, register_bits, bitstream,
+ bitstream_length);
+
+ comedi_udelay(ad7376_comedi_udelay);
+ outw(cal_enable_bits(dev), devpriv->control_status + CALIBRATION_REG);
+
+ return 0;
+}
+
+/* For 1602/16 only
+ * ch 0 : adc gain
+ * ch 1 : adc postgain offset */
+static int trimpot_8402_write(struct comedi_device * dev, unsigned int channel,
+ uint8_t value)
+{
+ static const int bitstream_length = 10;
+ unsigned int bitstream = ((channel & 0x3) << 8) | (value & 0xff);
+ unsigned int register_bits;
+ static const int ad8402_comedi_udelay = 1;
+
+ register_bits = cal_enable_bits(dev) | SELECT_TRIMPOT_BIT;
+ comedi_udelay(ad8402_comedi_udelay);
+ outw(register_bits, devpriv->control_status + CALIBRATION_REG);
+
+ write_calibration_bitstream(dev, register_bits, bitstream,
+ bitstream_length);
+
+ comedi_udelay(ad8402_comedi_udelay);
+ outw(cal_enable_bits(dev), devpriv->control_status + CALIBRATION_REG);
+
+ return 0;
+}
+
+static int wait_for_nvram_ready(unsigned long s5933_base_addr)
+{
+ static const int timeout = 1000;
+ unsigned int i;
+
+ for (i = 0; i < timeout; i++) {
+ if ((inb(s5933_base_addr +
+ AMCC_OP_REG_MCSR_NVCMD) & MCSR_NV_BUSY)
+ == 0)
+ return 0;
+ comedi_udelay(1);
+ }
+ return -1;
+}
+
+static int nvram_read(struct comedi_device * dev, unsigned int address, uint8_t * data)
+{
+ unsigned long iobase = devpriv->s5933_config;
+
+ if (wait_for_nvram_ready(iobase) < 0)
+ return -ETIMEDOUT;
+
+ outb(MCSR_NV_ENABLE | MCSR_NV_LOAD_LOW_ADDR,
+ iobase + AMCC_OP_REG_MCSR_NVCMD);
+ outb(address & 0xff, iobase + AMCC_OP_REG_MCSR_NVDATA);
+ outb(MCSR_NV_ENABLE | MCSR_NV_LOAD_HIGH_ADDR,
+ iobase + AMCC_OP_REG_MCSR_NVCMD);
+ outb((address >> 8) & 0xff, iobase + AMCC_OP_REG_MCSR_NVDATA);
+ outb(MCSR_NV_ENABLE | MCSR_NV_READ, iobase + AMCC_OP_REG_MCSR_NVCMD);
+
+ if (wait_for_nvram_ready(iobase) < 0)
+ return -ETIMEDOUT;
+
+ *data = inb(iobase + AMCC_OP_REG_MCSR_NVDATA);
+
+ return 0;
+}
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_PCI_INITCLEANUP(driver_cb_pcidas, cb_pcidas_pci_table);
diff --git a/drivers/staging/comedi/drivers/cb_pcidas64.c b/drivers/staging/comedi/drivers/cb_pcidas64.c
new file mode 100644
index 000000000000..1a580bd2247a
--- /dev/null
+++ b/drivers/staging/comedi/drivers/cb_pcidas64.c
@@ -0,0 +1,4222 @@
+/*
+ comedi/drivers/cb_pcidas64.c
+ This is a driver for the ComputerBoards/MeasurementComputing PCI-DAS
+ 64xx, 60xx, and 4020 cards.
+
+ Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+ Copyright (C) 2001, 2002 Frank Mori Hess
+
+ Thanks also go to the following people:
+
+ Steve Rosenbluth, for providing the source code for
+ his pci-das6402 driver, and source code for working QNX pci-6402
+ drivers by Greg Laird and Mariusz Bogacz. None of the code was
+ used directly here, but it was useful as an additional source of
+ documentation on how to program the boards.
+
+ John Sims, for much testing and feedback on pcidas-4020 support.
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+************************************************************************/
+
+/*
+
+Driver: cb_pcidas64
+Description: MeasurementComputing PCI-DAS64xx, 60XX, and 4020 series with the PLX 9080 PCI controller
+Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+Status: works
+Updated: 2002-10-09
+Devices: [Measurement Computing] PCI-DAS6402/16 (cb_pcidas64),
+ PCI-DAS6402/12, PCI-DAS64/M1/16, PCI-DAS64/M2/16,
+ PCI-DAS64/M3/16, PCI-DAS6402/16/JR, PCI-DAS64/M1/16/JR,
+ PCI-DAS64/M2/16/JR, PCI-DAS64/M3/16/JR, PCI-DAS64/M1/14,
+ PCI-DAS64/M2/14, PCI-DAS64/M3/14, PCI-DAS6013, PCI-DAS6014,
+ PCI-DAS6023, PCI-DAS6025, PCI-DAS6030,
+ PCI-DAS6031, PCI-DAS6032, PCI-DAS6033, PCI-DAS6034,
+ PCI-DAS6035, PCI-DAS6036, PCI-DAS6040, PCI-DAS6052,
+ PCI-DAS6070, PCI-DAS6071, PCI-DAS4020/12
+
+Configuration options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+
+These boards may be autocalibrated with the comedi_calibrate utility.
+
+To select the bnc trigger input on the 4020 (instead of the dio input),
+specify a nonzero channel in the chanspec. If you wish to use an external
+master clock on the 4020, you may do so by setting the scan_begin_src
+to TRIG_OTHER, and using an INSN_CONFIG_TIMER_1 configuration insn
+to configure the divisor to use for the external clock.
+
+Some devices are not identified because the PCI device IDs are not yet
+known. If you have such a board, please file a bug report at
+https://bugs.comedi.org.
+
+*/
+
+/*
+
+TODO:
+ make it return error if user attempts an ai command that uses the
+ external queue, and an ao command simultaneously
+ user counter subdevice
+ there are a number of boards this driver will support when they are
+ fully released, but does not yet since the pci device id numbers
+ are not yet available.
+ support prescaled 100khz clock for slow pacing (not available on 6000 series?)
+ make ao fifo size adjustable like ai fifo
+*/
+
+#include "../comedidev.h"
+#include <linux/delay.h>
+#include <asm/system.h>
+
+#include "comedi_pci.h"
+#include "8253.h"
+#include "8255.h"
+#include "plx9080.h"
+#include "comedi_fc.h"
+
+#undef PCIDAS64_DEBUG // disable debugging code
+//#define PCIDAS64_DEBUG // enable debugging code
+
+#ifdef PCIDAS64_DEBUG
+#define DEBUG_PRINT(format, args...) rt_printk(format , ## args )
+#else
+#define DEBUG_PRINT(format, args...)
+#endif
+
+#define TIMER_BASE 25 // 40MHz master clock
+#define PRESCALED_TIMER_BASE 10000 // 100kHz 'prescaled' clock for slow aquisition, maybe I'll support this someday
+#define DMA_BUFFER_SIZE 0x1000
+
+/* maximum value that can be loaded into board's 24-bit counters*/
+static const int max_counter_value = 0xffffff;
+
+/* PCI-DAS64xxx base addresses */
+
+// indices of base address regions
+enum base_address_regions {
+ PLX9080_BADDRINDEX = 0,
+ MAIN_BADDRINDEX = 2,
+ DIO_COUNTER_BADDRINDEX = 3,
+};
+
+// priv(dev)->main_iobase registers
+enum write_only_registers {
+ INTR_ENABLE_REG = 0x0, // interrupt enable register
+ HW_CONFIG_REG = 0x2, // hardware config register
+ DAQ_SYNC_REG = 0xc,
+ DAQ_ATRIG_LOW_4020_REG = 0xc,
+ ADC_CONTROL0_REG = 0x10, // adc control register 0
+ ADC_CONTROL1_REG = 0x12, // adc control register 1
+ CALIBRATION_REG = 0x14,
+ ADC_SAMPLE_INTERVAL_LOWER_REG = 0x16, // lower 16 bits of adc sample interval counter
+ ADC_SAMPLE_INTERVAL_UPPER_REG = 0x18, // upper 8 bits of adc sample interval counter
+ ADC_DELAY_INTERVAL_LOWER_REG = 0x1a, // lower 16 bits of delay interval counter
+ ADC_DELAY_INTERVAL_UPPER_REG = 0x1c, // upper 8 bits of delay interval counter
+ ADC_COUNT_LOWER_REG = 0x1e, // lower 16 bits of hardware conversion/scan counter
+ ADC_COUNT_UPPER_REG = 0x20, // upper 8 bits of hardware conversion/scan counter
+ ADC_START_REG = 0x22, // software trigger to start aquisition
+ ADC_CONVERT_REG = 0x24, // initiates single conversion
+ ADC_QUEUE_CLEAR_REG = 0x26, // clears adc queue
+ ADC_QUEUE_LOAD_REG = 0x28, // loads adc queue
+ ADC_BUFFER_CLEAR_REG = 0x2a,
+ ADC_QUEUE_HIGH_REG = 0x2c, // high channel for internal queue, use adc_chan_bits() inline above
+ DAC_CONTROL0_REG = 0x50, // dac control register 0
+ DAC_CONTROL1_REG = 0x52, // dac control register 0
+ DAC_SAMPLE_INTERVAL_LOWER_REG = 0x54, // lower 16 bits of dac sample interval counter
+ DAC_SAMPLE_INTERVAL_UPPER_REG = 0x56, // upper 8 bits of dac sample interval counter
+ DAC_SELECT_REG = 0x60,
+ DAC_START_REG = 0x64,
+ DAC_BUFFER_CLEAR_REG = 0x66, // clear dac buffer
+};
+static inline unsigned int dac_convert_reg(unsigned int channel)
+{
+ return 0x70 + (2 * (channel & 0x1));
+}
+static inline unsigned int dac_lsb_4020_reg(unsigned int channel)
+{
+ return 0x70 + (4 * (channel & 0x1));
+}
+static inline unsigned int dac_msb_4020_reg(unsigned int channel)
+{
+ return 0x72 + (4 * (channel & 0x1));
+}
+
+enum read_only_registers {
+ HW_STATUS_REG = 0x0, // hardware status register, reading this apparently clears pending interrupts as well
+ PIPE1_READ_REG = 0x4,
+ ADC_READ_PNTR_REG = 0x8,
+ LOWER_XFER_REG = 0x10,
+ ADC_WRITE_PNTR_REG = 0xc,
+ PREPOST_REG = 0x14,
+};
+
+enum read_write_registers {
+ I8255_4020_REG = 0x48, // 8255 offset, for 4020 only
+ ADC_QUEUE_FIFO_REG = 0x100, // external channel/gain queue, uses same bits as ADC_QUEUE_LOAD_REG
+ ADC_FIFO_REG = 0x200, /* adc data fifo */
+ DAC_FIFO_REG = 0x300, /* dac data fifo, has weird interactions with external channel queue */
+};
+
+// priv(dev)->dio_counter_iobase registers
+enum dio_counter_registers {
+ DIO_8255_OFFSET = 0x0,
+ DO_REG = 0x20,
+ DI_REG = 0x28,
+ DIO_DIRECTION_60XX_REG = 0x40,
+ DIO_DATA_60XX_REG = 0x48,
+};
+
+// bit definitions for write-only registers
+
+enum intr_enable_contents {
+ ADC_INTR_SRC_MASK = 0x3, // bits that set adc interrupt source
+ ADC_INTR_QFULL_BITS = 0x0, // interrupt fifo quater full
+ ADC_INTR_EOC_BITS = 0x1, // interrupt end of conversion
+ ADC_INTR_EOSCAN_BITS = 0x2, // interrupt end of scan
+ ADC_INTR_EOSEQ_BITS = 0x3, // interrupt end of sequence (probably wont use this it's pretty fancy)
+ EN_ADC_INTR_SRC_BIT = 0x4, // enable adc interrupt source
+ EN_ADC_DONE_INTR_BIT = 0x8, // enable adc aquisition done interrupt
+ DAC_INTR_SRC_MASK = 0x30,
+ DAC_INTR_QEMPTY_BITS = 0x0,
+ DAC_INTR_HIGH_CHAN_BITS = 0x10,
+ EN_DAC_INTR_SRC_BIT = 0x40, // enable dac interrupt source
+ EN_DAC_DONE_INTR_BIT = 0x80,
+ EN_ADC_ACTIVE_INTR_BIT = 0x200, // enable adc active interrupt
+ EN_ADC_STOP_INTR_BIT = 0x400, // enable adc stop trigger interrupt
+ EN_DAC_ACTIVE_INTR_BIT = 0x800, // enable dac active interrupt
+ EN_DAC_UNDERRUN_BIT = 0x4000, // enable dac underrun status bit
+ EN_ADC_OVERRUN_BIT = 0x8000, // enable adc overrun status bit
+};
+
+enum hw_config_contents {
+ MASTER_CLOCK_4020_MASK = 0x3, // bits that specify master clock source for 4020
+ INTERNAL_CLOCK_4020_BITS = 0x1, // use 40 MHz internal master clock for 4020
+ BNC_CLOCK_4020_BITS = 0x2, // use BNC input for master clock
+ EXT_CLOCK_4020_BITS = 0x3, // use dio input for master clock
+ EXT_QUEUE_BIT = 0x200, // use external channel/gain queue (more versatile than internal queue)
+ SLOW_DAC_BIT = 0x400, // use 225 nanosec strobe when loading dac instead of 50 nanosec
+ HW_CONFIG_DUMMY_BITS = 0x2000, // bit with unknown function yet given as default value in pci-das64 manual
+ DMA_CH_SELECT_BIT = 0x8000, // bit selects channels 1/0 for analog input/output, otherwise 0/1
+ FIFO_SIZE_REG = 0x4, // allows adjustment of fifo sizes
+ DAC_FIFO_SIZE_MASK = 0xff00, // bits that set dac fifo size
+ DAC_FIFO_BITS = 0xf800, /* 8k sample ao fifo */
+};
+#define DAC_FIFO_SIZE 0x2000
+
+enum daq_atrig_low_4020_contents {
+ EXT_AGATE_BNC_BIT = 0x8000, // use trig/ext clk bnc input for analog gate signal
+ EXT_STOP_TRIG_BNC_BIT = 0x4000, // use trig/ext clk bnc input for external stop trigger signal
+ EXT_START_TRIG_BNC_BIT = 0x2000, // use trig/ext clk bnc input for external start trigger signal
+};
+static inline uint16_t analog_trig_low_threshold_bits(uint16_t threshold)
+{
+ return threshold & 0xfff;
+}
+
+enum adc_control0_contents {
+ ADC_GATE_SRC_MASK = 0x3, // bits that select gate
+ ADC_SOFT_GATE_BITS = 0x1, // software gate
+ ADC_EXT_GATE_BITS = 0x2, // external digital gate
+ ADC_ANALOG_GATE_BITS = 0x3, // analog level gate
+ ADC_GATE_LEVEL_BIT = 0x4, // level-sensitive gate (for digital)
+ ADC_GATE_POLARITY_BIT = 0x8, // gate active low
+ ADC_START_TRIG_SOFT_BITS = 0x10,
+ ADC_START_TRIG_EXT_BITS = 0x20,
+ ADC_START_TRIG_ANALOG_BITS = 0x30,
+ ADC_START_TRIG_MASK = 0x30,
+ ADC_START_TRIG_FALLING_BIT = 0x40, // trig 1 uses falling edge
+ ADC_EXT_CONV_FALLING_BIT = 0x800, // external pacing uses falling edge
+ ADC_SAMPLE_COUNTER_EN_BIT = 0x1000, // enable hardware scan counter
+ ADC_DMA_DISABLE_BIT = 0x4000, // disables dma
+ ADC_ENABLE_BIT = 0x8000, // master adc enable
+};
+
+enum adc_control1_contents {
+ ADC_QUEUE_CONFIG_BIT = 0x1, // should be set for boards with > 16 channels
+ CONVERT_POLARITY_BIT = 0x10,
+ EOC_POLARITY_BIT = 0x20,
+ ADC_SW_GATE_BIT = 0x40, // software gate of adc
+ ADC_DITHER_BIT = 0x200, // turn on extra noise for dithering
+ RETRIGGER_BIT = 0x800,
+ ADC_LO_CHANNEL_4020_MASK = 0x300,
+ ADC_HI_CHANNEL_4020_MASK = 0xc00,
+ TWO_CHANNEL_4020_BITS = 0x1000, // two channel mode for 4020
+ FOUR_CHANNEL_4020_BITS = 0x2000, // four channel mode for 4020
+ CHANNEL_MODE_4020_MASK = 0x3000,
+ ADC_MODE_MASK = 0xf000,
+};
+static inline uint16_t adc_lo_chan_4020_bits(unsigned int channel)
+{
+ return (channel & 0x3) << 8;
+};
+static inline uint16_t adc_hi_chan_4020_bits(unsigned int channel)
+{
+ return (channel & 0x3) << 10;
+};
+static inline uint16_t adc_mode_bits(unsigned int mode)
+{
+ return (mode & 0xf) << 12;
+};
+
+enum calibration_contents {
+ SELECT_8800_BIT = 0x1,
+ SELECT_8402_64XX_BIT = 0x2,
+ SELECT_1590_60XX_BIT = 0x2,
+ CAL_EN_64XX_BIT = 0x40, // calibration enable for 64xx series
+ SERIAL_DATA_IN_BIT = 0x80,
+ SERIAL_CLOCK_BIT = 0x100,
+ CAL_EN_60XX_BIT = 0x200, // calibration enable for 60xx series
+ CAL_GAIN_BIT = 0x800,
+};
+/* calibration sources for 6025 are:
+ * 0 : ground
+ * 1 : 10V
+ * 2 : 5V
+ * 3 : 0.5V
+ * 4 : 0.05V
+ * 5 : ground
+ * 6 : dac channel 0
+ * 7 : dac channel 1
+ */
+static inline uint16_t adc_src_bits(unsigned int source)
+{
+ return (source & 0xf) << 3;
+};
+
+static inline uint16_t adc_convert_chan_4020_bits(unsigned int channel)
+{
+ return (channel & 0x3) << 8;
+};
+
+enum adc_queue_load_contents {
+ UNIP_BIT = 0x800, // unipolar/bipolar bit
+ ADC_SE_DIFF_BIT = 0x1000, // single-ended/ differential bit
+ ADC_COMMON_BIT = 0x2000, // non-referenced single-ended (common-mode input)
+ QUEUE_EOSEQ_BIT = 0x4000, // queue end of sequence
+ QUEUE_EOSCAN_BIT = 0x8000, // queue end of scan
+};
+static inline uint16_t adc_chan_bits(unsigned int channel)
+{
+ return channel & 0x3f;
+};
+
+enum dac_control0_contents {
+ DAC_ENABLE_BIT = 0x8000, // dac controller enable bit
+ DAC_CYCLIC_STOP_BIT = 0x4000,
+ DAC_WAVEFORM_MODE_BIT = 0x100,
+ DAC_EXT_UPDATE_FALLING_BIT = 0x80,
+ DAC_EXT_UPDATE_ENABLE_BIT = 0x40,
+ WAVEFORM_TRIG_MASK = 0x30,
+ WAVEFORM_TRIG_DISABLED_BITS = 0x0,
+ WAVEFORM_TRIG_SOFT_BITS = 0x10,
+ WAVEFORM_TRIG_EXT_BITS = 0x20,
+ WAVEFORM_TRIG_ADC1_BITS = 0x30,
+ WAVEFORM_TRIG_FALLING_BIT = 0x8,
+ WAVEFORM_GATE_LEVEL_BIT = 0x4,
+ WAVEFORM_GATE_ENABLE_BIT = 0x2,
+ WAVEFORM_GATE_SELECT_BIT = 0x1,
+};
+
+enum dac_control1_contents {
+ DAC_WRITE_POLARITY_BIT = 0x800, /* board-dependent setting */
+ DAC1_EXT_REF_BIT = 0x200,
+ DAC0_EXT_REF_BIT = 0x100,
+ DAC_OUTPUT_ENABLE_BIT = 0x80, // dac output enable bit
+ DAC_UPDATE_POLARITY_BIT = 0x40, /* board-dependent setting */
+ DAC_SW_GATE_BIT = 0x20,
+ DAC1_UNIPOLAR_BIT = 0x8,
+ DAC0_UNIPOLAR_BIT = 0x2,
+};
+
+// bit definitions for read-only registers
+enum hw_status_contents {
+ DAC_UNDERRUN_BIT = 0x1,
+ ADC_OVERRUN_BIT = 0x2,
+ DAC_ACTIVE_BIT = 0x4,
+ ADC_ACTIVE_BIT = 0x8,
+ DAC_INTR_PENDING_BIT = 0x10,
+ ADC_INTR_PENDING_BIT = 0x20,
+ DAC_DONE_BIT = 0x40,
+ ADC_DONE_BIT = 0x80,
+ EXT_INTR_PENDING_BIT = 0x100,
+ ADC_STOP_BIT = 0x200,
+};
+static inline uint16_t pipe_full_bits(uint16_t hw_status_bits)
+{
+ return (hw_status_bits >> 10) & 0x3;
+};
+
+static inline unsigned int dma_chain_flag_bits(uint16_t prepost_bits)
+{
+ return (prepost_bits >> 6) & 0x3;
+}
+static inline unsigned int adc_upper_read_ptr_code(uint16_t prepost_bits)
+{
+ return (prepost_bits >> 12) & 0x3;
+}
+static inline unsigned int adc_upper_write_ptr_code(uint16_t prepost_bits)
+{
+ return (prepost_bits >> 14) & 0x3;
+}
+
+// I2C addresses for 4020
+enum i2c_addresses {
+ RANGE_CAL_I2C_ADDR = 0x20,
+ CALDAC0_I2C_ADDR = 0xc,
+ CALDAC1_I2C_ADDR = 0xd,
+};
+
+enum range_cal_i2c_contents {
+ ADC_SRC_4020_MASK = 0x70, // bits that set what source the adc converter measures
+ BNC_TRIG_THRESHOLD_0V_BIT = 0x80, // make bnc trig/ext clock threshold 0V instead of 2.5V
+};
+static inline uint8_t adc_src_4020_bits(unsigned int source)
+{
+ return (source << 4) & ADC_SRC_4020_MASK;
+};
+static inline uint8_t attenuate_bit(unsigned int channel)
+{
+ // attenuate channel (+-5V input range)
+ return 1 << (channel & 0x3);
+};
+
+// analog input ranges for 64xx boards
+static const struct comedi_lrange ai_ranges_64xx = {
+ 8,
+ {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+/* analog input ranges for 60xx boards */
+static const struct comedi_lrange ai_ranges_60xx = {
+ 4,
+ {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ }
+};
+
+/* analog input ranges for 6030, etc boards */
+static const struct comedi_lrange ai_ranges_6030 = {
+ 14,
+ {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.2),
+ BIP_RANGE(0.1),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1),
+ UNI_RANGE(0.5),
+ UNI_RANGE(0.2),
+ UNI_RANGE(0.1),
+ }
+};
+
+/* analog input ranges for 6052, etc boards */
+static const struct comedi_lrange ai_ranges_6052 = {
+ 15,
+ {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.25),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.05),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1),
+ UNI_RANGE(0.5),
+ UNI_RANGE(0.2),
+ UNI_RANGE(0.1),
+ }
+};
+
+// analog input ranges for 4020 board
+static const struct comedi_lrange ai_ranges_4020 = {
+ 2,
+ {
+ BIP_RANGE(5),
+ BIP_RANGE(1),
+ }
+};
+
+// analog output ranges
+static const struct comedi_lrange ao_ranges_64xx = {
+ 4,
+ {
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(10),
+ }
+};
+static const int ao_range_code_64xx[] = {
+ 0x0,
+ 0x1,
+ 0x2,
+ 0x3,
+};
+
+static const struct comedi_lrange ao_ranges_60xx = {
+ 1,
+ {
+ BIP_RANGE(10),
+ }
+};
+static const int ao_range_code_60xx[] = {
+ 0x0,
+};
+
+static const struct comedi_lrange ao_ranges_6030 = {
+ 2,
+ {
+ BIP_RANGE(10),
+ UNI_RANGE(10),
+ }
+};
+static const int ao_range_code_6030[] = {
+ 0x0,
+ 0x2,
+};
+
+static const struct comedi_lrange ao_ranges_4020 = {
+ 2,
+ {
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ }
+};
+static const int ao_range_code_4020[] = {
+ 0x1,
+ 0x0,
+};
+
+enum register_layout {
+ LAYOUT_60XX,
+ LAYOUT_64XX,
+ LAYOUT_4020,
+};
+
+struct hw_fifo_info {
+ unsigned int num_segments;
+ unsigned int max_segment_length;
+ unsigned int sample_packing_ratio;
+ uint16_t fifo_size_reg_mask;
+};
+
+struct pcidas64_board {
+ const char *name;
+ int device_id; // pci device id
+ int ai_se_chans; // number of ai inputs in single-ended mode
+ int ai_bits; // analog input resolution
+ int ai_speed; // fastest conversion period in ns
+ const struct comedi_lrange *ai_range_table;
+ int ao_nchan; // number of analog out channels
+ int ao_bits; // analog output resolution
+ int ao_scan_speed; // analog output speed (for a scan, not conversion)
+ const struct comedi_lrange *ao_range_table;
+ const int *ao_range_code;
+ const struct hw_fifo_info *const ai_fifo;
+ enum register_layout layout; // different board families have slightly different registers
+ unsigned has_8255:1;
+};
+
+static const struct hw_fifo_info ai_fifo_4020 = {
+ num_segments:2,
+ max_segment_length:0x8000,
+ sample_packing_ratio:2,
+ fifo_size_reg_mask:0x7f,
+};
+
+static const struct hw_fifo_info ai_fifo_64xx = {
+ num_segments:4,
+ max_segment_length:0x800,
+ sample_packing_ratio:1,
+ fifo_size_reg_mask:0x3f,
+};
+
+static const struct hw_fifo_info ai_fifo_60xx = {
+ num_segments:4,
+ max_segment_length:0x800,
+ sample_packing_ratio:1,
+ fifo_size_reg_mask:0x7f,
+};
+
+/* maximum number of dma transfers we will chain together into a ring
+ * (and the maximum number of dma buffers we maintain) */
+#define MAX_AI_DMA_RING_COUNT (0x80000 / DMA_BUFFER_SIZE)
+#define MIN_AI_DMA_RING_COUNT (0x10000 / DMA_BUFFER_SIZE)
+#define AO_DMA_RING_COUNT (0x10000 / DMA_BUFFER_SIZE)
+static inline unsigned int ai_dma_ring_count(struct pcidas64_board * board)
+{
+ if (board->layout == LAYOUT_4020)
+ return MAX_AI_DMA_RING_COUNT;
+ else
+ return MIN_AI_DMA_RING_COUNT;
+}
+
+static const int bytes_in_sample = 2;
+
+static const struct pcidas64_board pcidas64_boards[] = {
+ {
+ name: "pci-das6402/16",
+ device_id:0x1d,
+ ai_se_chans:64,
+ ai_bits: 16,
+ ai_speed:5000,
+ ao_nchan:2,
+ ao_bits: 16,
+ ao_scan_speed:10000,
+ layout: LAYOUT_64XX,
+ ai_range_table:&ai_ranges_64xx,
+ ao_range_table:&ao_ranges_64xx,
+ ao_range_code:ao_range_code_64xx,
+ ai_fifo: &ai_fifo_64xx,
+ has_8255:1,
+ },
+ {
+ name: "pci-das6402/12", // XXX check
+ device_id:0x1e,
+ ai_se_chans:64,
+ ai_bits: 12,
+ ai_speed:5000,
+ ao_nchan:2,
+ ao_bits: 12,
+ ao_scan_speed:10000,
+ layout: LAYOUT_64XX,
+ ai_range_table:&ai_ranges_64xx,
+ ao_range_table:&ao_ranges_64xx,
+ ao_range_code:ao_range_code_64xx,
+ ai_fifo: &ai_fifo_64xx,
+ has_8255:1,
+ },
+ {
+ name: "pci-das64/m1/16",
+ device_id:0x35,
+ ai_se_chans:64,
+ ai_bits: 16,
+ ai_speed:1000,
+ ao_nchan:2,
+ ao_bits: 16,
+ ao_scan_speed:10000,
+ layout: LAYOUT_64XX,
+ ai_range_table:&ai_ranges_64xx,
+ ao_range_table:&ao_ranges_64xx,
+ ao_range_code:ao_range_code_64xx,
+ ai_fifo: &ai_fifo_64xx,
+ has_8255:1,
+ },
+ {
+ name: "pci-das64/m2/16",
+ device_id:0x36,
+ ai_se_chans:64,
+ ai_bits: 16,
+ ai_speed:500,
+ ao_nchan:2,
+ ao_bits: 16,
+ ao_scan_speed:10000,
+ layout: LAYOUT_64XX,
+ ai_range_table:&ai_ranges_64xx,
+ ao_range_table:&ao_ranges_64xx,
+ ao_range_code:ao_range_code_64xx,
+ ai_fifo: &ai_fifo_64xx,
+ has_8255:1,
+ },
+ {
+ name: "pci-das64/m3/16",
+ device_id:0x37,
+ ai_se_chans:64,
+ ai_bits: 16,
+ ai_speed:333,
+ ao_nchan:2,
+ ao_bits: 16,
+ ao_scan_speed:10000,
+ layout: LAYOUT_64XX,
+ ai_range_table:&ai_ranges_64xx,
+ ao_range_table:&ao_ranges_64xx,
+ ao_range_code:ao_range_code_64xx,
+ ai_fifo: &ai_fifo_64xx,
+ has_8255:1,
+ },
+ {
+ .name = "pci-das6013",
+ .device_id = 0x78,
+ .ai_se_chans = 16,
+ .ai_bits = 16,
+ .ai_speed = 5000,
+ .ao_nchan = 0,
+ .ao_bits = 16,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_60xx,
+ .ao_range_table = &ao_ranges_60xx,
+ .ao_range_code = ao_range_code_60xx,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 0,
+ },
+ {
+ name: "pci-das6014",
+ device_id:0x79,
+ ai_se_chans:16,
+ ai_bits: 16,
+ ai_speed:5000,
+ ao_nchan:2,
+ ao_bits: 16,
+ ao_scan_speed:100000,
+ layout: LAYOUT_60XX,
+ ai_range_table:&ai_ranges_60xx,
+ ao_range_table:&ao_ranges_60xx,
+ ao_range_code:ao_range_code_60xx,
+ ai_fifo: &ai_fifo_60xx,
+ has_8255:0,
+ },
+ {
+ name: "pci-das6023",
+ device_id:0x5d,
+ ai_se_chans:16,
+ ai_bits: 12,
+ ai_speed:5000,
+ ao_nchan:0,
+ ao_scan_speed:100000,
+ layout: LAYOUT_60XX,
+ ai_range_table:&ai_ranges_60xx,
+ ao_range_table:&ao_ranges_60xx,
+ ao_range_code:ao_range_code_60xx,
+ ai_fifo: &ai_fifo_60xx,
+ has_8255:1,
+ },
+ {
+ name: "pci-das6025",
+ device_id:0x5e,
+ ai_se_chans:16,
+ ai_bits: 12,
+ ai_speed:5000,
+ ao_nchan:2,
+ ao_bits: 12,
+ ao_scan_speed:100000,
+ layout: LAYOUT_60XX,
+ ai_range_table:&ai_ranges_60xx,
+ ao_range_table:&ao_ranges_60xx,
+ ao_range_code:ao_range_code_60xx,
+ ai_fifo: &ai_fifo_60xx,
+ has_8255:1,
+ },
+ {
+ name: "pci-das6030",
+ device_id:0x5f,
+ ai_se_chans:16,
+ ai_bits: 16,
+ ai_speed:10000,
+ ao_nchan:2,
+ ao_bits: 16,
+ ao_scan_speed:10000,
+ layout: LAYOUT_60XX,
+ ai_range_table:&ai_ranges_6030,
+ ao_range_table:&ao_ranges_6030,
+ ao_range_code:ao_range_code_6030,
+ ai_fifo: &ai_fifo_60xx,
+ has_8255:0,
+ },
+ {
+ name: "pci-das6031",
+ device_id:0x60,
+ ai_se_chans:64,
+ ai_bits: 16,
+ ai_speed:10000,
+ ao_nchan:2,
+ ao_bits: 16,
+ ao_scan_speed:10000,
+ layout: LAYOUT_60XX,
+ ai_range_table:&ai_ranges_6030,
+ ao_range_table:&ao_ranges_6030,
+ ao_range_code:ao_range_code_6030,
+ ai_fifo: &ai_fifo_60xx,
+ has_8255:0,
+ },
+ {
+ name: "pci-das6032",
+ device_id:0x61,
+ ai_se_chans:16,
+ ai_bits: 16,
+ ai_speed:10000,
+ ao_nchan:0,
+ layout: LAYOUT_60XX,
+ ai_range_table:&ai_ranges_6030,
+ ai_fifo: &ai_fifo_60xx,
+ has_8255:0,
+ },
+ {
+ name: "pci-das6033",
+ device_id:0x62,
+ ai_se_chans:64,
+ ai_bits: 16,
+ ai_speed:10000,
+ ao_nchan:0,
+ layout: LAYOUT_60XX,
+ ai_range_table:&ai_ranges_6030,
+ ai_fifo: &ai_fifo_60xx,
+ has_8255:0,
+ },
+ {
+ name: "pci-das6034",
+ device_id:0x63,
+ ai_se_chans:16,
+ ai_bits: 16,
+ ai_speed:5000,
+ ao_nchan:0,
+ ao_scan_speed:0,
+ layout: LAYOUT_60XX,
+ ai_range_table:&ai_ranges_60xx,
+ ai_fifo: &ai_fifo_60xx,
+ has_8255:0,
+ },
+ {
+ name: "pci-das6035",
+ device_id:0x64,
+ ai_se_chans:16,
+ ai_bits: 16,
+ ai_speed:5000,
+ ao_nchan:2,
+ ao_bits: 12,
+ ao_scan_speed:100000,
+ layout: LAYOUT_60XX,
+ ai_range_table:&ai_ranges_60xx,
+ ao_range_table:&ao_ranges_60xx,
+ ao_range_code:ao_range_code_60xx,
+ ai_fifo: &ai_fifo_60xx,
+ has_8255:0,
+ },
+ {
+ name: "pci-das6036",
+ device_id:0x6f,
+ ai_se_chans:16,
+ ai_bits: 16,
+ ai_speed:5000,
+ ao_nchan:2,
+ ao_bits: 16,
+ ao_scan_speed:100000,
+ layout: LAYOUT_60XX,
+ ai_range_table:&ai_ranges_60xx,
+ ao_range_table:&ao_ranges_60xx,
+ ao_range_code:ao_range_code_60xx,
+ ai_fifo: &ai_fifo_60xx,
+ has_8255:0,
+ },
+ {
+ name: "pci-das6040",
+ device_id:0x65,
+ ai_se_chans:16,
+ ai_bits: 12,
+ ai_speed:2000,
+ ao_nchan:2,
+ ao_bits: 12,
+ ao_scan_speed:1000,
+ layout: LAYOUT_60XX,
+ ai_range_table:&ai_ranges_6052,
+ ao_range_table:&ao_ranges_6030,
+ ao_range_code:ao_range_code_6030,
+ ai_fifo: &ai_fifo_60xx,
+ has_8255:0,
+ },
+ {
+ name: "pci-das6052",
+ device_id:0x66,
+ ai_se_chans:16,
+ ai_bits: 16,
+ ai_speed:3333,
+ ao_nchan:2,
+ ao_bits: 16,
+ ao_scan_speed:3333,
+ layout: LAYOUT_60XX,
+ ai_range_table:&ai_ranges_6052,
+ ao_range_table:&ao_ranges_6030,
+ ao_range_code:ao_range_code_6030,
+ ai_fifo: &ai_fifo_60xx,
+ has_8255:0,
+ },
+ {
+ name: "pci-das6070",
+ device_id:0x67,
+ ai_se_chans:16,
+ ai_bits: 12,
+ ai_speed:800,
+ ao_nchan:2,
+ ao_bits: 12,
+ ao_scan_speed:1000,
+ layout: LAYOUT_60XX,
+ ai_range_table:&ai_ranges_6052,
+ ao_range_table:&ao_ranges_6030,
+ ao_range_code:ao_range_code_6030,
+ ai_fifo: &ai_fifo_60xx,
+ has_8255:0,
+ },
+ {
+ name: "pci-das6071",
+ device_id:0x68,
+ ai_se_chans:64,
+ ai_bits: 12,
+ ai_speed:800,
+ ao_nchan:2,
+ ao_bits: 12,
+ ao_scan_speed:1000,
+ layout: LAYOUT_60XX,
+ ai_range_table:&ai_ranges_6052,
+ ao_range_table:&ao_ranges_6030,
+ ao_range_code:ao_range_code_6030,
+ ai_fifo: &ai_fifo_60xx,
+ has_8255:0,
+ },
+ {
+ name: "pci-das4020/12",
+ device_id:0x52,
+ ai_se_chans:4,
+ ai_bits: 12,
+ ai_speed:50,
+ ao_bits: 12,
+ ao_nchan:2,
+ ao_scan_speed:0, // no hardware pacing on ao
+ layout: LAYOUT_4020,
+ ai_range_table:&ai_ranges_4020,
+ ao_range_table:&ao_ranges_4020,
+ ao_range_code:ao_range_code_4020,
+ ai_fifo: &ai_fifo_4020,
+ has_8255:1,
+ },
+#if 0
+ {
+ name: "pci-das6402/16/jr",
+ device_id:0 // XXX,
+ ai_se_chans:64,
+ ai_bits: 16,
+ ai_speed:5000,
+ ao_nchan:0,
+ ao_scan_speed:10000,
+ layout: LAYOUT_64XX,
+ ai_range_table:&ai_ranges_64xx,
+ ai_fifo: ai_fifo_64xx,
+ has_8255:1,
+ },
+ {
+ name: "pci-das64/m1/16/jr",
+ device_id:0 // XXX,
+ ai_se_chans:64,
+ ai_bits: 16,
+ ai_speed:1000,
+ ao_nchan:0,
+ ao_scan_speed:10000,
+ layout: LAYOUT_64XX,
+ ai_range_table:&ai_ranges_64xx,
+ ai_fifo: ai_fifo_64xx,
+ has_8255:1,
+ },
+ {
+ name: "pci-das64/m2/16/jr",
+ device_id:0 // XXX,
+ ai_se_chans:64,
+ ai_bits: 16,
+ ai_speed:500,
+ ao_nchan:0,
+ ao_scan_speed:10000,
+ layout: LAYOUT_64XX,
+ ai_range_table:&ai_ranges_64xx,
+ ai_fifo: ai_fifo_64xx,
+ has_8255:1,
+ },
+ {
+ name: "pci-das64/m3/16/jr",
+ device_id:0 // XXX,
+ ai_se_chans:64,
+ ai_bits: 16,
+ ai_speed:333,
+ ao_nchan:0,
+ ao_scan_speed:10000,
+ layout: LAYOUT_64XX,
+ ai_range_table:&ai_ranges_64xx,
+ ai_fifo: ai_fifo_64xx,
+ has_8255:1,
+ },
+ {
+ name: "pci-das64/m1/14",
+ device_id:0, // XXX
+ ai_se_chans:64,
+ ai_bits: 14,
+ ai_speed:1000,
+ ao_nchan:2,
+ ao_scan_speed:10000,
+ layout: LAYOUT_64XX,
+ ai_range_table:&ai_ranges_64xx,
+ ai_fifo: ai_fifo_64xx,
+ has_8255:1,
+ },
+ {
+ name: "pci-das64/m2/14",
+ device_id:0, // XXX
+ ai_se_chans:64,
+ ai_bits: 14,
+ ai_speed:500,
+ ao_nchan:2,
+ ao_scan_speed:10000,
+ layout: LAYOUT_64XX,
+ ai_range_table:&ai_ranges_64xx,
+ ai_fifo: ai_fifo_64xx,
+ has_8255:1,
+ },
+ {
+ name: "pci-das64/m3/14",
+ device_id:0, // XXX
+ ai_se_chans:64,
+ ai_bits: 14,
+ ai_speed:333,
+ ao_nchan:2,
+ ao_scan_speed:10000,
+ layout: LAYOUT_64XX,
+ ai_range_table:&ai_ranges_64xx,
+ ai_fifo: ai_fifo_64xx,
+ has_8255:1,
+ },
+#endif
+};
+
+// Number of boards in cb_pcidas_boards
+static inline unsigned int num_boards(void)
+{
+ return sizeof(pcidas64_boards) / sizeof(struct pcidas64_board);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(pcidas64_pci_table) = {
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x001d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x001e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x0035, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x0036, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x0037, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x0052, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x005d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x005e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x005f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x0061, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x0062, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x0063, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x0064, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x0066, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x0067, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x0068, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x006f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x0078, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x0079, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, pcidas64_pci_table);
+
+static inline struct pcidas64_board *board(const struct comedi_device * dev)
+{
+ return (struct pcidas64_board *) dev->board_ptr;
+}
+
+static inline unsigned short se_diff_bit_6xxx(struct comedi_device * dev,
+ int use_differential)
+{
+ if ((board(dev)->layout == LAYOUT_64XX && !use_differential) ||
+ (board(dev)->layout == LAYOUT_60XX && use_differential))
+ return ADC_SE_DIFF_BIT;
+ else
+ return 0;
+};
+
+struct ext_clock_info {
+ unsigned int divisor; // master clock divisor to use for scans with external master clock
+ unsigned int chanspec; // chanspec for master clock input when used as scan begin src
+};
+
+/* this structure is for data unique to this hardware driver. */
+struct pcidas64_private {
+
+ struct pci_dev *hw_dev; // pointer to board's pci_dev struct
+ // base addresses (physical)
+ resource_size_t plx9080_phys_iobase;
+ resource_size_t main_phys_iobase;
+ resource_size_t dio_counter_phys_iobase;
+ // base addresses (ioremapped)
+ void *plx9080_iobase;
+ void *main_iobase;
+ void *dio_counter_iobase;
+ // local address (used by dma controller)
+ uint32_t local0_iobase;
+ uint32_t local1_iobase;
+ volatile unsigned int ai_count; // number of analog input samples remaining
+ uint16_t *ai_buffer[MAX_AI_DMA_RING_COUNT]; // dma buffers for analog input
+ dma_addr_t ai_buffer_bus_addr[MAX_AI_DMA_RING_COUNT]; // physical addresses of ai dma buffers
+ struct plx_dma_desc *ai_dma_desc; // array of ai dma descriptors read by plx9080, allocated to get proper alignment
+ dma_addr_t ai_dma_desc_bus_addr; // physical address of ai dma descriptor array
+ volatile unsigned int ai_dma_index; // index of the ai dma descriptor/buffer that is currently being used
+ uint16_t *ao_buffer[AO_DMA_RING_COUNT]; // dma buffers for analog output
+ dma_addr_t ao_buffer_bus_addr[AO_DMA_RING_COUNT]; // physical addresses of ao dma buffers
+ struct plx_dma_desc *ao_dma_desc;
+ dma_addr_t ao_dma_desc_bus_addr;
+ volatile unsigned int ao_dma_index; // keeps track of buffer where the next ao sample should go
+ volatile unsigned long ao_count; // number of analog output samples remaining
+ volatile unsigned int ao_value[2]; // remember what the analog outputs are set to, to allow readback
+ unsigned int hw_revision; // stc chip hardware revision number
+ volatile unsigned int intr_enable_bits; // last bits sent to INTR_ENABLE_REG register
+ volatile uint16_t adc_control1_bits; // last bits sent to ADC_CONTROL1_REG register
+ volatile uint16_t fifo_size_bits; // last bits sent to FIFO_SIZE_REG register
+ volatile uint16_t hw_config_bits; // last bits sent to HW_CONFIG_REG register
+ volatile uint16_t dac_control1_bits;
+ volatile uint32_t plx_control_bits; // last bits written to plx9080 control register
+ volatile uint32_t plx_intcsr_bits; // last bits written to plx interrupt control and status register
+ volatile int calibration_source; // index of calibration source readable through ai ch0
+ volatile uint8_t i2c_cal_range_bits; // bits written to i2c calibration/range register
+ volatile unsigned int ext_trig_falling; // configure digital triggers to trigger on falling edge
+ // states of various devices stored to enable read-back
+ unsigned int ad8402_state[2];
+ unsigned int caldac_state[8];
+ volatile short ai_cmd_running;
+ unsigned int ai_fifo_segment_length;
+ struct ext_clock_info ext_clock;
+ short ao_bounce_buffer[DAC_FIFO_SIZE];
+};
+
+
+/* inline function that makes it easier to
+ * access the private structure.
+ */
+static inline struct pcidas64_private *priv(struct comedi_device * dev)
+{
+ return dev->private;
+}
+
+/*
+ * The comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int detach(struct comedi_device * dev);
+static struct comedi_driver driver_cb_pcidas = {
+ driver_name:"cb_pcidas64",
+ module:THIS_MODULE,
+ attach:attach,
+ detach:detach,
+};
+
+static int ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ai_config_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ao_readback_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int ao_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int ao_inttrig(struct comedi_device * dev, struct comedi_subdevice * subdev,
+ unsigned int trig_num);
+static int ao_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static irqreturn_t handle_interrupt(int irq, void *d PT_REGS_ARG);
+static int ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static int ao_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static int dio_callback(int dir, int port, int data, unsigned long arg);
+static int dio_callback_4020(int dir, int port, int data, unsigned long arg);
+static int di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dio_60xx_config_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dio_60xx_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int calib_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int calib_write_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ad8402_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static void ad8402_write(struct comedi_device * dev, unsigned int channel,
+ unsigned int value);
+static int ad8402_write_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int eeprom_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static void check_adc_timing(struct comedi_device * dev, struct comedi_cmd * cmd);
+static unsigned int get_divisor(unsigned int ns, unsigned int flags);
+static void i2c_write(struct comedi_device * dev, unsigned int address,
+ const uint8_t * data, unsigned int length);
+static void caldac_write(struct comedi_device * dev, unsigned int channel,
+ unsigned int value);
+static int caldac_8800_write(struct comedi_device * dev, unsigned int address,
+ uint8_t value);
+//static int dac_1590_write(struct comedi_device *dev, unsigned int dac_a, unsigned int dac_b);
+static int caldac_i2c_write(struct comedi_device * dev, unsigned int caldac_channel,
+ unsigned int value);
+static void abort_dma(struct comedi_device * dev, unsigned int channel);
+static void disable_plx_interrupts(struct comedi_device * dev);
+static int set_ai_fifo_size(struct comedi_device * dev, unsigned int num_samples);
+static unsigned int ai_fifo_size(struct comedi_device * dev);
+static int set_ai_fifo_segment_length(struct comedi_device * dev,
+ unsigned int num_entries);
+static void disable_ai_pacing(struct comedi_device * dev);
+static void disable_ai_interrupts(struct comedi_device * dev);
+static void enable_ai_interrupts(struct comedi_device * dev, const struct comedi_cmd * cmd);
+static unsigned int get_ao_divisor(unsigned int ns, unsigned int flags);
+static void load_ao_dma(struct comedi_device * dev, const struct comedi_cmd * cmd);
+
+COMEDI_PCI_INITCLEANUP(driver_cb_pcidas, pcidas64_pci_table);
+
+static unsigned int ai_range_bits_6xxx(const struct comedi_device * dev,
+ unsigned int range_index)
+{
+ const struct comedi_krange *range =
+ &board(dev)->ai_range_table->range[range_index];
+ unsigned int bits = 0;
+
+ switch (range->max) {
+ case 10000000:
+ bits = 0x000;
+ break;
+ case 5000000:
+ bits = 0x100;
+ break;
+ case 2000000:
+ case 2500000:
+ bits = 0x200;
+ break;
+ case 1000000:
+ case 1250000:
+ bits = 0x300;
+ break;
+ case 500000:
+ bits = 0x400;
+ break;
+ case 200000:
+ case 250000:
+ bits = 0x500;
+ break;
+ case 100000:
+ bits = 0x600;
+ break;
+ case 50000:
+ bits = 0x700;
+ break;
+ default:
+ comedi_error(dev, "bug! in ai_range_bits_6xxx");
+ break;
+ }
+ if (range->min == 0)
+ bits += 0x900;
+ return bits;
+}
+
+static unsigned int hw_revision(const struct comedi_device * dev,
+ uint16_t hw_status_bits)
+{
+ if (board(dev)->layout == LAYOUT_4020)
+ return (hw_status_bits >> 13) & 0x7;
+
+ return (hw_status_bits >> 12) & 0xf;
+}
+
+static void set_dac_range_bits(struct comedi_device * dev, volatile uint16_t * bits,
+ unsigned int channel, unsigned int range)
+{
+ unsigned int code = board(dev)->ao_range_code[range];
+
+ if (channel > 1)
+ comedi_error(dev, "bug! bad channel?");
+ if (code & ~0x3)
+ comedi_error(dev, "bug! bad range code?");
+
+ *bits &= ~(0x3 << (2 * channel));
+ *bits |= code << (2 * channel);
+};
+
+static inline int ao_cmd_is_supported(const struct pcidas64_board * board)
+{
+ return board->ao_nchan && board->layout != LAYOUT_4020;
+}
+
+// initialize plx9080 chip
+static void init_plx9080(struct comedi_device * dev)
+{
+ uint32_t bits;
+ void *plx_iobase = priv(dev)->plx9080_iobase;
+
+ priv(dev)->plx_control_bits =
+ readl(priv(dev)->plx9080_iobase + PLX_CONTROL_REG);
+
+ // plx9080 dump
+ DEBUG_PRINT(" plx interrupt status 0x%x\n",
+ readl(plx_iobase + PLX_INTRCS_REG));
+ DEBUG_PRINT(" plx id bits 0x%x\n", readl(plx_iobase + PLX_ID_REG));
+ DEBUG_PRINT(" plx control reg 0x%x\n", priv(dev)->plx_control_bits);
+ DEBUG_PRINT(" plx mode/arbitration reg 0x%x\n",
+ readl(plx_iobase + PLX_MARB_REG));
+ DEBUG_PRINT(" plx region0 reg 0x%x\n",
+ readl(plx_iobase + PLX_REGION0_REG));
+ DEBUG_PRINT(" plx region1 reg 0x%x\n",
+ readl(plx_iobase + PLX_REGION1_REG));
+
+ DEBUG_PRINT(" plx revision 0x%x\n",
+ readl(plx_iobase + PLX_REVISION_REG));
+ DEBUG_PRINT(" plx dma channel 0 mode 0x%x\n",
+ readl(plx_iobase + PLX_DMA0_MODE_REG));
+ DEBUG_PRINT(" plx dma channel 1 mode 0x%x\n",
+ readl(plx_iobase + PLX_DMA1_MODE_REG));
+ DEBUG_PRINT(" plx dma channel 0 pci address 0x%x\n",
+ readl(plx_iobase + PLX_DMA0_PCI_ADDRESS_REG));
+ DEBUG_PRINT(" plx dma channel 0 local address 0x%x\n",
+ readl(plx_iobase + PLX_DMA0_LOCAL_ADDRESS_REG));
+ DEBUG_PRINT(" plx dma channel 0 transfer size 0x%x\n",
+ readl(plx_iobase + PLX_DMA0_TRANSFER_SIZE_REG));
+ DEBUG_PRINT(" plx dma channel 0 descriptor 0x%x\n",
+ readl(plx_iobase + PLX_DMA0_DESCRIPTOR_REG));
+ DEBUG_PRINT(" plx dma channel 0 command status 0x%x\n",
+ readb(plx_iobase + PLX_DMA0_CS_REG));
+ DEBUG_PRINT(" plx dma channel 0 threshold 0x%x\n",
+ readl(plx_iobase + PLX_DMA0_THRESHOLD_REG));
+ DEBUG_PRINT(" plx bigend 0x%x\n", readl(plx_iobase + PLX_BIGEND_REG));
+
+#ifdef __BIG_ENDIAN
+ bits = BIGEND_DMA0 | BIGEND_DMA1;
+#else
+ bits = 0;
+#endif
+ writel(bits, priv(dev)->plx9080_iobase + PLX_BIGEND_REG);
+
+ disable_plx_interrupts(dev);
+
+ abort_dma(dev, 0);
+ abort_dma(dev, 1);
+
+ // configure dma0 mode
+ bits = 0;
+ // enable ready input, not sure if this is necessary
+ bits |= PLX_DMA_EN_READYIN_BIT;
+ // enable bterm, not sure if this is necessary
+ bits |= PLX_EN_BTERM_BIT;
+ // enable dma chaining
+ bits |= PLX_EN_CHAIN_BIT;
+ // enable interrupt on dma done (probably don't need this, since chain never finishes)
+ bits |= PLX_EN_DMA_DONE_INTR_BIT;
+ // don't increment local address during transfers (we are transferring from a fixed fifo register)
+ bits |= PLX_LOCAL_ADDR_CONST_BIT;
+ // route dma interrupt to pci bus
+ bits |= PLX_DMA_INTR_PCI_BIT;
+ // enable demand mode
+ bits |= PLX_DEMAND_MODE_BIT;
+ // enable local burst mode
+ bits |= PLX_DMA_LOCAL_BURST_EN_BIT;
+ // 4020 uses 32 bit dma
+ if (board(dev)->layout == LAYOUT_4020) {
+ bits |= PLX_LOCAL_BUS_32_WIDE_BITS;
+ } else { // localspace0 bus is 16 bits wide
+ bits |= PLX_LOCAL_BUS_16_WIDE_BITS;
+ }
+ writel(bits, plx_iobase + PLX_DMA1_MODE_REG);
+ if (ao_cmd_is_supported(board(dev)))
+ writel(bits, plx_iobase + PLX_DMA0_MODE_REG);
+
+ // enable interrupts on plx 9080
+ priv(dev)->plx_intcsr_bits |=
+ ICS_AERR | ICS_PERR | ICS_PIE | ICS_PLIE | ICS_PAIE | ICS_LIE |
+ ICS_DMA0_E | ICS_DMA1_E;
+ writel(priv(dev)->plx_intcsr_bits,
+ priv(dev)->plx9080_iobase + PLX_INTRCS_REG);
+}
+
+/* Allocate and initialize the subdevice structures.
+ */
+static int setup_subdevices(struct comedi_device * dev)
+{
+ struct comedi_subdevice *s;
+ void *dio_8255_iobase;
+ int i;
+
+ if (alloc_subdevices(dev, 10) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ /* analog input subdevice */
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DITHER | SDF_CMD_READ;
+ if (board(dev)->layout == LAYOUT_60XX)
+ s->subdev_flags |= SDF_COMMON | SDF_DIFF;
+ else if (board(dev)->layout == LAYOUT_64XX)
+ s->subdev_flags |= SDF_DIFF;
+ /* XXX Number of inputs in differential mode is ignored */
+ s->n_chan = board(dev)->ai_se_chans;
+ s->len_chanlist = 0x2000;
+ s->maxdata = (1 << board(dev)->ai_bits) - 1;
+ s->range_table = board(dev)->ai_range_table;
+ s->insn_read = ai_rinsn;
+ s->insn_config = ai_config_insn;
+ s->do_cmd = ai_cmd;
+ s->do_cmdtest = ai_cmdtest;
+ s->cancel = ai_cancel;
+ if (board(dev)->layout == LAYOUT_4020) {
+ unsigned int i;
+ uint8_t data;
+ // set adc to read from inputs (not internal calibration sources)
+ priv(dev)->i2c_cal_range_bits = adc_src_4020_bits(4);
+ // set channels to +-5 volt input ranges
+ for (i = 0; i < s->n_chan; i++)
+ priv(dev)->i2c_cal_range_bits |= attenuate_bit(i);
+ data = priv(dev)->i2c_cal_range_bits;
+ i2c_write(dev, RANGE_CAL_I2C_ADDR, &data, sizeof(data));
+ }
+
+ /* analog output subdevice */
+ s = dev->subdevices + 1;
+ if (board(dev)->ao_nchan) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags =
+ SDF_READABLE | SDF_WRITABLE | SDF_GROUND |
+ SDF_CMD_WRITE;
+ s->n_chan = board(dev)->ao_nchan;
+ s->maxdata = (1 << board(dev)->ao_bits) - 1;
+ s->range_table = board(dev)->ao_range_table;
+ s->insn_read = ao_readback_insn;
+ s->insn_write = ao_winsn;
+ if (ao_cmd_is_supported(board(dev))) {
+ dev->write_subdev = s;
+ s->do_cmdtest = ao_cmdtest;
+ s->do_cmd = ao_cmd;
+ s->len_chanlist = board(dev)->ao_nchan;
+ s->cancel = ao_cancel;
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ // digital input
+ s = dev->subdevices + 2;
+ if (board(dev)->layout == LAYOUT_64XX) {
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = di_rbits;
+ } else
+ s->type = COMEDI_SUBD_UNUSED;
+
+ // digital output
+ if (board(dev)->layout == LAYOUT_64XX) {
+ s = dev->subdevices + 3;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = do_wbits;
+ } else
+ s->type = COMEDI_SUBD_UNUSED;
+
+ /* 8255 */
+ s = dev->subdevices + 4;
+ if (board(dev)->has_8255) {
+ if (board(dev)->layout == LAYOUT_4020) {
+ dio_8255_iobase =
+ priv(dev)->main_iobase + I8255_4020_REG;
+ subdev_8255_init(dev, s, dio_callback_4020,
+ (unsigned long)dio_8255_iobase);
+ } else {
+ dio_8255_iobase =
+ priv(dev)->dio_counter_iobase + DIO_8255_OFFSET;
+ subdev_8255_init(dev, s, dio_callback,
+ (unsigned long)dio_8255_iobase);
+ }
+ } else
+ s->type = COMEDI_SUBD_UNUSED;
+
+ // 8 channel dio for 60xx
+ s = dev->subdevices + 5;
+ if (board(dev)->layout == LAYOUT_60XX) {
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_config = dio_60xx_config_insn;
+ s->insn_bits = dio_60xx_wbits;
+ } else
+ s->type = COMEDI_SUBD_UNUSED;
+
+ // caldac
+ s = dev->subdevices + 6;
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = 8;
+ if (board(dev)->layout == LAYOUT_4020)
+ s->maxdata = 0xfff;
+ else
+ s->maxdata = 0xff;
+ s->insn_read = calib_read_insn;
+ s->insn_write = calib_write_insn;
+ for (i = 0; i < s->n_chan; i++)
+ caldac_write(dev, i, s->maxdata / 2);
+
+ // 2 channel ad8402 potentiometer
+ s = dev->subdevices + 7;
+ if (board(dev)->layout == LAYOUT_64XX) {
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = 2;
+ s->insn_read = ad8402_read_insn;
+ s->insn_write = ad8402_write_insn;
+ s->maxdata = 0xff;
+ for (i = 0; i < s->n_chan; i++)
+ ad8402_write(dev, i, s->maxdata / 2);
+ } else
+ s->type = COMEDI_SUBD_UNUSED;
+
+ //serial EEPROM, if present
+ s = dev->subdevices + 8;
+ if (readl(priv(dev)->plx9080_iobase + PLX_CONTROL_REG) & CTL_EECHK) {
+ s->type = COMEDI_SUBD_MEMORY;
+ s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
+ s->n_chan = 128;
+ s->maxdata = 0xffff;
+ s->insn_read = eeprom_read_insn;
+ } else
+ s->type = COMEDI_SUBD_UNUSED;
+
+ // user counter subd XXX
+ s = dev->subdevices + 9;
+ s->type = COMEDI_SUBD_UNUSED;
+
+ return 0;
+}
+
+static void disable_plx_interrupts(struct comedi_device * dev)
+{
+ priv(dev)->plx_intcsr_bits = 0;
+ writel(priv(dev)->plx_intcsr_bits,
+ priv(dev)->plx9080_iobase + PLX_INTRCS_REG);
+}
+
+static void init_stc_registers(struct comedi_device * dev)
+{
+ uint16_t bits;
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+
+ // bit should be set for 6025, although docs say boards with <= 16 chans should be cleared XXX
+ if (1)
+ priv(dev)->adc_control1_bits |= ADC_QUEUE_CONFIG_BIT;
+ writew(priv(dev)->adc_control1_bits,
+ priv(dev)->main_iobase + ADC_CONTROL1_REG);
+
+ // 6402/16 manual says this register must be initialized to 0xff?
+ writew(0xff, priv(dev)->main_iobase + ADC_SAMPLE_INTERVAL_UPPER_REG);
+
+ bits = SLOW_DAC_BIT | DMA_CH_SELECT_BIT;
+ if (board(dev)->layout == LAYOUT_4020)
+ bits |= INTERNAL_CLOCK_4020_BITS;
+ priv(dev)->hw_config_bits |= bits;
+ writew(priv(dev)->hw_config_bits,
+ priv(dev)->main_iobase + HW_CONFIG_REG);
+
+ writew(0, priv(dev)->main_iobase + DAQ_SYNC_REG);
+ writew(0, priv(dev)->main_iobase + CALIBRATION_REG);
+
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ // set fifos to maximum size
+ priv(dev)->fifo_size_bits |= DAC_FIFO_BITS;
+ set_ai_fifo_segment_length(dev,
+ board(dev)->ai_fifo->max_segment_length);
+
+ priv(dev)->dac_control1_bits = DAC_OUTPUT_ENABLE_BIT;
+ priv(dev)->intr_enable_bits = /* EN_DAC_INTR_SRC_BIT | DAC_INTR_QEMPTY_BITS | */
+ EN_DAC_DONE_INTR_BIT | EN_DAC_UNDERRUN_BIT;
+ writew(priv(dev)->intr_enable_bits,
+ priv(dev)->main_iobase + INTR_ENABLE_REG);
+
+ disable_ai_pacing(dev);
+};
+
+int alloc_and_init_dma_members(struct comedi_device * dev)
+{
+ int i;
+
+ // alocate pci dma buffers
+ for (i = 0; i < ai_dma_ring_count(board(dev)); i++) {
+ priv(dev)->ai_buffer[i] =
+ pci_alloc_consistent(priv(dev)->hw_dev, DMA_BUFFER_SIZE,
+ &priv(dev)->ai_buffer_bus_addr[i]);
+ if (priv(dev)->ai_buffer[i] == NULL) {
+ return -ENOMEM;
+ }
+ }
+ for (i = 0; i < AO_DMA_RING_COUNT; i++) {
+ if (ao_cmd_is_supported(board(dev))) {
+ priv(dev)->ao_buffer[i] =
+ pci_alloc_consistent(priv(dev)->hw_dev,
+ DMA_BUFFER_SIZE,
+ &priv(dev)->ao_buffer_bus_addr[i]);
+ if (priv(dev)->ao_buffer[i] == NULL) {
+ return -ENOMEM;
+ }
+ }
+ }
+ // allocate dma descriptors
+ priv(dev)->ai_dma_desc =
+ pci_alloc_consistent(priv(dev)->hw_dev,
+ sizeof(struct plx_dma_desc) * ai_dma_ring_count(board(dev)),
+ &priv(dev)->ai_dma_desc_bus_addr);
+ if (priv(dev)->ai_dma_desc == NULL) {
+ return -ENOMEM;
+ }
+ DEBUG_PRINT("ai dma descriptors start at bus addr 0x%x\n",
+ priv(dev)->ai_dma_desc_bus_addr);
+ if (ao_cmd_is_supported(board(dev))) {
+ priv(dev)->ao_dma_desc =
+ pci_alloc_consistent(priv(dev)->hw_dev,
+ sizeof(struct plx_dma_desc) * AO_DMA_RING_COUNT,
+ &priv(dev)->ao_dma_desc_bus_addr);
+ if (priv(dev)->ao_dma_desc == NULL) {
+ return -ENOMEM;
+ }
+ DEBUG_PRINT("ao dma descriptors start at bus addr 0x%x\n",
+ priv(dev)->ao_dma_desc_bus_addr);
+ }
+ // initialize dma descriptors
+ for (i = 0; i < ai_dma_ring_count(board(dev)); i++) {
+ priv(dev)->ai_dma_desc[i].pci_start_addr =
+ cpu_to_le32(priv(dev)->ai_buffer_bus_addr[i]);
+ if (board(dev)->layout == LAYOUT_4020)
+ priv(dev)->ai_dma_desc[i].local_start_addr =
+ cpu_to_le32(priv(dev)->local1_iobase +
+ ADC_FIFO_REG);
+ else
+ priv(dev)->ai_dma_desc[i].local_start_addr =
+ cpu_to_le32(priv(dev)->local0_iobase +
+ ADC_FIFO_REG);
+ priv(dev)->ai_dma_desc[i].transfer_size = cpu_to_le32(0);
+ priv(dev)->ai_dma_desc[i].next =
+ cpu_to_le32((priv(dev)->ai_dma_desc_bus_addr + ((i +
+ 1) %
+ ai_dma_ring_count(board(dev))) *
+ sizeof(priv(dev)->
+ ai_dma_desc[0])) | PLX_DESC_IN_PCI_BIT |
+ PLX_INTR_TERM_COUNT | PLX_XFER_LOCAL_TO_PCI);
+ }
+ if (ao_cmd_is_supported(board(dev))) {
+ for (i = 0; i < AO_DMA_RING_COUNT; i++) {
+ priv(dev)->ao_dma_desc[i].pci_start_addr =
+ cpu_to_le32(priv(dev)->ao_buffer_bus_addr[i]);
+ priv(dev)->ao_dma_desc[i].local_start_addr =
+ cpu_to_le32(priv(dev)->local0_iobase +
+ DAC_FIFO_REG);
+ priv(dev)->ao_dma_desc[i].transfer_size =
+ cpu_to_le32(0);
+ priv(dev)->ao_dma_desc[i].next =
+ cpu_to_le32((priv(dev)->ao_dma_desc_bus_addr +
+ ((i + 1) % (AO_DMA_RING_COUNT)) *
+ sizeof(priv(dev)->
+ ao_dma_desc[0])) |
+ PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT);
+ }
+ }
+ return 0;
+}
+
+static inline void warn_external_queue(struct comedi_device * dev)
+{
+ comedi_error(dev,
+ "AO command and AI external channel queue cannot be used simultaneously.");
+ comedi_error(dev,
+ "Use internal AI channel queue (channels must be consecutive and use same range/aref)");
+}
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board.
+ */
+static int attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ struct pci_dev *pcidev;
+ int index;
+ uint32_t local_range, local_decode;
+ int retval;
+
+ printk("comedi%d: cb_pcidas64\n", dev->minor);
+
+/*
+ * Allocate the private structure area.
+ */
+ if (alloc_private(dev, sizeof(struct pcidas64_private)) < 0)
+ return -ENOMEM;
+
+/*
+ * Probe the device to determine what device in the series it is.
+ */
+
+ for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pcidev != NULL;
+ pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
+ // is it not a computer boards card?
+ if (pcidev->vendor != PCI_VENDOR_ID_COMPUTERBOARDS)
+ continue;
+ // loop through cards supported by this driver
+ for (index = 0; index < num_boards(); index++) {
+ if (pcidas64_boards[index].device_id != pcidev->device)
+ continue;
+ // was a particular bus/slot requested?
+ if (it->options[0] || it->options[1]) {
+ // are we on the wrong bus/slot?
+ if (pcidev->bus->number != it->options[0] ||
+ PCI_SLOT(pcidev->devfn) !=
+ it->options[1]) {
+ continue;
+ }
+ }
+ priv(dev)->hw_dev = pcidev;
+ dev->board_ptr = pcidas64_boards + index;
+ break;
+ }
+ if (dev->board_ptr)
+ break;
+ }
+
+ if (dev->board_ptr == NULL) {
+ printk("No supported ComputerBoards/MeasurementComputing card found\n");
+ return -EIO;
+ }
+
+ printk("Found %s on bus %i, slot %i\n", board(dev)->name,
+ pcidev->bus->number, PCI_SLOT(pcidev->devfn));
+
+ if (comedi_pci_enable(pcidev, driver_cb_pcidas.driver_name)) {
+ printk(KERN_WARNING
+ " failed to enable PCI device and request regions\n");
+ return -EIO;
+ }
+ pci_set_master(pcidev);
+
+ //Initialize dev->board_name
+ dev->board_name = board(dev)->name;
+
+ priv(dev)->plx9080_phys_iobase =
+ pci_resource_start(pcidev, PLX9080_BADDRINDEX);
+ priv(dev)->main_phys_iobase =
+ pci_resource_start(pcidev, MAIN_BADDRINDEX);
+ priv(dev)->dio_counter_phys_iobase =
+ pci_resource_start(pcidev, DIO_COUNTER_BADDRINDEX);
+
+ // remap, won't work with 2.0 kernels but who cares
+ priv(dev)->plx9080_iobase = ioremap(priv(dev)->plx9080_phys_iobase,
+ pci_resource_len(pcidev, PLX9080_BADDRINDEX));
+ priv(dev)->main_iobase = ioremap(priv(dev)->main_phys_iobase,
+ pci_resource_len(pcidev, MAIN_BADDRINDEX));
+ priv(dev)->dio_counter_iobase =
+ ioremap(priv(dev)->dio_counter_phys_iobase,
+ pci_resource_len(pcidev, DIO_COUNTER_BADDRINDEX));
+
+ if (!priv(dev)->plx9080_iobase || !priv(dev)->main_iobase
+ || !priv(dev)->dio_counter_iobase) {
+ printk(" failed to remap io memory\n");
+ return -ENOMEM;
+ }
+
+ DEBUG_PRINT(" plx9080 remapped to 0x%p\n", priv(dev)->plx9080_iobase);
+ DEBUG_PRINT(" main remapped to 0x%p\n", priv(dev)->main_iobase);
+ DEBUG_PRINT(" diocounter remapped to 0x%p\n",
+ priv(dev)->dio_counter_iobase);
+
+ // figure out what local addresses are
+ local_range =
+ readl(priv(dev)->plx9080_iobase +
+ PLX_LAS0RNG_REG) & LRNG_MEM_MASK;
+ local_decode =
+ readl(priv(dev)->plx9080_iobase +
+ PLX_LAS0MAP_REG) & local_range & LMAP_MEM_MASK;
+ priv(dev)->local0_iobase =
+ ((uint32_t) priv(dev)->
+ main_phys_iobase & ~local_range) | local_decode;
+ local_range =
+ readl(priv(dev)->plx9080_iobase +
+ PLX_LAS1RNG_REG) & LRNG_MEM_MASK;
+ local_decode =
+ readl(priv(dev)->plx9080_iobase +
+ PLX_LAS1MAP_REG) & local_range & LMAP_MEM_MASK;
+ priv(dev)->local1_iobase =
+ ((uint32_t) priv(dev)->
+ dio_counter_phys_iobase & ~local_range) | local_decode;
+
+ DEBUG_PRINT(" local 0 io addr 0x%x\n", priv(dev)->local0_iobase);
+ DEBUG_PRINT(" local 1 io addr 0x%x\n", priv(dev)->local1_iobase);
+
+ retval = alloc_and_init_dma_members(dev);
+ if (retval < 0)
+ return retval;
+
+ priv(dev)->hw_revision =
+ hw_revision(dev, readw(priv(dev)->main_iobase + HW_STATUS_REG));
+ printk(" stc hardware revision %i\n", priv(dev)->hw_revision);
+ init_plx9080(dev);
+ init_stc_registers(dev);
+ // get irq
+ if (comedi_request_irq(pcidev->irq, handle_interrupt, IRQF_SHARED,
+ "cb_pcidas64", dev)) {
+ printk(" unable to allocate irq %u\n", pcidev->irq);
+ return -EINVAL;
+ }
+ dev->irq = pcidev->irq;
+ printk(" irq %u\n", dev->irq);
+
+ retval = setup_subdevices(dev);
+ if (retval < 0) {
+ return retval;
+ }
+
+ return 0;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int detach(struct comedi_device * dev)
+{
+ unsigned int i;
+
+ printk("comedi%d: cb_pcidas: remove\n", dev->minor);
+
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+ if (priv(dev)) {
+ if (priv(dev)->hw_dev) {
+ if (priv(dev)->plx9080_iobase) {
+ disable_plx_interrupts(dev);
+ iounmap((void *)priv(dev)->plx9080_iobase);
+ }
+ if (priv(dev)->main_iobase)
+ iounmap((void *)priv(dev)->main_iobase);
+ if (priv(dev)->dio_counter_iobase)
+ iounmap((void *)priv(dev)->dio_counter_iobase);
+ // free pci dma buffers
+ for (i = 0; i < ai_dma_ring_count(board(dev)); i++) {
+ if (priv(dev)->ai_buffer[i])
+ pci_free_consistent(priv(dev)->hw_dev,
+ DMA_BUFFER_SIZE,
+ priv(dev)->ai_buffer[i],
+ priv(dev)->
+ ai_buffer_bus_addr[i]);
+ }
+ for (i = 0; i < AO_DMA_RING_COUNT; i++) {
+ if (priv(dev)->ao_buffer[i])
+ pci_free_consistent(priv(dev)->hw_dev,
+ DMA_BUFFER_SIZE,
+ priv(dev)->ao_buffer[i],
+ priv(dev)->
+ ao_buffer_bus_addr[i]);
+ }
+ // free dma descriptors
+ if (priv(dev)->ai_dma_desc)
+ pci_free_consistent(priv(dev)->hw_dev,
+ sizeof(struct plx_dma_desc) *
+ ai_dma_ring_count(board(dev)),
+ priv(dev)->ai_dma_desc,
+ priv(dev)->ai_dma_desc_bus_addr);
+ if (priv(dev)->ao_dma_desc)
+ pci_free_consistent(priv(dev)->hw_dev,
+ sizeof(struct plx_dma_desc) *
+ AO_DMA_RING_COUNT,
+ priv(dev)->ao_dma_desc,
+ priv(dev)->ao_dma_desc_bus_addr);
+ if (priv(dev)->main_phys_iobase) {
+ comedi_pci_disable(priv(dev)->hw_dev);
+ }
+ pci_dev_put(priv(dev)->hw_dev);
+ }
+ }
+ if (dev->subdevices)
+ subdev_8255_cleanup(dev, dev->subdevices + 4);
+
+ return 0;
+}
+
+static int ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int bits = 0, n, i;
+ unsigned int channel, range, aref;
+ unsigned long flags;
+ static const int timeout = 100;
+
+ DEBUG_PRINT("chanspec 0x%x\n", insn->chanspec);
+ channel = CR_CHAN(insn->chanspec);
+ range = CR_RANGE(insn->chanspec);
+ aref = CR_AREF(insn->chanspec);
+
+ // disable card's analog input interrupt sources and pacing
+ // 4020 generates dac done interrupts even though they are disabled
+ disable_ai_pacing(dev);
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ if (insn->chanspec & CR_ALT_FILTER)
+ priv(dev)->adc_control1_bits |= ADC_DITHER_BIT;
+ else
+ priv(dev)->adc_control1_bits &= ~ADC_DITHER_BIT;
+ writew(priv(dev)->adc_control1_bits,
+ priv(dev)->main_iobase + ADC_CONTROL1_REG);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ if (board(dev)->layout != LAYOUT_4020) {
+ // use internal queue
+ priv(dev)->hw_config_bits &= ~EXT_QUEUE_BIT;
+ writew(priv(dev)->hw_config_bits,
+ priv(dev)->main_iobase + HW_CONFIG_REG);
+
+ // ALT_SOURCE is internal calibration reference
+ if (insn->chanspec & CR_ALT_SOURCE) {
+ unsigned int cal_en_bit;
+
+ DEBUG_PRINT("reading calibration source\n");
+ if (board(dev)->layout == LAYOUT_60XX)
+ cal_en_bit = CAL_EN_60XX_BIT;
+ else
+ cal_en_bit = CAL_EN_64XX_BIT;
+ // select internal reference source to connect to channel 0
+ writew(cal_en_bit | adc_src_bits(priv(dev)->
+ calibration_source),
+ priv(dev)->main_iobase + CALIBRATION_REG);
+ } else {
+ // make sure internal calibration source is turned off
+ writew(0, priv(dev)->main_iobase + CALIBRATION_REG);
+ }
+ // load internal queue
+ bits = 0;
+ // set gain
+ bits |= ai_range_bits_6xxx(dev, CR_RANGE(insn->chanspec));
+ // set single-ended / differential
+ bits |= se_diff_bit_6xxx(dev, aref == AREF_DIFF);
+ if (aref == AREF_COMMON)
+ bits |= ADC_COMMON_BIT;
+ bits |= adc_chan_bits(channel);
+ // set stop channel
+ writew(adc_chan_bits(channel),
+ priv(dev)->main_iobase + ADC_QUEUE_HIGH_REG);
+ // set start channel, and rest of settings
+ writew(bits, priv(dev)->main_iobase + ADC_QUEUE_LOAD_REG);
+ } else {
+ uint8_t old_cal_range_bits = priv(dev)->i2c_cal_range_bits;
+
+ priv(dev)->i2c_cal_range_bits &= ~ADC_SRC_4020_MASK;
+ if (insn->chanspec & CR_ALT_SOURCE) {
+ DEBUG_PRINT("reading calibration source\n");
+ priv(dev)->i2c_cal_range_bits |=
+ adc_src_4020_bits(priv(dev)->
+ calibration_source);
+ } else { //select BNC inputs
+ priv(dev)->i2c_cal_range_bits |= adc_src_4020_bits(4);
+ }
+ // select range
+ if (range == 0)
+ priv(dev)->i2c_cal_range_bits |= attenuate_bit(channel);
+ else
+ priv(dev)->i2c_cal_range_bits &=
+ ~attenuate_bit(channel);
+ // update calibration/range i2c register only if necessary, as it is very slow
+ if (old_cal_range_bits != priv(dev)->i2c_cal_range_bits) {
+ uint8_t i2c_data = priv(dev)->i2c_cal_range_bits;
+ i2c_write(dev, RANGE_CAL_I2C_ADDR, &i2c_data,
+ sizeof(i2c_data));
+ }
+
+ /* 4020 manual asks that sample interval register to be set before writing to convert register.
+ * Using somewhat arbitrary setting of 4 master clock ticks = 0.1 usec */
+ writew(0,
+ priv(dev)->main_iobase + ADC_SAMPLE_INTERVAL_UPPER_REG);
+ writew(2,
+ priv(dev)->main_iobase + ADC_SAMPLE_INTERVAL_LOWER_REG);
+ }
+
+ for (n = 0; n < insn->n; n++) {
+
+ // clear adc buffer (inside loop for 4020 sake)
+ writew(0, priv(dev)->main_iobase + ADC_BUFFER_CLEAR_REG);
+
+ /* trigger conversion, bits sent only matter for 4020 */
+ writew(adc_convert_chan_4020_bits(CR_CHAN(insn->chanspec)),
+ priv(dev)->main_iobase + ADC_CONVERT_REG);
+
+ // wait for data
+ for (i = 0; i < timeout; i++) {
+ bits = readw(priv(dev)->main_iobase + HW_STATUS_REG);
+ DEBUG_PRINT(" pipe bits 0x%x\n", pipe_full_bits(bits));
+ if (board(dev)->layout == LAYOUT_4020) {
+ if (readw(priv(dev)->main_iobase +
+ ADC_WRITE_PNTR_REG))
+ break;
+ } else {
+ if (pipe_full_bits(bits))
+ break;
+ }
+ comedi_udelay(1);
+ }
+ DEBUG_PRINT(" looped %i times waiting for data\n", i);
+ if (i == timeout) {
+ comedi_error(dev, " analog input read insn timed out");
+ rt_printk(" status 0x%x\n", bits);
+ return -ETIME;
+ }
+ if (board(dev)->layout == LAYOUT_4020)
+ data[n] =
+ readl(priv(dev)->dio_counter_iobase +
+ ADC_FIFO_REG) & 0xffff;
+ else
+ data[n] =
+ readw(priv(dev)->main_iobase + PIPE1_READ_REG);
+ }
+
+ return n;
+}
+
+static int ai_config_calibration_source(struct comedi_device * dev, unsigned int * data)
+{
+ unsigned int source = data[1];
+ int num_calibration_sources;
+
+ if (board(dev)->layout == LAYOUT_60XX)
+ num_calibration_sources = 16;
+ else
+ num_calibration_sources = 8;
+ if (source >= num_calibration_sources) {
+ printk("invalid calibration source: %i\n", source);
+ return -EINVAL;
+ }
+
+ DEBUG_PRINT("setting calibration source to %i\n", source);
+ priv(dev)->calibration_source = source;
+
+ return 2;
+}
+
+static int ai_config_block_size(struct comedi_device * dev, unsigned int * data)
+{
+ int fifo_size;
+ const struct hw_fifo_info *const fifo = board(dev)->ai_fifo;
+ unsigned int block_size, requested_block_size;
+ int retval;
+
+ requested_block_size = data[1];
+
+ if (requested_block_size) {
+ fifo_size =
+ requested_block_size * fifo->num_segments /
+ bytes_in_sample;
+
+ retval = set_ai_fifo_size(dev, fifo_size);
+ if (retval < 0)
+ return retval;
+
+ }
+
+ block_size = ai_fifo_size(dev) / fifo->num_segments * bytes_in_sample;
+
+ data[1] = block_size;
+
+ return 2;
+}
+
+static int ai_config_master_clock_4020(struct comedi_device * dev, unsigned int * data)
+{
+ unsigned int divisor = data[4];
+ int retval = 0;
+
+ if (divisor < 2) {
+ divisor = 2;
+ retval = -EAGAIN;
+ }
+
+ switch (data[1]) {
+ case COMEDI_EV_SCAN_BEGIN:
+ priv(dev)->ext_clock.divisor = divisor;
+ priv(dev)->ext_clock.chanspec = data[2];
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ data[4] = divisor;
+
+ return retval ? retval : 5;
+}
+
+// XXX could add support for 60xx series
+static int ai_config_master_clock(struct comedi_device * dev, unsigned int * data)
+{
+
+ switch (board(dev)->layout) {
+ case LAYOUT_4020:
+ return ai_config_master_clock_4020(dev, data);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ai_config_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int id = data[0];
+
+ switch (id) {
+ case INSN_CONFIG_ALT_SOURCE:
+ return ai_config_calibration_source(dev, data);
+ break;
+ case INSN_CONFIG_BLOCK_SIZE:
+ return ai_config_block_size(dev, data);
+ break;
+ case INSN_CONFIG_TIMER_1:
+ return ai_config_master_clock(dev, data);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return -EINVAL;
+}
+
+static int ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+ unsigned int tmp_arg, tmp_arg2;
+ int i;
+ int aref;
+ unsigned int triggers;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_EXT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ triggers = TRIG_TIMER;
+ if (board(dev)->layout == LAYOUT_4020)
+ triggers |= TRIG_OTHER;
+ else
+ triggers |= TRIG_FOLLOW;
+ cmd->scan_begin_src &= triggers;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ triggers = TRIG_TIMER;
+ if (board(dev)->layout == LAYOUT_4020)
+ triggers |= TRIG_NOW;
+ else
+ triggers |= TRIG_EXT;
+ cmd->convert_src &= triggers;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_EXT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ // uniqueness check
+ if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
+ err++;
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_OTHER &&
+ cmd->scan_begin_src != TRIG_FOLLOW)
+ err++;
+ if (cmd->convert_src != TRIG_TIMER &&
+ cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT &&
+ cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_EXT)
+ err++;
+
+ // compatibility check
+ if (cmd->convert_src == TRIG_EXT && cmd->scan_begin_src == TRIG_TIMER)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT &&
+ cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_EXT)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (board(dev)->layout == LAYOUT_4020) {
+ if (cmd->convert_arg) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ } else {
+ if (cmd->convert_arg < board(dev)->ai_speed) {
+ cmd->convert_arg = board(dev)->ai_speed;
+ err++;
+ }
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ // if scans are timed faster than conversion rate allows
+ if (cmd->convert_arg * cmd->chanlist_len >
+ cmd->scan_begin_arg) {
+ cmd->scan_begin_arg =
+ cmd->convert_arg *
+ cmd->chanlist_len;
+ err++;
+ }
+ }
+ }
+ }
+
+ if (!cmd->chanlist_len) {
+ cmd->chanlist_len = 1;
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ switch (cmd->stop_src) {
+ case TRIG_EXT:
+ break;
+ case TRIG_COUNT:
+ if (!cmd->stop_arg) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ break;
+ case TRIG_NONE:
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp_arg = cmd->convert_arg;
+ tmp_arg2 = cmd->scan_begin_arg;
+ check_adc_timing(dev, cmd);
+ if (tmp_arg != cmd->convert_arg)
+ err++;
+ if (tmp_arg2 != cmd->scan_begin_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ // make sure user is doesn't change analog reference mid chanlist
+ if (cmd->chanlist) {
+ aref = CR_AREF(cmd->chanlist[0]);
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ if (aref != CR_AREF(cmd->chanlist[i])) {
+ comedi_error(dev,
+ "all elements in chanlist must use the same analog reference");
+ err++;
+ break;
+ }
+ }
+ // check 4020 chanlist
+ if (board(dev)->layout == LAYOUT_4020) {
+ unsigned int first_channel = CR_CHAN(cmd->chanlist[0]);
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ if (CR_CHAN(cmd->chanlist[i]) !=
+ first_channel + i) {
+ comedi_error(dev,
+ "chanlist must use consecutive channels");
+ err++;
+ break;
+ }
+ }
+ if (cmd->chanlist_len == 3) {
+ comedi_error(dev,
+ "chanlist cannot be 3 channels long, use 1, 2, or 4 channels");
+ err++;
+ }
+ }
+ }
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int use_hw_sample_counter(struct comedi_cmd * cmd)
+{
+// disable for now until I work out a race
+ return 0;
+
+ if (cmd->stop_src == TRIG_COUNT && cmd->stop_arg <= max_counter_value)
+ return 1;
+ else
+ return 0;
+}
+
+static void setup_sample_counters(struct comedi_device * dev, struct comedi_cmd * cmd)
+{
+ if (cmd->stop_src == TRIG_COUNT) {
+ // set software count
+ priv(dev)->ai_count = cmd->stop_arg * cmd->chanlist_len;
+ }
+ // load hardware conversion counter
+ if (use_hw_sample_counter(cmd)) {
+ writew(cmd->stop_arg & 0xffff,
+ priv(dev)->main_iobase + ADC_COUNT_LOWER_REG);
+ writew((cmd->stop_arg >> 16) & 0xff,
+ priv(dev)->main_iobase + ADC_COUNT_UPPER_REG);
+ } else {
+ writew(1, priv(dev)->main_iobase + ADC_COUNT_LOWER_REG);
+ }
+}
+
+static inline unsigned int dma_transfer_size(struct comedi_device * dev)
+{
+ unsigned int num_samples;
+
+ num_samples =
+ priv(dev)->ai_fifo_segment_length *
+ board(dev)->ai_fifo->sample_packing_ratio;
+ if (num_samples > DMA_BUFFER_SIZE / sizeof(uint16_t))
+ num_samples = DMA_BUFFER_SIZE / sizeof(uint16_t);
+
+ return num_samples;
+}
+
+static void disable_ai_pacing(struct comedi_device * dev)
+{
+ unsigned long flags;
+
+ disable_ai_interrupts(dev);
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ priv(dev)->adc_control1_bits &= ~ADC_SW_GATE_BIT;
+ writew(priv(dev)->adc_control1_bits,
+ priv(dev)->main_iobase + ADC_CONTROL1_REG);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ /* disable pacing, triggering, etc */
+ writew(ADC_DMA_DISABLE_BIT | ADC_SOFT_GATE_BITS | ADC_GATE_LEVEL_BIT,
+ priv(dev)->main_iobase + ADC_CONTROL0_REG);
+}
+
+static void disable_ai_interrupts(struct comedi_device * dev)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ priv(dev)->intr_enable_bits &=
+ ~EN_ADC_INTR_SRC_BIT & ~EN_ADC_DONE_INTR_BIT &
+ ~EN_ADC_ACTIVE_INTR_BIT & ~EN_ADC_STOP_INTR_BIT &
+ ~EN_ADC_OVERRUN_BIT & ~ADC_INTR_SRC_MASK;
+ writew(priv(dev)->intr_enable_bits,
+ priv(dev)->main_iobase + INTR_ENABLE_REG);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ DEBUG_PRINT("intr enable bits 0x%x\n", priv(dev)->intr_enable_bits);
+}
+
+static void enable_ai_interrupts(struct comedi_device * dev, const struct comedi_cmd * cmd)
+{
+ uint32_t bits;
+ unsigned long flags;
+
+ bits = EN_ADC_OVERRUN_BIT | EN_ADC_DONE_INTR_BIT |
+ EN_ADC_ACTIVE_INTR_BIT | EN_ADC_STOP_INTR_BIT;
+ // Use pio transfer and interrupt on end of conversion if TRIG_WAKE_EOS flag is set.
+ if (cmd->flags & TRIG_WAKE_EOS) {
+ // 4020 doesn't support pio transfers except for fifo dregs
+ if (board(dev)->layout != LAYOUT_4020)
+ bits |= ADC_INTR_EOSCAN_BITS | EN_ADC_INTR_SRC_BIT;
+ }
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ priv(dev)->intr_enable_bits |= bits;
+ writew(priv(dev)->intr_enable_bits,
+ priv(dev)->main_iobase + INTR_ENABLE_REG);
+ DEBUG_PRINT("intr enable bits 0x%x\n", priv(dev)->intr_enable_bits);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+static uint32_t ai_convert_counter_6xxx(const struct comedi_device * dev,
+ const struct comedi_cmd * cmd)
+{
+ // supposed to load counter with desired divisor minus 3
+ return cmd->convert_arg / TIMER_BASE - 3;
+}
+
+static uint32_t ai_scan_counter_6xxx(struct comedi_device * dev, struct comedi_cmd * cmd)
+{
+ uint32_t count;
+ // figure out how long we need to delay at end of scan
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ count = (cmd->scan_begin_arg -
+ (cmd->convert_arg * (cmd->chanlist_len - 1)))
+ / TIMER_BASE;
+ break;
+ case TRIG_FOLLOW:
+ count = cmd->convert_arg / TIMER_BASE;
+ break;
+ default:
+ return 0;
+ break;
+ }
+ return count - 3;
+}
+
+static uint32_t ai_convert_counter_4020(struct comedi_device * dev, struct comedi_cmd * cmd)
+{
+ unsigned int divisor;
+
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ divisor = cmd->scan_begin_arg / TIMER_BASE;
+ break;
+ case TRIG_OTHER:
+ divisor = priv(dev)->ext_clock.divisor;
+ break;
+ default: // should never happen
+ comedi_error(dev, "bug! failed to set ai pacing!");
+ divisor = 1000;
+ break;
+ }
+
+ // supposed to load counter with desired divisor minus 2 for 4020
+ return divisor - 2;
+}
+
+static void select_master_clock_4020(struct comedi_device * dev,
+ const struct comedi_cmd * cmd)
+{
+ // select internal/external master clock
+ priv(dev)->hw_config_bits &= ~MASTER_CLOCK_4020_MASK;
+ if (cmd->scan_begin_src == TRIG_OTHER) {
+ int chanspec = priv(dev)->ext_clock.chanspec;
+
+ if (CR_CHAN(chanspec))
+ priv(dev)->hw_config_bits |= BNC_CLOCK_4020_BITS;
+ else
+ priv(dev)->hw_config_bits |= EXT_CLOCK_4020_BITS;
+ } else {
+ priv(dev)->hw_config_bits |= INTERNAL_CLOCK_4020_BITS;
+ }
+ writew(priv(dev)->hw_config_bits,
+ priv(dev)->main_iobase + HW_CONFIG_REG);
+}
+
+static void select_master_clock(struct comedi_device * dev, const struct comedi_cmd * cmd)
+{
+ switch (board(dev)->layout) {
+ case LAYOUT_4020:
+ select_master_clock_4020(dev, cmd);
+ break;
+ default:
+ break;
+ }
+}
+
+static inline void dma_start_sync(struct comedi_device * dev, unsigned int channel)
+{
+ unsigned long flags;
+
+ // spinlock for plx dma control/status reg
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ if (channel)
+ writeb(PLX_DMA_EN_BIT | PLX_DMA_START_BIT |
+ PLX_CLEAR_DMA_INTR_BIT,
+ priv(dev)->plx9080_iobase + PLX_DMA1_CS_REG);
+ else
+ writeb(PLX_DMA_EN_BIT | PLX_DMA_START_BIT |
+ PLX_CLEAR_DMA_INTR_BIT,
+ priv(dev)->plx9080_iobase + PLX_DMA0_CS_REG);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+static void set_ai_pacing(struct comedi_device * dev, struct comedi_cmd * cmd)
+{
+ uint32_t convert_counter = 0, scan_counter = 0;
+
+ check_adc_timing(dev, cmd);
+
+ select_master_clock(dev, cmd);
+
+ if (board(dev)->layout == LAYOUT_4020) {
+ convert_counter = ai_convert_counter_4020(dev, cmd);
+ } else {
+ convert_counter = ai_convert_counter_6xxx(dev, cmd);
+ scan_counter = ai_scan_counter_6xxx(dev, cmd);
+ }
+
+ // load lower 16 bits of convert interval
+ writew(convert_counter & 0xffff,
+ priv(dev)->main_iobase + ADC_SAMPLE_INTERVAL_LOWER_REG);
+ DEBUG_PRINT("convert counter 0x%x\n", convert_counter);
+ // load upper 8 bits of convert interval
+ writew((convert_counter >> 16) & 0xff,
+ priv(dev)->main_iobase + ADC_SAMPLE_INTERVAL_UPPER_REG);
+ // load lower 16 bits of scan delay
+ writew(scan_counter & 0xffff,
+ priv(dev)->main_iobase + ADC_DELAY_INTERVAL_LOWER_REG);
+ // load upper 8 bits of scan delay
+ writew((scan_counter >> 16) & 0xff,
+ priv(dev)->main_iobase + ADC_DELAY_INTERVAL_UPPER_REG);
+ DEBUG_PRINT("scan counter 0x%x\n", scan_counter);
+}
+
+static int use_internal_queue_6xxx(const struct comedi_cmd * cmd)
+{
+ int i;
+ for (i = 0; i + 1 < cmd->chanlist_len; i++) {
+ if (CR_CHAN(cmd->chanlist[i + 1]) !=
+ CR_CHAN(cmd->chanlist[i]) + 1)
+ return 0;
+ if (CR_RANGE(cmd->chanlist[i + 1]) !=
+ CR_RANGE(cmd->chanlist[i]))
+ return 0;
+ if (CR_AREF(cmd->chanlist[i + 1]) != CR_AREF(cmd->chanlist[i]))
+ return 0;
+ }
+ return 1;
+}
+
+static int setup_channel_queue(struct comedi_device * dev, const struct comedi_cmd * cmd)
+{
+ unsigned short bits;
+ int i;
+
+ if (board(dev)->layout != LAYOUT_4020) {
+ if (use_internal_queue_6xxx(cmd)) {
+ priv(dev)->hw_config_bits &= ~EXT_QUEUE_BIT;
+ writew(priv(dev)->hw_config_bits,
+ priv(dev)->main_iobase + HW_CONFIG_REG);
+ bits = 0;
+ // set channel
+ bits |= adc_chan_bits(CR_CHAN(cmd->chanlist[0]));
+ // set gain
+ bits |= ai_range_bits_6xxx(dev,
+ CR_RANGE(cmd->chanlist[0]));
+ // set single-ended / differential
+ bits |= se_diff_bit_6xxx(dev,
+ CR_AREF(cmd->chanlist[0]) == AREF_DIFF);
+ if (CR_AREF(cmd->chanlist[0]) == AREF_COMMON)
+ bits |= ADC_COMMON_BIT;
+ // set stop channel
+ writew(adc_chan_bits(CR_CHAN(cmd->chanlist[cmd->
+ chanlist_len - 1])),
+ priv(dev)->main_iobase + ADC_QUEUE_HIGH_REG);
+ // set start channel, and rest of settings
+ writew(bits,
+ priv(dev)->main_iobase + ADC_QUEUE_LOAD_REG);
+ } else {
+ // use external queue
+ if (dev->write_subdev && dev->write_subdev->busy) {
+ warn_external_queue(dev);
+ return -EBUSY;
+ }
+ priv(dev)->hw_config_bits |= EXT_QUEUE_BIT;
+ writew(priv(dev)->hw_config_bits,
+ priv(dev)->main_iobase + HW_CONFIG_REG);
+ // clear DAC buffer to prevent weird interactions
+ writew(0,
+ priv(dev)->main_iobase + DAC_BUFFER_CLEAR_REG);
+ // clear queue pointer
+ writew(0, priv(dev)->main_iobase + ADC_QUEUE_CLEAR_REG);
+ // load external queue
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ bits = 0;
+ // set channel
+ bits |= adc_chan_bits(CR_CHAN(cmd->
+ chanlist[i]));
+ // set gain
+ bits |= ai_range_bits_6xxx(dev,
+ CR_RANGE(cmd->chanlist[i]));
+ // set single-ended / differential
+ bits |= se_diff_bit_6xxx(dev,
+ CR_AREF(cmd->chanlist[i]) == AREF_DIFF);
+ if (CR_AREF(cmd->chanlist[i]) == AREF_COMMON)
+ bits |= ADC_COMMON_BIT;
+ // mark end of queue
+ if (i == cmd->chanlist_len - 1)
+ bits |= QUEUE_EOSCAN_BIT |
+ QUEUE_EOSEQ_BIT;
+ writew(bits,
+ priv(dev)->main_iobase +
+ ADC_QUEUE_FIFO_REG);
+ DEBUG_PRINT
+ ("wrote 0x%x to external channel queue\n",
+ bits);
+ }
+ /* doing a queue clear is not specified in board docs,
+ * but required for reliable operation */
+ writew(0, priv(dev)->main_iobase + ADC_QUEUE_CLEAR_REG);
+ // prime queue holding register
+ writew(0, priv(dev)->main_iobase + ADC_QUEUE_LOAD_REG);
+ }
+ } else {
+ unsigned short old_cal_range_bits =
+ priv(dev)->i2c_cal_range_bits;
+
+ priv(dev)->i2c_cal_range_bits &= ~ADC_SRC_4020_MASK;
+ //select BNC inputs
+ priv(dev)->i2c_cal_range_bits |= adc_src_4020_bits(4);
+ // select ranges
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int channel = CR_CHAN(cmd->chanlist[i]);
+ unsigned int range = CR_RANGE(cmd->chanlist[i]);
+
+ if (range == 0)
+ priv(dev)->i2c_cal_range_bits |=
+ attenuate_bit(channel);
+ else
+ priv(dev)->i2c_cal_range_bits &=
+ ~attenuate_bit(channel);
+ }
+ // update calibration/range i2c register only if necessary, as it is very slow
+ if (old_cal_range_bits != priv(dev)->i2c_cal_range_bits) {
+ uint8_t i2c_data = priv(dev)->i2c_cal_range_bits;
+ i2c_write(dev, RANGE_CAL_I2C_ADDR, &i2c_data,
+ sizeof(i2c_data));
+ }
+ }
+ return 0;
+}
+
+static inline void load_first_dma_descriptor(struct comedi_device * dev,
+ unsigned int dma_channel, unsigned int descriptor_bits)
+{
+ /* The transfer size, pci address, and local address registers
+ * are supposedly unused during chained dma,
+ * but I have found that left over values from last operation
+ * occasionally cause problems with transfer of first dma
+ * block. Initializing them to zero seems to fix the problem. */
+ if (dma_channel) {
+ writel(0,
+ priv(dev)->plx9080_iobase + PLX_DMA1_TRANSFER_SIZE_REG);
+ writel(0, priv(dev)->plx9080_iobase + PLX_DMA1_PCI_ADDRESS_REG);
+ writel(0,
+ priv(dev)->plx9080_iobase + PLX_DMA1_LOCAL_ADDRESS_REG);
+ writel(descriptor_bits,
+ priv(dev)->plx9080_iobase + PLX_DMA1_DESCRIPTOR_REG);
+ } else {
+ writel(0,
+ priv(dev)->plx9080_iobase + PLX_DMA0_TRANSFER_SIZE_REG);
+ writel(0, priv(dev)->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG);
+ writel(0,
+ priv(dev)->plx9080_iobase + PLX_DMA0_LOCAL_ADDRESS_REG);
+ writel(descriptor_bits,
+ priv(dev)->plx9080_iobase + PLX_DMA0_DESCRIPTOR_REG);
+ }
+}
+
+static int ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ uint32_t bits;
+ unsigned int i;
+ unsigned long flags;
+ int retval;
+
+ disable_ai_pacing(dev);
+ abort_dma(dev, 1);
+
+ retval = setup_channel_queue(dev, cmd);
+ if (retval < 0)
+ return retval;
+
+ // make sure internal calibration source is turned off
+ writew(0, priv(dev)->main_iobase + CALIBRATION_REG);
+
+ set_ai_pacing(dev, cmd);
+
+ setup_sample_counters(dev, cmd);
+
+ enable_ai_interrupts(dev, cmd);
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ /* set mode, allow conversions through software gate */
+ priv(dev)->adc_control1_bits |= ADC_SW_GATE_BIT;
+ priv(dev)->adc_control1_bits &= ~ADC_DITHER_BIT;
+ if (board(dev)->layout != LAYOUT_4020) {
+ priv(dev)->adc_control1_bits &= ~ADC_MODE_MASK;
+ if (cmd->convert_src == TRIG_EXT)
+ priv(dev)->adc_control1_bits |= adc_mode_bits(13); // good old mode 13
+ else
+ priv(dev)->adc_control1_bits |= adc_mode_bits(8); // mode 8. What else could you need?
+ } else {
+ priv(dev)->adc_control1_bits &= ~CHANNEL_MODE_4020_MASK;
+ if (cmd->chanlist_len == 4)
+ priv(dev)->adc_control1_bits |= FOUR_CHANNEL_4020_BITS;
+ else if (cmd->chanlist_len == 2)
+ priv(dev)->adc_control1_bits |= TWO_CHANNEL_4020_BITS;
+ priv(dev)->adc_control1_bits &= ~ADC_LO_CHANNEL_4020_MASK;
+ priv(dev)->adc_control1_bits |=
+ adc_lo_chan_4020_bits(CR_CHAN(cmd->chanlist[0]));
+ priv(dev)->adc_control1_bits &= ~ADC_HI_CHANNEL_4020_MASK;
+ priv(dev)->adc_control1_bits |=
+ adc_hi_chan_4020_bits(CR_CHAN(cmd->chanlist[cmd->
+ chanlist_len - 1]));
+ }
+ writew(priv(dev)->adc_control1_bits,
+ priv(dev)->main_iobase + ADC_CONTROL1_REG);
+ DEBUG_PRINT("control1 bits 0x%x\n", priv(dev)->adc_control1_bits);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ // clear adc buffer
+ writew(0, priv(dev)->main_iobase + ADC_BUFFER_CLEAR_REG);
+
+ if ((cmd->flags & TRIG_WAKE_EOS) == 0 ||
+ board(dev)->layout == LAYOUT_4020) {
+ priv(dev)->ai_dma_index = 0;
+
+ // set dma transfer size
+ for (i = 0; i < ai_dma_ring_count(board(dev)); i++)
+ priv(dev)->ai_dma_desc[i].transfer_size =
+ cpu_to_le32(dma_transfer_size(dev) *
+ sizeof(uint16_t));
+
+ // give location of first dma descriptor
+ load_first_dma_descriptor(dev, 1,
+ priv(dev)->
+ ai_dma_desc_bus_addr | PLX_DESC_IN_PCI_BIT |
+ PLX_INTR_TERM_COUNT | PLX_XFER_LOCAL_TO_PCI);
+
+ dma_start_sync(dev, 1);
+ }
+
+ if (board(dev)->layout == LAYOUT_4020) {
+ /* set source for external triggers */
+ bits = 0;
+ if (cmd->start_src == TRIG_EXT && CR_CHAN(cmd->start_arg))
+ bits |= EXT_START_TRIG_BNC_BIT;
+ if (cmd->stop_src == TRIG_EXT && CR_CHAN(cmd->stop_arg))
+ bits |= EXT_STOP_TRIG_BNC_BIT;
+ writew(bits, priv(dev)->main_iobase + DAQ_ATRIG_LOW_4020_REG);
+ }
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+
+ /* enable pacing, triggering, etc */
+ bits = ADC_ENABLE_BIT | ADC_SOFT_GATE_BITS | ADC_GATE_LEVEL_BIT;
+ if (cmd->flags & TRIG_WAKE_EOS)
+ bits |= ADC_DMA_DISABLE_BIT;
+ // set start trigger
+ if (cmd->start_src == TRIG_EXT) {
+ bits |= ADC_START_TRIG_EXT_BITS;
+ if (cmd->start_arg & CR_INVERT)
+ bits |= ADC_START_TRIG_FALLING_BIT;
+ } else if (cmd->start_src == TRIG_NOW)
+ bits |= ADC_START_TRIG_SOFT_BITS;
+ if (use_hw_sample_counter(cmd))
+ bits |= ADC_SAMPLE_COUNTER_EN_BIT;
+ writew(bits, priv(dev)->main_iobase + ADC_CONTROL0_REG);
+ DEBUG_PRINT("control0 bits 0x%x\n", bits);
+
+ priv(dev)->ai_cmd_running = 1;
+
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ // start aquisition
+ if (cmd->start_src == TRIG_NOW) {
+ writew(0, priv(dev)->main_iobase + ADC_START_REG);
+ DEBUG_PRINT("soft trig\n");
+ }
+
+ return 0;
+}
+
+// read num_samples from 16 bit wide ai fifo
+static void pio_drain_ai_fifo_16(struct comedi_device * dev)
+{
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int i;
+ uint16_t prepost_bits;
+ int read_segment, read_index, write_segment, write_index;
+ int num_samples;
+
+ do {
+ // get least significant 15 bits
+ read_index =
+ readw(priv(dev)->main_iobase +
+ ADC_READ_PNTR_REG) & 0x7fff;
+ write_index =
+ readw(priv(dev)->main_iobase +
+ ADC_WRITE_PNTR_REG) & 0x7fff;
+ /* Get most significant bits (grey code). Different boards use different code
+ * so use a scheme that doesn't depend on encoding. This read must
+ * occur after reading least significant 15 bits to avoid race
+ * with fifo switching to next segment. */
+ prepost_bits = readw(priv(dev)->main_iobase + PREPOST_REG);
+
+ /* if read and write pointers are not on the same fifo segment, read to the
+ * end of the read segment */
+ read_segment = adc_upper_read_ptr_code(prepost_bits);
+ write_segment = adc_upper_write_ptr_code(prepost_bits);
+
+ DEBUG_PRINT(" rd seg %i, wrt seg %i, rd idx %i, wrt idx %i\n",
+ read_segment, write_segment, read_index, write_index);
+
+ if (read_segment != write_segment)
+ num_samples =
+ priv(dev)->ai_fifo_segment_length - read_index;
+ else
+ num_samples = write_index - read_index;
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (priv(dev)->ai_count == 0)
+ break;
+ if (num_samples > priv(dev)->ai_count) {
+ num_samples = priv(dev)->ai_count;
+ }
+ priv(dev)->ai_count -= num_samples;
+ }
+
+ if (num_samples < 0) {
+ rt_printk(" cb_pcidas64: bug! num_samples < 0\n");
+ break;
+ }
+
+ DEBUG_PRINT(" read %i samples from fifo\n", num_samples);
+
+ for (i = 0; i < num_samples; i++) {
+ cfc_write_to_buffer(s,
+ readw(priv(dev)->main_iobase + ADC_FIFO_REG));
+ }
+
+ } while (read_segment != write_segment);
+}
+
+/* Read from 32 bit wide ai fifo of 4020 - deal with insane grey coding of pointers.
+ * The pci-4020 hardware only supports
+ * dma transfers (it only supports the use of pio for draining the last remaining
+ * points from the fifo when a data aquisition operation has completed).
+ */
+static void pio_drain_ai_fifo_32(struct comedi_device * dev)
+{
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int i;
+ unsigned int max_transfer = 100000;
+ uint32_t fifo_data;
+ int write_code =
+ readw(priv(dev)->main_iobase + ADC_WRITE_PNTR_REG) & 0x7fff;
+ int read_code =
+ readw(priv(dev)->main_iobase + ADC_READ_PNTR_REG) & 0x7fff;
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (max_transfer > priv(dev)->ai_count) {
+ max_transfer = priv(dev)->ai_count;
+ }
+ }
+ for (i = 0; read_code != write_code && i < max_transfer;) {
+ fifo_data = readl(priv(dev)->dio_counter_iobase + ADC_FIFO_REG);
+ cfc_write_to_buffer(s, fifo_data & 0xffff);
+ i++;
+ if (i < max_transfer) {
+ cfc_write_to_buffer(s, (fifo_data >> 16) & 0xffff);
+ i++;
+ }
+ read_code =
+ readw(priv(dev)->main_iobase +
+ ADC_READ_PNTR_REG) & 0x7fff;
+ }
+ priv(dev)->ai_count -= i;
+}
+
+// empty fifo
+static void pio_drain_ai_fifo(struct comedi_device * dev)
+{
+ if (board(dev)->layout == LAYOUT_4020) {
+ pio_drain_ai_fifo_32(dev);
+ } else
+ pio_drain_ai_fifo_16(dev);
+}
+
+static void drain_dma_buffers(struct comedi_device * dev, unsigned int channel)
+{
+ struct comedi_async *async = dev->read_subdev->async;
+ uint32_t next_transfer_addr;
+ int j;
+ int num_samples = 0;
+ void *pci_addr_reg;
+
+ if (channel)
+ pci_addr_reg =
+ priv(dev)->plx9080_iobase + PLX_DMA1_PCI_ADDRESS_REG;
+ else
+ pci_addr_reg =
+ priv(dev)->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG;
+
+ // loop until we have read all the full buffers
+ for (j = 0, next_transfer_addr = readl(pci_addr_reg);
+ (next_transfer_addr <
+ priv(dev)->ai_buffer_bus_addr[priv(dev)->ai_dma_index]
+ || next_transfer_addr >=
+ priv(dev)->ai_buffer_bus_addr[priv(dev)->ai_dma_index] +
+ DMA_BUFFER_SIZE) && j < ai_dma_ring_count(board(dev));
+ j++) {
+ // transfer data from dma buffer to comedi buffer
+ num_samples = dma_transfer_size(dev);
+ if (async->cmd.stop_src == TRIG_COUNT) {
+ if (num_samples > priv(dev)->ai_count)
+ num_samples = priv(dev)->ai_count;
+ priv(dev)->ai_count -= num_samples;
+ }
+ cfc_write_array_to_buffer(dev->read_subdev,
+ priv(dev)->ai_buffer[priv(dev)->ai_dma_index],
+ num_samples * sizeof(uint16_t));
+ priv(dev)->ai_dma_index =
+ (priv(dev)->ai_dma_index +
+ 1) % ai_dma_ring_count(board(dev));
+
+ DEBUG_PRINT("next buffer addr 0x%lx\n",
+ (unsigned long)priv(dev)->ai_buffer_bus_addr[priv(dev)->
+ ai_dma_index]);
+ DEBUG_PRINT("pci addr reg 0x%x\n", next_transfer_addr);
+ }
+ /* XXX check for dma ring buffer overrun (use end-of-chain bit to mark last
+ * unused buffer) */
+}
+
+void handle_ai_interrupt(struct comedi_device * dev, unsigned short status,
+ unsigned int plx_status)
+{
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ uint8_t dma1_status;
+ unsigned long flags;
+
+ // check for fifo overrun
+ if (status & ADC_OVERRUN_BIT) {
+ comedi_error(dev, "fifo overrun");
+ async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ }
+ // spin lock makes sure noone else changes plx dma control reg
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ dma1_status = readb(priv(dev)->plx9080_iobase + PLX_DMA1_CS_REG);
+ if (plx_status & ICS_DMA1_A) { // dma chan 1 interrupt
+ writeb((dma1_status & PLX_DMA_EN_BIT) | PLX_CLEAR_DMA_INTR_BIT,
+ priv(dev)->plx9080_iobase + PLX_DMA1_CS_REG);
+ DEBUG_PRINT("dma1 status 0x%x\n", dma1_status);
+
+ if (dma1_status & PLX_DMA_EN_BIT) {
+ drain_dma_buffers(dev, 1);
+ }
+ DEBUG_PRINT(" cleared dma ch1 interrupt\n");
+ }
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ if (status & ADC_DONE_BIT)
+ DEBUG_PRINT("adc done interrupt\n");
+
+ // drain fifo with pio
+ if ((status & ADC_DONE_BIT) ||
+ ((cmd->flags & TRIG_WAKE_EOS) &&
+ (status & ADC_INTR_PENDING_BIT) &&
+ (board(dev)->layout != LAYOUT_4020))) {
+ DEBUG_PRINT("pio fifo drain\n");
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ if (priv(dev)->ai_cmd_running) {
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ pio_drain_ai_fifo(dev);
+ } else
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ }
+ // if we are have all the data, then quit
+ if ((cmd->stop_src == TRIG_COUNT && priv(dev)->ai_count <= 0) ||
+ (cmd->stop_src == TRIG_EXT && (status & ADC_STOP_BIT))) {
+ async->events |= COMEDI_CB_EOA;
+ }
+
+ cfc_handle_events(dev, s);
+}
+
+static inline unsigned int prev_ao_dma_index(struct comedi_device * dev)
+{
+ unsigned int buffer_index;
+
+ if (priv(dev)->ao_dma_index == 0)
+ buffer_index = AO_DMA_RING_COUNT - 1;
+ else
+ buffer_index = priv(dev)->ao_dma_index - 1;
+ return buffer_index;
+}
+
+static int last_ao_dma_load_completed(struct comedi_device * dev)
+{
+ unsigned int buffer_index;
+ unsigned int transfer_address;
+ unsigned short dma_status;
+
+ buffer_index = prev_ao_dma_index(dev);
+ dma_status = readb(priv(dev)->plx9080_iobase + PLX_DMA0_CS_REG);
+ if ((dma_status & PLX_DMA_DONE_BIT) == 0)
+ return 0;
+
+ transfer_address =
+ readl(priv(dev)->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG);
+ if (transfer_address != priv(dev)->ao_buffer_bus_addr[buffer_index])
+ return 0;
+
+ return 1;
+}
+
+static int ao_stopped_by_error(struct comedi_device * dev, const struct comedi_cmd * cmd)
+{
+ if (cmd->stop_src == TRIG_NONE)
+ return 1;
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (priv(dev)->ao_count)
+ return 1;
+ if (last_ao_dma_load_completed(dev) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+static inline int ao_dma_needs_restart(struct comedi_device * dev,
+ unsigned short dma_status)
+{
+ if ((dma_status & PLX_DMA_DONE_BIT) == 0 ||
+ (dma_status & PLX_DMA_EN_BIT) == 0)
+ return 0;
+ if (last_ao_dma_load_completed(dev))
+ return 0;
+
+ return 1;
+}
+
+static void restart_ao_dma(struct comedi_device * dev)
+{
+ unsigned int dma_desc_bits;
+
+ dma_desc_bits =
+ readl(priv(dev)->plx9080_iobase + PLX_DMA0_DESCRIPTOR_REG);
+ dma_desc_bits &= ~PLX_END_OF_CHAIN_BIT;
+ DEBUG_PRINT("restarting ao dma, descriptor reg 0x%x\n", dma_desc_bits);
+ load_first_dma_descriptor(dev, 0, dma_desc_bits);
+
+ dma_start_sync(dev, 0);
+}
+
+static void handle_ao_interrupt(struct comedi_device * dev, unsigned short status,
+ unsigned int plx_status)
+{
+ struct comedi_subdevice *s = dev->write_subdev;
+ struct comedi_async *async;
+ struct comedi_cmd *cmd;
+ uint8_t dma0_status;
+ unsigned long flags;
+
+ /* board might not support ao, in which case write_subdev is NULL */
+ if (s == NULL)
+ return;
+ async = s->async;
+ cmd = &async->cmd;
+
+ // spin lock makes sure noone else changes plx dma control reg
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ dma0_status = readb(priv(dev)->plx9080_iobase + PLX_DMA0_CS_REG);
+ if (plx_status & ICS_DMA0_A) { // dma chan 0 interrupt
+ if ((dma0_status & PLX_DMA_EN_BIT)
+ && !(dma0_status & PLX_DMA_DONE_BIT))
+ writeb(PLX_DMA_EN_BIT | PLX_CLEAR_DMA_INTR_BIT,
+ priv(dev)->plx9080_iobase + PLX_DMA0_CS_REG);
+ else
+ writeb(PLX_CLEAR_DMA_INTR_BIT,
+ priv(dev)->plx9080_iobase + PLX_DMA0_CS_REG);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ DEBUG_PRINT("dma0 status 0x%x\n", dma0_status);
+ if (dma0_status & PLX_DMA_EN_BIT) {
+ load_ao_dma(dev, cmd);
+ /* try to recover from dma end-of-chain event */
+ if (ao_dma_needs_restart(dev, dma0_status))
+ restart_ao_dma(dev);
+ }
+ DEBUG_PRINT(" cleared dma ch0 interrupt\n");
+ } else
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ if ((status & DAC_DONE_BIT)) {
+ async->events |= COMEDI_CB_EOA;
+ if (ao_stopped_by_error(dev, cmd))
+ async->events |= COMEDI_CB_ERROR;
+ DEBUG_PRINT("plx dma0 desc reg 0x%x\n",
+ readl(priv(dev)->plx9080_iobase +
+ PLX_DMA0_DESCRIPTOR_REG));
+ DEBUG_PRINT("plx dma0 address reg 0x%x\n",
+ readl(priv(dev)->plx9080_iobase +
+ PLX_DMA0_PCI_ADDRESS_REG));
+ }
+ cfc_handle_events(dev, s);
+}
+
+static irqreturn_t handle_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ unsigned short status;
+ uint32_t plx_status;
+ uint32_t plx_bits;
+
+ plx_status = readl(priv(dev)->plx9080_iobase + PLX_INTRCS_REG);
+ status = readw(priv(dev)->main_iobase + HW_STATUS_REG);
+
+ DEBUG_PRINT("cb_pcidas64: hw status 0x%x ", status);
+ DEBUG_PRINT("plx status 0x%x\n", plx_status);
+
+ /* an interrupt before all the postconfig stuff gets done could
+ * cause a NULL dereference if we continue through the
+ * interrupt handler */
+ if (dev->attached == 0) {
+ DEBUG_PRINT("cb_pcidas64: premature interrupt, ignoring",
+ status);
+ return IRQ_HANDLED;
+ }
+ handle_ai_interrupt(dev, status, plx_status);
+ handle_ao_interrupt(dev, status, plx_status);
+
+ // clear possible plx9080 interrupt sources
+ if (plx_status & ICS_LDIA) { // clear local doorbell interrupt
+ plx_bits = readl(priv(dev)->plx9080_iobase + PLX_DBR_OUT_REG);
+ writel(plx_bits, priv(dev)->plx9080_iobase + PLX_DBR_OUT_REG);
+ DEBUG_PRINT(" cleared local doorbell bits 0x%x\n", plx_bits);
+ }
+
+ DEBUG_PRINT("exiting handler\n");
+
+ return IRQ_HANDLED;
+}
+
+void abort_dma(struct comedi_device * dev, unsigned int channel)
+{
+ unsigned long flags;
+
+ // spinlock for plx dma control/status reg
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+
+ plx9080_abort_dma(priv(dev)->plx9080_iobase, channel);
+
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+static int ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ if (priv(dev)->ai_cmd_running == 0) {
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ return 0;
+ }
+ priv(dev)->ai_cmd_running = 0;
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ disable_ai_pacing(dev);
+
+ abort_dma(dev, 1);
+
+ DEBUG_PRINT("ai canceled\n");
+ return 0;
+}
+
+static int ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+ int range = CR_RANGE(insn->chanspec);
+
+ // do some initializing
+ writew(0, priv(dev)->main_iobase + DAC_CONTROL0_REG);
+
+ // set range
+ set_dac_range_bits(dev, &priv(dev)->dac_control1_bits, chan, range);
+ writew(priv(dev)->dac_control1_bits,
+ priv(dev)->main_iobase + DAC_CONTROL1_REG);
+
+ // write to channel
+ if (board(dev)->layout == LAYOUT_4020) {
+ writew(data[0] & 0xff,
+ priv(dev)->main_iobase + dac_lsb_4020_reg(chan));
+ writew((data[0] >> 8) & 0xf,
+ priv(dev)->main_iobase + dac_msb_4020_reg(chan));
+ } else {
+ writew(data[0], priv(dev)->main_iobase + dac_convert_reg(chan));
+ }
+
+ // remember output value
+ priv(dev)->ao_value[chan] = data[0];
+
+ return 1;
+}
+
+static int ao_readback_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = priv(dev)->ao_value[CR_CHAN(insn->chanspec)];
+
+ return 1;
+}
+
+static void set_dac_control0_reg(struct comedi_device * dev, const struct comedi_cmd * cmd)
+{
+ unsigned int bits = DAC_ENABLE_BIT | WAVEFORM_GATE_LEVEL_BIT |
+ WAVEFORM_GATE_ENABLE_BIT | WAVEFORM_GATE_SELECT_BIT;
+
+ if (cmd->start_src == TRIG_EXT) {
+ bits |= WAVEFORM_TRIG_EXT_BITS;
+ if (cmd->start_arg & CR_INVERT)
+ bits |= WAVEFORM_TRIG_FALLING_BIT;
+ } else {
+ bits |= WAVEFORM_TRIG_SOFT_BITS;
+ }
+ if (cmd->scan_begin_src == TRIG_EXT) {
+ bits |= DAC_EXT_UPDATE_ENABLE_BIT;
+ if (cmd->scan_begin_arg & CR_INVERT)
+ bits |= DAC_EXT_UPDATE_FALLING_BIT;
+ }
+ writew(bits, priv(dev)->main_iobase + DAC_CONTROL0_REG);
+}
+
+static void set_dac_control1_reg(struct comedi_device * dev, const struct comedi_cmd * cmd)
+{
+ int i;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ int channel, range;
+
+ channel = CR_CHAN(cmd->chanlist[i]);
+ range = CR_RANGE(cmd->chanlist[i]);
+ set_dac_range_bits(dev, &priv(dev)->dac_control1_bits, channel,
+ range);
+ }
+ priv(dev)->dac_control1_bits |= DAC_SW_GATE_BIT;
+ writew(priv(dev)->dac_control1_bits,
+ priv(dev)->main_iobase + DAC_CONTROL1_REG);
+}
+
+static void set_dac_select_reg(struct comedi_device * dev, const struct comedi_cmd * cmd)
+{
+ uint16_t bits;
+ unsigned int first_channel, last_channel;
+
+ first_channel = CR_CHAN(cmd->chanlist[0]);
+ last_channel = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]);
+ if (last_channel < first_channel)
+ comedi_error(dev, "bug! last ao channel < first ao channel");
+
+ bits = (first_channel & 0x7) | (last_channel & 0x7) << 3;
+
+ writew(bits, priv(dev)->main_iobase + DAC_SELECT_REG);
+}
+
+static void set_dac_interval_regs(struct comedi_device * dev, const struct comedi_cmd * cmd)
+{
+ unsigned int divisor;
+
+ if (cmd->scan_begin_src != TRIG_TIMER)
+ return;
+
+ divisor = get_ao_divisor(cmd->scan_begin_arg, cmd->flags);
+ if (divisor > max_counter_value) {
+ comedi_error(dev, "bug! ao divisor too big");
+ divisor = max_counter_value;
+ }
+ writew(divisor & 0xffff,
+ priv(dev)->main_iobase + DAC_SAMPLE_INTERVAL_LOWER_REG);
+ writew((divisor >> 16) & 0xff,
+ priv(dev)->main_iobase + DAC_SAMPLE_INTERVAL_UPPER_REG);
+}
+
+static unsigned int load_ao_dma_buffer(struct comedi_device * dev,
+ const struct comedi_cmd * cmd)
+{
+ unsigned int num_bytes, buffer_index, prev_buffer_index;
+ unsigned int next_bits;
+
+ buffer_index = priv(dev)->ao_dma_index;
+ prev_buffer_index = prev_ao_dma_index(dev);
+
+ DEBUG_PRINT("attempting to load ao buffer %i (0x%x)\n", buffer_index,
+ priv(dev)->ao_buffer_bus_addr[buffer_index]);
+
+ num_bytes = comedi_buf_read_n_available(dev->write_subdev->async);
+ if (num_bytes > DMA_BUFFER_SIZE)
+ num_bytes = DMA_BUFFER_SIZE;
+ if (cmd->stop_src == TRIG_COUNT && num_bytes > priv(dev)->ao_count)
+ num_bytes = priv(dev)->ao_count;
+ num_bytes -= num_bytes % bytes_in_sample;
+
+ if (num_bytes == 0)
+ return 0;
+
+ DEBUG_PRINT("loading %i bytes\n", num_bytes);
+
+ num_bytes = cfc_read_array_from_buffer(dev->write_subdev,
+ priv(dev)->ao_buffer[buffer_index], num_bytes);
+ priv(dev)->ao_dma_desc[buffer_index].transfer_size =
+ cpu_to_le32(num_bytes);
+ /* set end of chain bit so we catch underruns */
+ next_bits = le32_to_cpu(priv(dev)->ao_dma_desc[buffer_index].next);
+ next_bits |= PLX_END_OF_CHAIN_BIT;
+ priv(dev)->ao_dma_desc[buffer_index].next = cpu_to_le32(next_bits);
+ /* clear end of chain bit on previous buffer now that we have set it
+ * for the last buffer */
+ next_bits = le32_to_cpu(priv(dev)->ao_dma_desc[prev_buffer_index].next);
+ next_bits &= ~PLX_END_OF_CHAIN_BIT;
+ priv(dev)->ao_dma_desc[prev_buffer_index].next = cpu_to_le32(next_bits);
+
+ priv(dev)->ao_dma_index = (buffer_index + 1) % AO_DMA_RING_COUNT;
+ priv(dev)->ao_count -= num_bytes;
+
+ return num_bytes;
+}
+
+static void load_ao_dma(struct comedi_device * dev, const struct comedi_cmd * cmd)
+{
+ unsigned int num_bytes;
+ unsigned int next_transfer_addr;
+ void *pci_addr_reg =
+ priv(dev)->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG;
+ unsigned int buffer_index;
+
+ do {
+ buffer_index = priv(dev)->ao_dma_index;
+ /* don't overwrite data that hasn't been transferred yet */
+ next_transfer_addr = readl(pci_addr_reg);
+ if (next_transfer_addr >=
+ priv(dev)->ao_buffer_bus_addr[buffer_index]
+ && next_transfer_addr <
+ priv(dev)->ao_buffer_bus_addr[buffer_index] +
+ DMA_BUFFER_SIZE)
+ return;
+ num_bytes = load_ao_dma_buffer(dev, cmd);
+ } while (num_bytes >= DMA_BUFFER_SIZE);
+}
+
+static int prep_ao_dma(struct comedi_device * dev, const struct comedi_cmd * cmd)
+{
+ unsigned int num_bytes;
+ int i;
+
+ /* clear queue pointer too, since external queue has
+ * weird interactions with ao fifo */
+ writew(0, priv(dev)->main_iobase + ADC_QUEUE_CLEAR_REG);
+ writew(0, priv(dev)->main_iobase + DAC_BUFFER_CLEAR_REG);
+
+ num_bytes = (DAC_FIFO_SIZE / 2) * bytes_in_sample;
+ if (cmd->stop_src == TRIG_COUNT &&
+ num_bytes / bytes_in_sample > priv(dev)->ao_count)
+ num_bytes = priv(dev)->ao_count * bytes_in_sample;
+ num_bytes = cfc_read_array_from_buffer(dev->write_subdev,
+ priv(dev)->ao_bounce_buffer, num_bytes);
+ for (i = 0; i < num_bytes / bytes_in_sample; i++) {
+ writew(priv(dev)->ao_bounce_buffer[i],
+ priv(dev)->main_iobase + DAC_FIFO_REG);
+ }
+ priv(dev)->ao_count -= num_bytes / bytes_in_sample;
+ if (cmd->stop_src == TRIG_COUNT && priv(dev)->ao_count == 0)
+ return 0;
+ num_bytes = load_ao_dma_buffer(dev, cmd);
+ if (num_bytes == 0)
+ return -1;
+ if (num_bytes >= DMA_BUFFER_SIZE) ;
+ load_ao_dma(dev, cmd);
+
+ dma_start_sync(dev, 0);
+
+ return 0;
+}
+
+static inline int external_ai_queue_in_use(struct comedi_device * dev)
+{
+ if (dev->read_subdev->busy)
+ return 0;
+ if (board(dev)->layout == LAYOUT_4020)
+ return 0;
+ else if (use_internal_queue_6xxx(&dev->read_subdev->async->cmd))
+ return 0;
+ return 1;
+}
+
+static int ao_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (external_ai_queue_in_use(dev)) {
+ warn_external_queue(dev);
+ return -EBUSY;
+ }
+ /* disable analog output system during setup */
+ writew(0x0, priv(dev)->main_iobase + DAC_CONTROL0_REG);
+
+ priv(dev)->ao_dma_index = 0;
+ priv(dev)->ao_count = cmd->stop_arg * cmd->chanlist_len;
+
+ set_dac_select_reg(dev, cmd);
+ set_dac_interval_regs(dev, cmd);
+ load_first_dma_descriptor(dev, 0, priv(dev)->ao_dma_desc_bus_addr |
+ PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT);
+
+ set_dac_control1_reg(dev, cmd);
+ s->async->inttrig = ao_inttrig;
+
+ return 0;
+}
+
+static int ao_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trig_num)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int retval;
+
+ if (trig_num != 0)
+ return -EINVAL;
+
+ retval = prep_ao_dma(dev, cmd);
+ if (retval < 0)
+ return -EPIPE;
+
+ set_dac_control0_reg(dev, cmd);
+
+ if (cmd->start_src == TRIG_INT)
+ writew(0, priv(dev)->main_iobase + DAC_START_REG);
+
+ s->async->inttrig = NULL;
+
+ return 0;
+}
+
+static int ao_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+ unsigned int tmp_arg;
+ int i;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_INT | TRIG_EXT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ // uniqueness check
+ if (cmd->start_src != TRIG_INT && cmd->start_src != TRIG_EXT)
+ err++;
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_EXT)
+ err++;
+
+ // compatibility check
+ if (cmd->convert_src == TRIG_EXT && cmd->scan_begin_src == TRIG_TIMER)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT &&
+ cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_EXT)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->scan_begin_arg < board(dev)->ao_scan_speed) {
+ cmd->scan_begin_arg = board(dev)->ao_scan_speed;
+ err++;
+ }
+ if (get_ao_divisor(cmd->scan_begin_arg,
+ cmd->flags) > max_counter_value) {
+ cmd->scan_begin_arg =
+ (max_counter_value + 2) * TIMER_BASE;
+ err++;
+ }
+ }
+
+ if (!cmd->chanlist_len) {
+ cmd->chanlist_len = 1;
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp_arg = cmd->scan_begin_arg;
+ cmd->scan_begin_arg =
+ get_divisor(cmd->scan_begin_arg,
+ cmd->flags) * TIMER_BASE;
+ if (tmp_arg != cmd->scan_begin_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ if (cmd->chanlist) {
+ unsigned int first_channel = CR_CHAN(cmd->chanlist[0]);
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ if (CR_CHAN(cmd->chanlist[i]) != first_channel + i) {
+ comedi_error(dev,
+ "chanlist must use consecutive channels");
+ err++;
+ break;
+ }
+ }
+ }
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int ao_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ writew(0x0, priv(dev)->main_iobase + DAC_CONTROL0_REG);
+ abort_dma(dev, 0);
+ return 0;
+}
+
+static int dio_callback(int dir, int port, int data, unsigned long iobase)
+{
+ if (dir) {
+ writeb(data, (void *)(iobase + port));
+ DEBUG_PRINT("wrote 0x%x to port %i\n", data, port);
+ return 0;
+ } else {
+ return readb((void *)(iobase + port));
+ }
+}
+
+static int dio_callback_4020(int dir, int port, int data, unsigned long iobase)
+{
+ if (dir) {
+ writew(data, (void *)(iobase + 2 * port));
+ return 0;
+ } else {
+ return readw((void *)(iobase + 2 * port));
+ }
+}
+
+static int di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int bits;
+
+ bits = readb(priv(dev)->dio_counter_iobase + DI_REG);
+ bits &= 0xf;
+ data[1] = bits;
+ data[0] = 0;
+
+ return 2;
+}
+
+static int do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] &= 0xf;
+ // zero bits we are going to change
+ s->state &= ~data[0];
+ // set new bits
+ s->state |= data[0] & data[1];
+
+ writeb(s->state, priv(dev)->dio_counter_iobase + DO_REG);
+
+ data[1] = s->state;
+
+ return 2;
+}
+
+static int dio_60xx_config_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int mask;
+
+ mask = 1 << CR_CHAN(insn->chanspec);
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~mask;
+ break;
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= mask;
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return 2;
+ default:
+ return -EINVAL;
+ }
+
+ writeb(s->io_bits,
+ priv(dev)->dio_counter_iobase + DIO_DIRECTION_60XX_REG);
+
+ return 1;
+}
+
+static int dio_60xx_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ writeb(s->state,
+ priv(dev)->dio_counter_iobase + DIO_DATA_60XX_REG);
+ }
+
+ data[1] = readb(priv(dev)->dio_counter_iobase + DIO_DATA_60XX_REG);
+
+ return 2;
+}
+
+static void caldac_write(struct comedi_device * dev, unsigned int channel,
+ unsigned int value)
+{
+ priv(dev)->caldac_state[channel] = value;
+
+ switch (board(dev)->layout) {
+ case LAYOUT_60XX:
+ case LAYOUT_64XX:
+ caldac_8800_write(dev, channel, value);
+ break;
+ case LAYOUT_4020:
+ caldac_i2c_write(dev, channel, value);
+ break;
+ default:
+ break;
+ }
+}
+
+static int calib_write_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int channel = CR_CHAN(insn->chanspec);
+
+ /* return immediately if setting hasn't changed, since
+ * programming these things is slow */
+ if (priv(dev)->caldac_state[channel] == data[0])
+ return 1;
+
+ caldac_write(dev, channel, data[0]);
+
+ return 1;
+}
+
+static int calib_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int channel = CR_CHAN(insn->chanspec);
+
+ data[0] = priv(dev)->caldac_state[channel];
+
+ return 1;
+}
+
+static void ad8402_write(struct comedi_device * dev, unsigned int channel,
+ unsigned int value)
+{
+ static const int bitstream_length = 10;
+ unsigned int bit, register_bits;
+ unsigned int bitstream = ((channel & 0x3) << 8) | (value & 0xff);
+ static const int ad8402_comedi_udelay = 1;
+
+ priv(dev)->ad8402_state[channel] = value;
+
+ register_bits = SELECT_8402_64XX_BIT;
+ comedi_udelay(ad8402_comedi_udelay);
+ writew(register_bits, priv(dev)->main_iobase + CALIBRATION_REG);
+
+ for (bit = 1 << (bitstream_length - 1); bit; bit >>= 1) {
+ if (bitstream & bit)
+ register_bits |= SERIAL_DATA_IN_BIT;
+ else
+ register_bits &= ~SERIAL_DATA_IN_BIT;
+ comedi_udelay(ad8402_comedi_udelay);
+ writew(register_bits, priv(dev)->main_iobase + CALIBRATION_REG);
+ comedi_udelay(ad8402_comedi_udelay);
+ writew(register_bits | SERIAL_CLOCK_BIT,
+ priv(dev)->main_iobase + CALIBRATION_REG);
+ }
+
+ comedi_udelay(ad8402_comedi_udelay);
+ writew(0, priv(dev)->main_iobase + CALIBRATION_REG);
+}
+
+/* for pci-das6402/16, channel 0 is analog input gain and channel 1 is offset */
+static int ad8402_write_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int channel = CR_CHAN(insn->chanspec);
+
+ /* return immediately if setting hasn't changed, since
+ * programming these things is slow */
+ if (priv(dev)->ad8402_state[channel] == data[0])
+ return 1;
+
+ priv(dev)->ad8402_state[channel] = data[0];
+
+ ad8402_write(dev, channel, data[0]);
+
+ return 1;
+}
+
+static int ad8402_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int channel = CR_CHAN(insn->chanspec);
+
+ data[0] = priv(dev)->ad8402_state[channel];
+
+ return 1;
+}
+
+static uint16_t read_eeprom(struct comedi_device * dev, uint8_t address)
+{
+ static const int bitstream_length = 11;
+ static const int read_command = 0x6;
+ unsigned int bitstream = (read_command << 8) | address;
+ unsigned int bit;
+ void *const plx_control_addr =
+ priv(dev)->plx9080_iobase + PLX_CONTROL_REG;
+ uint16_t value;
+ static const int value_length = 16;
+ static const int eeprom_comedi_udelay = 1;
+
+ comedi_udelay(eeprom_comedi_udelay);
+ priv(dev)->plx_control_bits &= ~CTL_EE_CLK & ~CTL_EE_CS;
+ // make sure we don't send anything to the i2c bus on 4020
+ priv(dev)->plx_control_bits |= CTL_USERO;
+ writel(priv(dev)->plx_control_bits, plx_control_addr);
+ // activate serial eeprom
+ comedi_udelay(eeprom_comedi_udelay);
+ priv(dev)->plx_control_bits |= CTL_EE_CS;
+ writel(priv(dev)->plx_control_bits, plx_control_addr);
+
+ // write read command and desired memory address
+ for (bit = 1 << (bitstream_length - 1); bit; bit >>= 1) {
+ // set bit to be written
+ comedi_udelay(eeprom_comedi_udelay);
+ if (bitstream & bit)
+ priv(dev)->plx_control_bits |= CTL_EE_W;
+ else
+ priv(dev)->plx_control_bits &= ~CTL_EE_W;
+ writel(priv(dev)->plx_control_bits, plx_control_addr);
+ // clock in bit
+ comedi_udelay(eeprom_comedi_udelay);
+ priv(dev)->plx_control_bits |= CTL_EE_CLK;
+ writel(priv(dev)->plx_control_bits, plx_control_addr);
+ comedi_udelay(eeprom_comedi_udelay);
+ priv(dev)->plx_control_bits &= ~CTL_EE_CLK;
+ writel(priv(dev)->plx_control_bits, plx_control_addr);
+ }
+ // read back value from eeprom memory location
+ value = 0;
+ for (bit = 1 << (value_length - 1); bit; bit >>= 1) {
+ // clock out bit
+ comedi_udelay(eeprom_comedi_udelay);
+ priv(dev)->plx_control_bits |= CTL_EE_CLK;
+ writel(priv(dev)->plx_control_bits, plx_control_addr);
+ comedi_udelay(eeprom_comedi_udelay);
+ priv(dev)->plx_control_bits &= ~CTL_EE_CLK;
+ writel(priv(dev)->plx_control_bits, plx_control_addr);
+ comedi_udelay(eeprom_comedi_udelay);
+ if (readl(plx_control_addr) & CTL_EE_R)
+ value |= bit;
+ }
+
+ // deactivate eeprom serial input
+ comedi_udelay(eeprom_comedi_udelay);
+ priv(dev)->plx_control_bits &= ~CTL_EE_CS;
+ writel(priv(dev)->plx_control_bits, plx_control_addr);
+
+ return value;
+}
+
+static int eeprom_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = read_eeprom(dev, CR_CHAN(insn->chanspec));
+
+ return 1;
+}
+
+/* utility function that rounds desired timing to an achievable time, and
+ * sets cmd members appropriately.
+ * adc paces conversions from master clock by dividing by (x + 3) where x is 24 bit number
+ */
+static void check_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd)
+{
+ unsigned int convert_divisor = 0, scan_divisor;
+ static const int min_convert_divisor = 3;
+ static const int max_convert_divisor =
+ max_counter_value + min_convert_divisor;
+ static const int min_scan_divisor_4020 = 2;
+ unsigned long long max_scan_divisor, min_scan_divisor;
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (board(dev)->layout == LAYOUT_4020) {
+ cmd->convert_arg = 0;
+ } else {
+ convert_divisor =
+ get_divisor(cmd->convert_arg, cmd->flags);
+ if (convert_divisor > max_convert_divisor)
+ convert_divisor = max_convert_divisor;
+ if (convert_divisor < min_convert_divisor)
+ convert_divisor = min_convert_divisor;
+ cmd->convert_arg = convert_divisor * TIMER_BASE;
+ }
+ } else if (cmd->convert_src == TRIG_NOW)
+ cmd->convert_arg = 0;
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ scan_divisor = get_divisor(cmd->scan_begin_arg, cmd->flags);
+ if (cmd->convert_src == TRIG_TIMER) {
+ // XXX check for integer overflows
+ min_scan_divisor = convert_divisor * cmd->chanlist_len;
+ max_scan_divisor =
+ (convert_divisor * cmd->chanlist_len - 1) +
+ max_counter_value;
+ } else {
+ min_scan_divisor = min_scan_divisor_4020;
+ max_scan_divisor = max_counter_value + min_scan_divisor;
+ }
+ if (scan_divisor > max_scan_divisor)
+ scan_divisor = max_scan_divisor;
+ if (scan_divisor < min_scan_divisor)
+ scan_divisor = min_scan_divisor;
+ cmd->scan_begin_arg = scan_divisor * TIMER_BASE;
+ }
+
+ return;
+}
+
+/* Gets nearest achievable timing given master clock speed, does not
+ * take into account possible minimum/maximum divisor values. Used
+ * by other timing checking functions. */
+static unsigned int get_divisor(unsigned int ns, unsigned int flags)
+{
+ unsigned int divisor;
+
+ switch (flags & TRIG_ROUND_MASK) {
+ case TRIG_ROUND_UP:
+ divisor = (ns + TIMER_BASE - 1) / TIMER_BASE;
+ break;
+ case TRIG_ROUND_DOWN:
+ divisor = ns / TIMER_BASE;
+ break;
+ case TRIG_ROUND_NEAREST:
+ default:
+ divisor = (ns + TIMER_BASE / 2) / TIMER_BASE;
+ break;
+ }
+ return divisor;
+}
+
+static unsigned int get_ao_divisor(unsigned int ns, unsigned int flags)
+{
+ return get_divisor(ns, flags) - 2;
+}
+
+// adjusts the size of hardware fifo (which determines block size for dma xfers)
+static int set_ai_fifo_size(struct comedi_device * dev, unsigned int num_samples)
+{
+ unsigned int num_fifo_entries;
+ int retval;
+ const struct hw_fifo_info *const fifo = board(dev)->ai_fifo;
+
+ num_fifo_entries = num_samples / fifo->sample_packing_ratio;
+
+ retval = set_ai_fifo_segment_length(dev,
+ num_fifo_entries / fifo->num_segments);
+ if (retval < 0)
+ return retval;
+
+ num_samples = retval * fifo->num_segments * fifo->sample_packing_ratio;
+
+ DEBUG_PRINT("set hardware fifo size to %i\n", num_samples);
+
+ return num_samples;
+}
+
+// query length of fifo
+static unsigned int ai_fifo_size(struct comedi_device * dev)
+{
+ return priv(dev)->ai_fifo_segment_length *
+ board(dev)->ai_fifo->num_segments *
+ board(dev)->ai_fifo->sample_packing_ratio;
+}
+
+static int set_ai_fifo_segment_length(struct comedi_device * dev,
+ unsigned int num_entries)
+{
+ static const int increment_size = 0x100;
+ const struct hw_fifo_info *const fifo = board(dev)->ai_fifo;
+ unsigned int num_increments;
+ uint16_t bits;
+
+ if (num_entries < increment_size)
+ num_entries = increment_size;
+ if (num_entries > fifo->max_segment_length)
+ num_entries = fifo->max_segment_length;
+
+ // 1 == 256 entries, 2 == 512 entries, etc
+ num_increments = (num_entries + increment_size / 2) / increment_size;
+
+ bits = (~(num_increments - 1)) & fifo->fifo_size_reg_mask;
+ priv(dev)->fifo_size_bits &= ~fifo->fifo_size_reg_mask;
+ priv(dev)->fifo_size_bits |= bits;
+ writew(priv(dev)->fifo_size_bits,
+ priv(dev)->main_iobase + FIFO_SIZE_REG);
+
+ priv(dev)->ai_fifo_segment_length = num_increments * increment_size;
+
+ DEBUG_PRINT("set hardware fifo segment length to %i\n",
+ priv(dev)->ai_fifo_segment_length);
+
+ return priv(dev)->ai_fifo_segment_length;
+}
+
+/* pci-6025 8800 caldac:
+ * address 0 == dac channel 0 offset
+ * address 1 == dac channel 0 gain
+ * address 2 == dac channel 1 offset
+ * address 3 == dac channel 1 gain
+ * address 4 == fine adc offset
+ * address 5 == coarse adc offset
+ * address 6 == coarse adc gain
+ * address 7 == fine adc gain
+ */
+/* pci-6402/16 uses all 8 channels for dac:
+ * address 0 == dac channel 0 fine gain
+ * address 1 == dac channel 0 coarse gain
+ * address 2 == dac channel 0 coarse offset
+ * address 3 == dac channel 1 coarse offset
+ * address 4 == dac channel 1 fine gain
+ * address 5 == dac channel 1 coarse gain
+ * address 6 == dac channel 0 fine offset
+ * address 7 == dac channel 1 fine offset
+*/
+
+static int caldac_8800_write(struct comedi_device * dev, unsigned int address,
+ uint8_t value)
+{
+ static const int num_caldac_channels = 8;
+ static const int bitstream_length = 11;
+ unsigned int bitstream = ((address & 0x7) << 8) | value;
+ unsigned int bit, register_bits;
+ static const int caldac_8800_udelay = 1;
+
+ if (address >= num_caldac_channels) {
+ comedi_error(dev, "illegal caldac channel");
+ return -1;
+ }
+ for (bit = 1 << (bitstream_length - 1); bit; bit >>= 1) {
+ register_bits = 0;
+ if (bitstream & bit)
+ register_bits |= SERIAL_DATA_IN_BIT;
+ comedi_udelay(caldac_8800_udelay);
+ writew(register_bits, priv(dev)->main_iobase + CALIBRATION_REG);
+ register_bits |= SERIAL_CLOCK_BIT;
+ comedi_udelay(caldac_8800_udelay);
+ writew(register_bits, priv(dev)->main_iobase + CALIBRATION_REG);
+ }
+ comedi_udelay(caldac_8800_udelay);
+ writew(SELECT_8800_BIT, priv(dev)->main_iobase + CALIBRATION_REG);
+ comedi_udelay(caldac_8800_udelay);
+ writew(0, priv(dev)->main_iobase + CALIBRATION_REG);
+ comedi_udelay(caldac_8800_udelay);
+ return 0;
+}
+
+// 4020 caldacs
+static int caldac_i2c_write(struct comedi_device * dev, unsigned int caldac_channel,
+ unsigned int value)
+{
+ uint8_t serial_bytes[3];
+ uint8_t i2c_addr;
+ enum pointer_bits {
+ // manual has gain and offset bits switched
+ OFFSET_0_2 = 0x1,
+ GAIN_0_2 = 0x2,
+ OFFSET_1_3 = 0x4,
+ GAIN_1_3 = 0x8,
+ };
+ enum data_bits {
+ NOT_CLEAR_REGISTERS = 0x20,
+ };
+
+ switch (caldac_channel) {
+ case 0: // chan 0 offset
+ i2c_addr = CALDAC0_I2C_ADDR;
+ serial_bytes[0] = OFFSET_0_2;
+ break;
+ case 1: // chan 1 offset
+ i2c_addr = CALDAC0_I2C_ADDR;
+ serial_bytes[0] = OFFSET_1_3;
+ break;
+ case 2: // chan 2 offset
+ i2c_addr = CALDAC1_I2C_ADDR;
+ serial_bytes[0] = OFFSET_0_2;
+ break;
+ case 3: // chan 3 offset
+ i2c_addr = CALDAC1_I2C_ADDR;
+ serial_bytes[0] = OFFSET_1_3;
+ break;
+ case 4: // chan 0 gain
+ i2c_addr = CALDAC0_I2C_ADDR;
+ serial_bytes[0] = GAIN_0_2;
+ break;
+ case 5: // chan 1 gain
+ i2c_addr = CALDAC0_I2C_ADDR;
+ serial_bytes[0] = GAIN_1_3;
+ break;
+ case 6: // chan 2 gain
+ i2c_addr = CALDAC1_I2C_ADDR;
+ serial_bytes[0] = GAIN_0_2;
+ break;
+ case 7: // chan 3 gain
+ i2c_addr = CALDAC1_I2C_ADDR;
+ serial_bytes[0] = GAIN_1_3;
+ break;
+ default:
+ comedi_error(dev, "invalid caldac channel\n");
+ return -1;
+ break;
+ }
+ serial_bytes[1] = NOT_CLEAR_REGISTERS | ((value >> 8) & 0xf);
+ serial_bytes[2] = value & 0xff;
+ i2c_write(dev, i2c_addr, serial_bytes, 3);
+ return 0;
+}
+
+// Their i2c requires a huge delay on setting clock or data high for some reason
+static const int i2c_high_comedi_udelay = 1000;
+static const int i2c_low_comedi_udelay = 10;
+
+// set i2c data line high or low
+static void i2c_set_sda(struct comedi_device * dev, int state)
+{
+ static const int data_bit = CTL_EE_W;
+ void *plx_control_addr = priv(dev)->plx9080_iobase + PLX_CONTROL_REG;
+
+ if (state) {
+ // set data line high
+ priv(dev)->plx_control_bits &= ~data_bit;
+ writel(priv(dev)->plx_control_bits, plx_control_addr);
+ comedi_udelay(i2c_high_comedi_udelay);
+ } else // set data line low
+ {
+ priv(dev)->plx_control_bits |= data_bit;
+ writel(priv(dev)->plx_control_bits, plx_control_addr);
+ comedi_udelay(i2c_low_comedi_udelay);
+ }
+}
+
+// set i2c clock line high or low
+static void i2c_set_scl(struct comedi_device * dev, int state)
+{
+ static const int clock_bit = CTL_USERO;
+ void *plx_control_addr = priv(dev)->plx9080_iobase + PLX_CONTROL_REG;
+
+ if (state) {
+ // set clock line high
+ priv(dev)->plx_control_bits &= ~clock_bit;
+ writel(priv(dev)->plx_control_bits, plx_control_addr);
+ comedi_udelay(i2c_high_comedi_udelay);
+ } else // set clock line low
+ {
+ priv(dev)->plx_control_bits |= clock_bit;
+ writel(priv(dev)->plx_control_bits, plx_control_addr);
+ comedi_udelay(i2c_low_comedi_udelay);
+ }
+}
+
+static void i2c_write_byte(struct comedi_device * dev, uint8_t byte)
+{
+ uint8_t bit;
+ unsigned int num_bits = 8;
+
+ DEBUG_PRINT("writing to i2c byte 0x%x\n", byte);
+
+ for (bit = 1 << (num_bits - 1); bit; bit >>= 1) {
+ i2c_set_scl(dev, 0);
+ if ((byte & bit))
+ i2c_set_sda(dev, 1);
+ else
+ i2c_set_sda(dev, 0);
+ i2c_set_scl(dev, 1);
+ }
+}
+
+// we can't really read the lines, so fake it
+static int i2c_read_ack(struct comedi_device * dev)
+{
+ i2c_set_scl(dev, 0);
+ i2c_set_sda(dev, 1);
+ i2c_set_scl(dev, 1);
+
+ return 0; // return fake acknowledge bit
+}
+
+// send start bit
+static void i2c_start(struct comedi_device * dev)
+{
+ i2c_set_scl(dev, 1);
+ i2c_set_sda(dev, 1);
+ i2c_set_sda(dev, 0);
+}
+
+// send stop bit
+static void i2c_stop(struct comedi_device * dev)
+{
+ i2c_set_scl(dev, 0);
+ i2c_set_sda(dev, 0);
+ i2c_set_scl(dev, 1);
+ i2c_set_sda(dev, 1);
+}
+
+static void i2c_write(struct comedi_device * dev, unsigned int address,
+ const uint8_t * data, unsigned int length)
+{
+ unsigned int i;
+ uint8_t bitstream;
+ static const int read_bit = 0x1;
+
+//XXX need mutex to prevent simultaneous attempts to access eeprom and i2c bus
+
+ // make sure we dont send anything to eeprom
+ priv(dev)->plx_control_bits &= ~CTL_EE_CS;
+
+ i2c_stop(dev);
+ i2c_start(dev);
+
+ // send address and write bit
+ bitstream = (address << 1) & ~read_bit;
+ i2c_write_byte(dev, bitstream);
+
+ // get acknowledge
+ if (i2c_read_ack(dev) != 0) {
+ comedi_error(dev, "i2c write failed: no acknowledge");
+ i2c_stop(dev);
+ return;
+ }
+ // write data bytes
+ for (i = 0; i < length; i++) {
+ i2c_write_byte(dev, data[i]);
+ if (i2c_read_ack(dev) != 0) {
+ comedi_error(dev, "i2c write failed: no acknowledge");
+ i2c_stop(dev);
+ return;
+ }
+ }
+ i2c_stop(dev);
+}
diff --git a/drivers/staging/comedi/drivers/cb_pcidda.c b/drivers/staging/comedi/drivers/cb_pcidda.c
new file mode 100644
index 000000000000..ed0a5eb3dd91
--- /dev/null
+++ b/drivers/staging/comedi/drivers/cb_pcidda.c
@@ -0,0 +1,841 @@
+/*
+ comedi/drivers/cb_pcidda.c
+ This intends to be a driver for the ComputerBoards / MeasurementComputing
+ PCI-DDA series.
+
+ Copyright (C) 2001 Ivan Martinez <ivanmr@altavista.com>
+ Copyright (C) 2001 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: cb_pcidda
+Description: MeasurementComputing PCI-DDA series
+Author: Ivan Martinez <ivanmr@altavista.com>, Frank Mori Hess <fmhess@users.sourceforge.net>
+Status: Supports 08/16, 04/16, 02/16, 08/12, 04/12, and 02/12
+Devices: [Measurement Computing] PCI-DDA08/12 (cb_pcidda), PCI-DDA04/12,
+ PCI-DDA02/12, PCI-DDA08/16, PCI-DDA04/16, PCI-DDA02/16
+
+Configuration options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first available PCI
+ device will be used.
+
+Only simple analog output writing is supported.
+
+So far it has only been tested with:
+ - PCI-DDA08/12
+Please report success/failure with other different cards to
+<comedi@comedi.org>.
+*/
+
+#include "../comedidev.h"
+
+#include "comedi_pci.h"
+#include "8255.h"
+
+#define PCI_VENDOR_ID_CB 0x1307 // PCI vendor number of ComputerBoards
+#define N_BOARDS 10 // Number of boards in cb_pcidda_boards
+#define EEPROM_SIZE 128 // number of entries in eeprom
+#define MAX_AO_CHANNELS 8 // maximum number of ao channels for supported boards
+
+/* PCI-DDA base addresses */
+#define DIGITALIO_BADRINDEX 2
+ // DIGITAL I/O is pci_dev->resource[2]
+#define DIGITALIO_SIZE 8
+ // DIGITAL I/O uses 8 I/O port addresses
+#define DAC_BADRINDEX 3
+ // DAC is pci_dev->resource[3]
+
+/* Digital I/O registers */
+#define PORT1A 0 // PORT 1A DATA
+
+#define PORT1B 1 // PORT 1B DATA
+
+#define PORT1C 2 // PORT 1C DATA
+
+#define CONTROL1 3 // CONTROL REGISTER 1
+
+#define PORT2A 4 // PORT 2A DATA
+
+#define PORT2B 5 // PORT 2B DATA
+
+#define PORT2C 6 // PORT 2C DATA
+
+#define CONTROL2 7 // CONTROL REGISTER 2
+
+/* DAC registers */
+#define DACONTROL 0 // D/A CONTROL REGISTER
+#define SU 0000001 // Simultaneous update enabled
+#define NOSU 0000000 // Simultaneous update disabled
+#define ENABLEDAC 0000002 // Enable specified DAC
+#define DISABLEDAC 0000000 // Disable specified DAC
+#define RANGE2V5 0000000 // 2.5V
+#define RANGE5V 0000200 // 5V
+#define RANGE10V 0000300 // 10V
+#define UNIP 0000400 // Unipolar outputs
+#define BIP 0000000 // Bipolar outputs
+
+#define DACALIBRATION1 4 // D/A CALIBRATION REGISTER 1
+//write bits
+#define SERIAL_IN_BIT 0x1 // serial data input for eeprom, caldacs, reference dac
+#define CAL_CHANNEL_MASK (0x7 << 1)
+#define CAL_CHANNEL_BITS(channel) (((channel) << 1) & CAL_CHANNEL_MASK)
+//read bits
+#define CAL_COUNTER_MASK 0x1f
+#define CAL_COUNTER_OVERFLOW_BIT 0x20 // calibration counter overflow status bit
+#define AO_BELOW_REF_BIT 0x40 // analog output is less than reference dac voltage
+#define SERIAL_OUT_BIT 0x80 // serial data out, for reading from eeprom
+
+#define DACALIBRATION2 6 // D/A CALIBRATION REGISTER 2
+#define SELECT_EEPROM_BIT 0x1 // send serial data in to eeprom
+#define DESELECT_REF_DAC_BIT 0x2 // don't send serial data to MAX542 reference dac
+#define DESELECT_CALDAC_BIT(n) (0x4 << (n)) // don't send serial data to caldac n
+#define DUMMY_BIT 0x40 // manual says to set this bit with no explanation
+
+#define DADATA 8 // FIRST D/A DATA REGISTER (0)
+
+static const struct comedi_lrange cb_pcidda_ranges = {
+ 6,
+ {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ }
+};
+
+/*
+ * Board descriptions for two imaginary boards. Describing the
+ * boards in this way is optional, and completely driver-dependent.
+ * Some drivers use arrays such as this, other do not.
+ */
+struct cb_pcidda_board {
+ const char *name;
+ char status; // Driver status:
+ // 0 - tested
+ // 1 - manual read, not tested
+ // 2 - manual not read
+ unsigned short device_id;
+ int ao_chans;
+ int ao_bits;
+ const struct comedi_lrange *ranges;
+};
+static const struct cb_pcidda_board cb_pcidda_boards[] = {
+ {
+ name: "pci-dda02/12",
+ status: 1,
+ device_id:0x20,
+ ao_chans:2,
+ ao_bits: 12,
+ ranges: &cb_pcidda_ranges,
+ },
+ {
+ name: "pci-dda04/12",
+ status: 1,
+ device_id:0x21,
+ ao_chans:4,
+ ao_bits: 12,
+ ranges: &cb_pcidda_ranges,
+ },
+ {
+ name: "pci-dda08/12",
+ status: 0,
+ device_id:0x22,
+ ao_chans:8,
+ ao_bits: 12,
+ ranges: &cb_pcidda_ranges,
+ },
+ {
+ name: "pci-dda02/16",
+ status: 2,
+ device_id:0x23,
+ ao_chans:2,
+ ao_bits: 16,
+ ranges: &cb_pcidda_ranges,
+ },
+ {
+ name: "pci-dda04/16",
+ status: 2,
+ device_id:0x24,
+ ao_chans:4,
+ ao_bits: 16,
+ ranges: &cb_pcidda_ranges,
+ },
+ {
+ name: "pci-dda08/16",
+ status: 0,
+ device_id:0x25,
+ ao_chans:8,
+ ao_bits: 16,
+ ranges: &cb_pcidda_ranges,
+ },
+};
+
+static DEFINE_PCI_DEVICE_TABLE(cb_pcidda_pci_table) = {
+ {PCI_VENDOR_ID_CB, 0x0020, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CB, 0x0021, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CB, 0x0022, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CB, 0x0023, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CB, 0x0024, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CB, 0x0025, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, cb_pcidda_pci_table);
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct cb_pcidda_board *)dev->board_ptr)
+
+/* 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 struct comedi_device struct. */
+struct cb_pcidda_private {
+ int data;
+
+ /* would be useful for a PCI device */
+ struct pci_dev *pci_dev;
+
+ unsigned long digitalio;
+ unsigned long dac;
+ //unsigned long control_status;
+ //unsigned long adc_fifo;
+ unsigned int dac_cal1_bits; // bits last written to da calibration register 1
+ unsigned int ao_range[MAX_AO_CHANNELS]; // current range settings for output channels
+ u16 eeprom_data[EEPROM_SIZE]; // software copy of board's eeprom
+};
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((struct cb_pcidda_private *)dev->private)
+
+static int cb_pcidda_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int cb_pcidda_detach(struct comedi_device * dev);
+//static int cb_pcidda_ai_rinsn(struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data);
+static int cb_pcidda_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+//static int cb_pcidda_ai_cmd(struct comedi_device *dev,struct comedi_subdevice *s);
+//static int cb_pcidda_ai_cmdtest(struct comedi_device *dev,struct comedi_subdevice *s, struct comedi_cmd *cmd);
+//static int cb_pcidda_ns_to_timer(unsigned int *ns,int round);
+static unsigned int cb_pcidda_serial_in(struct comedi_device * dev);
+static void cb_pcidda_serial_out(struct comedi_device * dev, unsigned int value,
+ unsigned int num_bits);
+static unsigned int cb_pcidda_read_eeprom(struct comedi_device * dev,
+ unsigned int address);
+static void cb_pcidda_calibrate(struct comedi_device * dev, unsigned int channel,
+ unsigned int range);
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static struct comedi_driver driver_cb_pcidda = {
+ driver_name:"cb_pcidda",
+ module:THIS_MODULE,
+ attach:cb_pcidda_attach,
+ detach:cb_pcidda_detach,
+};
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board.
+ */
+static int cb_pcidda_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ struct pci_dev *pcidev;
+ int index;
+
+ printk("comedi%d: cb_pcidda: ", dev->minor);
+
+/*
+ * Allocate the private structure area.
+ */
+ if (alloc_private(dev, sizeof(struct cb_pcidda_private)) < 0)
+ return -ENOMEM;
+
+/*
+ * Probe the device to determine what device in the series it is.
+ */
+ printk("\n");
+
+ for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pcidev != NULL;
+ pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
+ if (pcidev->vendor == PCI_VENDOR_ID_CB) {
+ if (it->options[0] || it->options[1]) {
+ if (pcidev->bus->number != it->options[0] ||
+ PCI_SLOT(pcidev->devfn) !=
+ it->options[1]) {
+ continue;
+ }
+ }
+ for (index = 0; index < N_BOARDS; index++) {
+ if (cb_pcidda_boards[index].device_id ==
+ pcidev->device) {
+ goto found;
+ }
+ }
+ }
+ }
+ if (!pcidev) {
+ printk("Not a ComputerBoards/MeasurementComputing card on requested position\n");
+ return -EIO;
+ }
+ found:
+ devpriv->pci_dev = pcidev;
+ dev->board_ptr = cb_pcidda_boards + index;
+ // "thisboard" macro can be used from here.
+ printk("Found %s at requested position\n", thisboard->name);
+
+ /*
+ * Enable PCI device and request regions.
+ */
+ if (comedi_pci_enable(pcidev, thisboard->name)) {
+ printk("cb_pcidda: failed to enable PCI device and request regions\n");
+ return -EIO;
+ }
+
+/*
+ * Allocate the I/O ports.
+ */
+ devpriv->digitalio =
+ pci_resource_start(devpriv->pci_dev, DIGITALIO_BADRINDEX);
+ devpriv->dac = pci_resource_start(devpriv->pci_dev, DAC_BADRINDEX);
+
+/*
+ * Warn about the status of the driver.
+ */
+ if (thisboard->status == 2)
+ printk("WARNING: DRIVER FOR THIS BOARD NOT CHECKED WITH MANUAL. " "WORKS ASSUMING FULL COMPATIBILITY WITH PCI-DDA08/12. " "PLEASE REPORT USAGE TO <ivanmr@altavista.com>.\n");
+
+/*
+ * Initialize dev->board_name.
+ */
+ dev->board_name = thisboard->name;
+
+/*
+ * Allocate the subdevice structures.
+ */
+ if (alloc_subdevices(dev, 3) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = thisboard->ao_chans;
+ s->maxdata = (1 << thisboard->ao_bits) - 1;
+ s->range_table = thisboard->ranges;
+ s->insn_write = cb_pcidda_ao_winsn;
+// s->subdev_flags |= SDF_CMD_READ;
+// s->do_cmd = cb_pcidda_ai_cmd;
+// s->do_cmdtest = cb_pcidda_ai_cmdtest;
+
+ // two 8255 digital io subdevices
+ s = dev->subdevices + 1;
+ subdev_8255_init(dev, s, NULL, devpriv->digitalio);
+ s = dev->subdevices + 2;
+ subdev_8255_init(dev, s, NULL, devpriv->digitalio + PORT2A);
+
+ printk(" eeprom:");
+ for (index = 0; index < EEPROM_SIZE; index++) {
+ devpriv->eeprom_data[index] = cb_pcidda_read_eeprom(dev, index);
+ printk(" %i:0x%x ", index, devpriv->eeprom_data[index]);
+ }
+ printk("\n");
+
+ // set calibrations dacs
+ for (index = 0; index < thisboard->ao_chans; index++)
+ cb_pcidda_calibrate(dev, index, devpriv->ao_range[index]);
+
+ return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int cb_pcidda_detach(struct comedi_device * dev)
+{
+/*
+ * Deallocate the I/O ports.
+ */
+ if (devpriv) {
+ if (devpriv->pci_dev) {
+ if (devpriv->dac) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ }
+ }
+ // cleanup 8255
+ if (dev->subdevices) {
+ subdev_8255_cleanup(dev, dev->subdevices + 1);
+ subdev_8255_cleanup(dev, dev->subdevices + 2);
+ }
+
+ printk("comedi%d: cb_pcidda: remove\n", dev->minor);
+
+ return 0;
+}
+
+/*
+ * I will program this later... ;-)
+ */
+#if 0
+static int cb_pcidda_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ printk("cb_pcidda_ai_cmd\n");
+ printk("subdev: %d\n", cmd->subdev);
+ printk("flags: %d\n", cmd->flags);
+ printk("start_src: %d\n", cmd->start_src);
+ printk("start_arg: %d\n", cmd->start_arg);
+ printk("scan_begin_src: %d\n", cmd->scan_begin_src);
+ printk("convert_src: %d\n", cmd->convert_src);
+ printk("convert_arg: %d\n", cmd->convert_arg);
+ printk("scan_end_src: %d\n", cmd->scan_end_src);
+ printk("scan_end_arg: %d\n", cmd->scan_end_arg);
+ printk("stop_src: %d\n", cmd->stop_src);
+ printk("stop_arg: %d\n", cmd->stop_arg);
+ printk("chanlist_len: %d\n", cmd->chanlist_len);
+}
+#endif
+
+#if 0
+static int cb_pcidda_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* cmdtest tests a particular command to see if it is valid.
+ * Using the cmdtest ioctl, a user can create a valid cmd
+ * and then have it executes by the cmd ioctl.
+ *
+ * cmdtest returns 1,2,3,4 or 0, depending on which tests
+ * the command passes. */
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ /* note that mutual compatiblity is not an issue here */
+ if (cmd->scan_begin_src != TRIG_TIMER
+ && cmd->scan_begin_src != TRIG_EXT)
+ err++;
+ if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
+ err++;
+ if (cmd->stop_src != TRIG_TIMER && cmd->stop_src != TRIG_EXT)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+#define MAX_SPEED 10000 /* in nanoseconds */
+#define MIN_SPEED 1000000000 /* in nanoseconds */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->scan_begin_arg < MAX_SPEED) {
+ cmd->scan_begin_arg = MAX_SPEED;
+ err++;
+ }
+ if (cmd->scan_begin_arg > MIN_SPEED) {
+ cmd->scan_begin_arg = MIN_SPEED;
+ err++;
+ }
+ } else {
+ /* external trigger */
+ /* should be level/edge, hi/lo specification here */
+ /* should specify multiple external triggers */
+ if (cmd->scan_begin_arg > 9) {
+ cmd->scan_begin_arg = 9;
+ err++;
+ }
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < MAX_SPEED) {
+ cmd->convert_arg = MAX_SPEED;
+ err++;
+ }
+ if (cmd->convert_arg > MIN_SPEED) {
+ cmd->convert_arg = MIN_SPEED;
+ err++;
+ }
+ } else {
+ /* external trigger */
+ /* see above */
+ if (cmd->convert_arg > 9) {
+ cmd->convert_arg = 9;
+ err++;
+ }
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (cmd->stop_arg > 0x00ffffff) {
+ cmd->stop_arg = 0x00ffffff;
+ err++;
+ }
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ cb_pcidda_ns_to_timer(&cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ cb_pcidda_ns_to_timer(&cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->convert_arg)
+ err++;
+ if (cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->scan_begin_arg <
+ cmd->convert_arg * cmd->scan_end_arg) {
+ cmd->scan_begin_arg =
+ cmd->convert_arg * cmd->scan_end_arg;
+ err++;
+ }
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+#endif
+
+/* This function doesn't require a particular form, this is just
+ * what happens to be used in some of the drivers. It should
+ * convert ns nanoseconds to a counter value suitable for programming
+ * the device. Also, it should adjust ns so that it cooresponds to
+ * the actual time that the device will use. */
+#if 0
+static int cb_pcidda_ns_to_timer(unsigned int *ns, int round)
+{
+ /* trivial timer */
+ return *ns;
+}
+#endif
+
+static int cb_pcidda_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int command;
+ unsigned int channel, range;
+
+ channel = CR_CHAN(insn->chanspec);
+ range = CR_RANGE(insn->chanspec);
+
+ // adjust calibration dacs if range has changed
+ if (range != devpriv->ao_range[channel])
+ cb_pcidda_calibrate(dev, channel, range);
+
+ /* output channel configuration */
+ command = NOSU | ENABLEDAC;
+
+ /* output channel range */
+ switch (range) {
+ case 0:
+ command |= BIP | RANGE10V;
+ break;
+ case 1:
+ command |= BIP | RANGE5V;
+ break;
+ case 2:
+ command |= BIP | RANGE2V5;
+ break;
+ case 3:
+ command |= UNIP | RANGE10V;
+ break;
+ case 4:
+ command |= UNIP | RANGE5V;
+ break;
+ case 5:
+ command |= UNIP | RANGE2V5;
+ break;
+ };
+
+ /* output channel specification */
+ command |= channel << 2;
+ outw(command, devpriv->dac + DACONTROL);
+
+ /* write data */
+ outw(data[0], devpriv->dac + DADATA + channel * 2);
+
+ /* return the number of samples read/written */
+ return 1;
+}
+
+// lowlevel read from eeprom
+static unsigned int cb_pcidda_serial_in(struct comedi_device * dev)
+{
+ unsigned int value = 0;
+ int i;
+ const int value_width = 16; // number of bits wide values are
+
+ for (i = 1; i <= value_width; i++) {
+ // read bits most significant bit first
+ if (inw_p(devpriv->dac + DACALIBRATION1) & SERIAL_OUT_BIT) {
+ value |= 1 << (value_width - i);
+ }
+ }
+
+ return value;
+}
+
+// lowlevel write to eeprom/dac
+static void cb_pcidda_serial_out(struct comedi_device * dev, unsigned int value,
+ unsigned int num_bits)
+{
+ int i;
+
+ for (i = 1; i <= num_bits; i++) {
+ // send bits most significant bit first
+ if (value & (1 << (num_bits - i)))
+ devpriv->dac_cal1_bits |= SERIAL_IN_BIT;
+ else
+ devpriv->dac_cal1_bits &= ~SERIAL_IN_BIT;
+ outw_p(devpriv->dac_cal1_bits, devpriv->dac + DACALIBRATION1);
+ }
+}
+
+// reads a 16 bit value from board's eeprom
+static unsigned int cb_pcidda_read_eeprom(struct comedi_device * dev,
+ unsigned int address)
+{
+ unsigned int i;
+ unsigned int cal2_bits;
+ unsigned int value;
+ const int max_num_caldacs = 4; // one caldac for every two dac channels
+ const int read_instruction = 0x6; // bits to send to tell eeprom we want to read
+ const int instruction_length = 3;
+ const int address_length = 8;
+
+ // send serial output stream to eeprom
+ cal2_bits = SELECT_EEPROM_BIT | DESELECT_REF_DAC_BIT | DUMMY_BIT;
+ // deactivate caldacs (one caldac for every two channels)
+ for (i = 0; i < max_num_caldacs; i++) {
+ cal2_bits |= DESELECT_CALDAC_BIT(i);
+ }
+ outw_p(cal2_bits, devpriv->dac + DACALIBRATION2);
+
+ // tell eeprom we want to read
+ cb_pcidda_serial_out(dev, read_instruction, instruction_length);
+ // send address we want to read from
+ cb_pcidda_serial_out(dev, address, address_length);
+
+ value = cb_pcidda_serial_in(dev);
+
+ // deactivate eeprom
+ cal2_bits &= ~SELECT_EEPROM_BIT;
+ outw_p(cal2_bits, devpriv->dac + DACALIBRATION2);
+
+ return value;
+}
+
+// writes to 8 bit calibration dacs
+static void cb_pcidda_write_caldac(struct comedi_device * dev, unsigned int caldac,
+ unsigned int channel, unsigned int value)
+{
+ unsigned int cal2_bits;
+ unsigned int i;
+ const int num_channel_bits = 3; // caldacs use 3 bit channel specification
+ const int num_caldac_bits = 8; // 8 bit calibration dacs
+ const int max_num_caldacs = 4; // one caldac for every two dac channels
+
+ /* write 3 bit channel */
+ cb_pcidda_serial_out(dev, channel, num_channel_bits);
+ // write 8 bit caldac value
+ cb_pcidda_serial_out(dev, value, num_caldac_bits);
+
+ // latch stream into appropriate caldac
+ // deselect reference dac
+ cal2_bits = DESELECT_REF_DAC_BIT | DUMMY_BIT;
+ // deactivate caldacs (one caldac for every two channels)
+ for (i = 0; i < max_num_caldacs; i++) {
+ cal2_bits |= DESELECT_CALDAC_BIT(i);
+ }
+ // activate the caldac we want
+ cal2_bits &= ~DESELECT_CALDAC_BIT(caldac);
+ outw_p(cal2_bits, devpriv->dac + DACALIBRATION2);
+ // deactivate caldac
+ cal2_bits |= DESELECT_CALDAC_BIT(caldac);
+ outw_p(cal2_bits, devpriv->dac + DACALIBRATION2);
+}
+
+// returns caldac that calibrates given analog out channel
+static unsigned int caldac_number(unsigned int channel)
+{
+ return channel / 2;
+}
+
+// returns caldac channel that provides fine gain for given ao channel
+static unsigned int fine_gain_channel(unsigned int ao_channel)
+{
+ return 4 * (ao_channel % 2);
+}
+
+// returns caldac channel that provides coarse gain for given ao channel
+static unsigned int coarse_gain_channel(unsigned int ao_channel)
+{
+ return 1 + 4 * (ao_channel % 2);
+}
+
+// returns caldac channel that provides coarse offset for given ao channel
+static unsigned int coarse_offset_channel(unsigned int ao_channel)
+{
+ return 2 + 4 * (ao_channel % 2);
+}
+
+// returns caldac channel that provides fine offset for given ao channel
+static unsigned int fine_offset_channel(unsigned int ao_channel)
+{
+ return 3 + 4 * (ao_channel % 2);
+}
+
+// returns eeprom address that provides offset for given ao channel and range
+static unsigned int offset_eeprom_address(unsigned int ao_channel,
+ unsigned int range)
+{
+ return 0x7 + 2 * range + 12 * ao_channel;
+}
+
+// returns eeprom address that provides gain calibration for given ao channel and range
+static unsigned int gain_eeprom_address(unsigned int ao_channel,
+ unsigned int range)
+{
+ return 0x8 + 2 * range + 12 * ao_channel;
+}
+
+// returns upper byte of eeprom entry, which gives the coarse adjustment values
+static unsigned int eeprom_coarse_byte(unsigned int word)
+{
+ return (word >> 8) & 0xff;
+}
+
+// returns lower byte of eeprom entry, which gives the fine adjustment values
+static unsigned int eeprom_fine_byte(unsigned int word)
+{
+ return word & 0xff;
+}
+
+// set caldacs to eeprom values for given channel and range
+static void cb_pcidda_calibrate(struct comedi_device * dev, unsigned int channel,
+ unsigned int range)
+{
+ unsigned int coarse_offset, fine_offset, coarse_gain, fine_gain;
+
+ // remember range so we can tell when we need to readjust calibration
+ devpriv->ao_range[channel] = range;
+
+ // get values from eeprom data
+ coarse_offset =
+ eeprom_coarse_byte(devpriv->
+ eeprom_data[offset_eeprom_address(channel, range)]);
+ fine_offset =
+ eeprom_fine_byte(devpriv->
+ eeprom_data[offset_eeprom_address(channel, range)]);
+ coarse_gain =
+ eeprom_coarse_byte(devpriv->
+ eeprom_data[gain_eeprom_address(channel, range)]);
+ fine_gain =
+ eeprom_fine_byte(devpriv->
+ eeprom_data[gain_eeprom_address(channel, range)]);
+
+ // set caldacs
+ cb_pcidda_write_caldac(dev, caldac_number(channel),
+ coarse_offset_channel(channel), coarse_offset);
+ cb_pcidda_write_caldac(dev, caldac_number(channel),
+ fine_offset_channel(channel), fine_offset);
+ cb_pcidda_write_caldac(dev, caldac_number(channel),
+ coarse_gain_channel(channel), coarse_gain);
+ cb_pcidda_write_caldac(dev, caldac_number(channel),
+ fine_gain_channel(channel), fine_gain);
+}
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_PCI_INITCLEANUP(driver_cb_pcidda, cb_pcidda_pci_table);
diff --git a/drivers/staging/comedi/drivers/cb_pcidio.c b/drivers/staging/comedi/drivers/cb_pcidio.c
new file mode 100644
index 000000000000..71a4b1482e36
--- /dev/null
+++ b/drivers/staging/comedi/drivers/cb_pcidio.c
@@ -0,0 +1,294 @@
+/*
+ comedi/drivers/cb_pcidio.c
+ A Comedi driver for PCI-DIO24H & PCI-DIO48H of ComputerBoards (currently MeasurementComputing)
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: cb_pcidio
+Description: ComputerBoards' DIO boards with PCI interface
+Devices: [Measurement Computing] PCI-DIO24 (cb_pcidio), PCI-DIO24H, PCI-DIO48H
+Author: Yoshiya Matsuzaka
+Updated: Mon, 29 Oct 2007 15:40:47 +0000
+Status: experimental
+
+This driver has been modified from skel.c of comedi-0.7.70.
+
+Configuration Options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first available PCI device will
+ be used.
+
+Passing a zero for an option is the same as leaving it unspecified.
+*/
+
+/*------------------------------ HEADER FILES ---------------------------------*/
+#include "../comedidev.h"
+#include "comedi_pci.h"
+#include "8255.h"
+
+/*-------------------------- MACROS and DATATYPES -----------------------------*/
+#define PCI_VENDOR_ID_CB 0x1307
+
+/*
+ * Board descriptions for two imaginary boards. Describing the
+ * boards in this way is optional, and completely driver-dependent.
+ * Some drivers use arrays such as this, other do not.
+ */
+struct pcidio_board {
+ const char *name; // anme of the board
+ int n_8255; // number of 8255 chips on board
+
+ // indices of base address regions
+ int pcicontroler_badrindex;
+ int dioregs_badrindex;
+};
+
+static const struct pcidio_board pcidio_boards[] = {
+ {
+ name: "pci-dio24",
+ n_8255: 1,
+ pcicontroler_badrindex:1,
+ dioregs_badrindex:2,
+ },
+ {
+ name: "pci-dio24h",
+ n_8255: 1,
+ pcicontroler_badrindex:1,
+ dioregs_badrindex:2,
+ },
+ {
+ name: "pci-dio48h",
+ n_8255: 2,
+ pcicontroler_badrindex:0,
+ dioregs_badrindex:1,
+ },
+};
+
+/* This is used by modprobe to translate PCI IDs to drivers. Should
+ * only be used for PCI and ISA-PnP devices */
+/* Please add your PCI vendor ID to comedidev.h, and it will be forwarded
+ * upstream. */
+static DEFINE_PCI_DEVICE_TABLE(pcidio_pci_table) = {
+ {PCI_VENDOR_ID_CB, 0x0028, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CB, 0x0014, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CB, 0x000b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, pcidio_pci_table);
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct pcidio_board *)dev->board_ptr)
+
+/* 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 struct comedi_device struct. */
+struct pcidio_private {
+ int data; // curently unused
+
+ /* would be useful for a PCI device */
+ struct pci_dev *pci_dev;
+
+ /* used for DO readback, curently unused */
+ unsigned int do_readback[4]; /* up to 4 unsigned int suffice to hold 96 bits for PCI-DIO96 */
+
+ unsigned long dio_reg_base; // address of port A of the first 8255 chip on board
+};
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((struct pcidio_private *)dev->private)
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int pcidio_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcidio_detach(struct comedi_device * dev);
+static struct comedi_driver driver_cb_pcidio = {
+ driver_name:"cb_pcidio",
+ module:THIS_MODULE,
+ attach:pcidio_attach,
+ detach:pcidio_detach,
+/* It is not necessary to implement the following members if you are
+ * writing a driver for a ISA PnP or PCI card */
+ /* Most drivers will support multiple types of boards by
+ * having an array of board structures. These were defined
+ * in pcidio_boards[] above. Note that the element 'name'
+ * was first in the structure -- Comedi uses this fact to
+ * extract the name of the board without knowing any details
+ * about the structure except for its length.
+ * When a device is attached (by comedi_config), the name
+ * of the device is given to Comedi, and Comedi tries to
+ * match it by going through the list of board names. If
+ * there is a match, the address of the pointer is put
+ * into dev->board_ptr and driver->attach() is called.
+ *
+ * Note that these are not necessary if you can determine
+ * the type of board in software. ISA PnP, PCI, and PCMCIA
+ * devices are such boards.
+ */
+// The following fields should NOT be initialized if you are dealing with PCI devices
+// board_name: pcidio_boards,
+// offset: sizeof(struct pcidio_board),
+// num_names: sizeof(pcidio_boards) / sizeof(struct pcidio_board),
+};
+
+/*------------------------------- FUNCTIONS -----------------------------------*/
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int pcidio_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct pci_dev *pcidev = NULL;
+ int index;
+ int i;
+
+ printk("comedi%d: cb_pcidio: \n", dev->minor);
+
+/*
+ * Allocate the private structure area. alloc_private() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_private(dev, sizeof(struct pcidio_private)) < 0)
+ return -ENOMEM;
+/*
+ * If you can probe the device to determine what device in a series
+ * it is, this is the place to do it. Otherwise, dev->board_ptr
+ * should already be initialized.
+ */
+/*
+ * Probe the device to determine what device in the series it is.
+ */
+
+ for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pcidev != NULL;
+ pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
+ // is it not a computer boards card?
+ if (pcidev->vendor != PCI_VENDOR_ID_CB)
+ continue;
+ // loop through cards supported by this driver
+ for (index = 0;
+ index < sizeof pcidio_boards / sizeof(struct pcidio_board);
+ index++) {
+ if (pcidio_pci_table[index].device != pcidev->device)
+ continue;
+
+ // was a particular bus/slot requested?
+ if (it->options[0] || it->options[1]) {
+ // are we on the wrong bus/slot?
+ if (pcidev->bus->number != it->options[0] ||
+ PCI_SLOT(pcidev->devfn) !=
+ it->options[1]) {
+ continue;
+ }
+ }
+ dev->board_ptr = pcidio_boards + index;
+ goto found;
+ }
+ }
+
+ printk("No supported ComputerBoards/MeasurementComputing card found on "
+ "requested position\n");
+ return -EIO;
+
+ found:
+
+/*
+ * Initialize dev->board_name. Note that we can use the "thisboard"
+ * macro now, since we just initialized it in the last line.
+ */
+ dev->board_name = thisboard->name;
+
+ devpriv->pci_dev = pcidev;
+ printk("Found %s on bus %i, slot %i\n", thisboard->name,
+ devpriv->pci_dev->bus->number,
+ PCI_SLOT(devpriv->pci_dev->devfn));
+ if (comedi_pci_enable(pcidev, thisboard->name)) {
+ printk("cb_pcidio: failed to enable PCI device and request regions\n");
+ return -EIO;
+ }
+ devpriv->dio_reg_base
+ =
+ pci_resource_start(devpriv->pci_dev,
+ pcidio_boards[index].dioregs_badrindex);
+
+/*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_subdevices(dev, thisboard->n_8255) < 0)
+ return -ENOMEM;
+
+ for (i = 0; i < thisboard->n_8255; i++) {
+ subdev_8255_init(dev, dev->subdevices + i,
+ NULL, devpriv->dio_reg_base + i * 4);
+ printk(" subdev %d: base = 0x%lx\n", i,
+ devpriv->dio_reg_base + i * 4);
+ }
+
+ printk("attached\n");
+ return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int pcidio_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: cb_pcidio: remove\n", dev->minor);
+ if (devpriv) {
+ if (devpriv->pci_dev) {
+ if (devpriv->dio_reg_base) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ }
+ }
+ if (dev->subdevices) {
+ int i;
+ for (i = 0; i < thisboard->n_8255; i++) {
+ subdev_8255_cleanup(dev, dev->subdevices + i);
+ }
+ }
+ return 0;
+}
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_PCI_INITCLEANUP(driver_cb_pcidio, pcidio_pci_table);
diff --git a/drivers/staging/comedi/drivers/cb_pcimdas.c b/drivers/staging/comedi/drivers/cb_pcimdas.c
new file mode 100644
index 000000000000..af705fa092c0
--- /dev/null
+++ b/drivers/staging/comedi/drivers/cb_pcimdas.c
@@ -0,0 +1,484 @@
+/*
+ comedi/drivers/cb_pcimdas.c
+ Comedi driver for Computer Boards PCIM-DAS1602/16
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: cb_pcimdas
+Description: Measurement Computing PCI Migration series boards
+Devices: [ComputerBoards] PCIM-DAS1602/16 (cb_pcimdas)
+Author: Richard Bytheway
+Updated: Wed, 13 Nov 2002 12:34:56 +0000
+Status: experimental
+
+Written to support the PCIM-DAS1602/16 on a 2.4 series kernel.
+
+Configuration Options:
+ [0] - PCI bus number
+ [1] - PCI slot number
+
+Developed from cb_pcidas and skel by Richard Bytheway (mocelet@sucs.org).
+Only supports DIO, AO and simple AI in it's present form.
+No interrupts, multi channel or FIFO AI, although the card looks like it could support this.
+See http://www.measurementcomputing.com/PDFManuals/pcim-das1602_16.pdf for more details.
+*/
+
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+
+#include "comedi_pci.h"
+#include "plx9052.h"
+#include "8255.h"
+
+//#define CBPCIMDAS_DEBUG
+#undef CBPCIMDAS_DEBUG
+
+/* Registers for the PCIM-DAS1602/16 */
+
+// sizes of io regions (bytes)
+#define BADR0_SIZE 2 //??
+#define BADR1_SIZE 4
+#define BADR2_SIZE 6
+#define BADR3_SIZE 16
+#define BADR4_SIZE 4
+
+//DAC Offsets
+#define ADC_TRIG 0
+#define DAC0_OFFSET 2
+#define DAC1_OFFSET 4
+
+//AI and Counter Constants
+#define MUX_LIMITS 0
+#define MAIN_CONN_DIO 1
+#define ADC_STAT 2
+#define ADC_CONV_STAT 3
+#define ADC_INT 4
+#define ADC_PACER 5
+#define BURST_MODE 6
+#define PROG_GAIN 7
+#define CLK8254_1_DATA 8
+#define CLK8254_2_DATA 9
+#define CLK8254_3_DATA 10
+#define CLK8254_CONTROL 11
+#define USER_COUNTER 12
+#define RESID_COUNT_H 13
+#define RESID_COUNT_L 14
+
+/* Board description */
+struct cb_pcimdas_board {
+ const char *name;
+ unsigned short device_id;
+ int ai_se_chans; // Inputs in single-ended mode
+ int ai_diff_chans; // Inputs in differential mode
+ int ai_bits; // analog input resolution
+ int ai_speed; // fastest conversion period in ns
+ int ao_nchan; // number of analog out channels
+ int ao_bits; // analogue output resolution
+ int has_ao_fifo; // analog output has fifo
+ int ao_scan_speed; // analog output speed for 1602 series (for a scan, not conversion)
+ int fifo_size; // number of samples fifo can hold
+ int dio_bits; // number of dio bits
+ int has_dio; // has DIO
+ const struct comedi_lrange *ranges;
+};
+
+static const struct cb_pcimdas_board cb_pcimdas_boards[] = {
+ {
+ name: "PCIM-DAS1602/16",
+ device_id:0x56,
+ ai_se_chans:16,
+ ai_diff_chans:8,
+ ai_bits: 16,
+ ai_speed:10000, //??
+ ao_nchan:2,
+ ao_bits: 12,
+ has_ao_fifo:0, //??
+ ao_scan_speed:10000,
+ //??
+ fifo_size:1024,
+ dio_bits:24,
+ has_dio: 1,
+// ranges: &cb_pcimdas_ranges,
+ },
+};
+
+/* This is used by modprobe to translate PCI IDs to drivers. Should
+ * only be used for PCI and ISA-PnP devices */
+static DEFINE_PCI_DEVICE_TABLE(cb_pcimdas_pci_table) = {
+ {PCI_VENDOR_ID_COMPUTERBOARDS, 0x0056, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, cb_pcimdas_pci_table);
+
+#define N_BOARDS 1 // Max number of boards supported
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct cb_pcimdas_board *)dev->board_ptr)
+
+/* 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 struct comedi_device struct. */
+struct cb_pcimdas_private {
+ int data;
+
+ // would be useful for a PCI device
+ struct pci_dev *pci_dev;
+
+ //base addresses
+ unsigned long BADR0;
+ unsigned long BADR1;
+ unsigned long BADR2;
+ unsigned long BADR3;
+ unsigned long BADR4;
+
+ /* Used for AO readback */
+ unsigned int ao_readback[2];
+
+ // Used for DIO
+ unsigned short int port_a; // copy of BADR4+0
+ unsigned short int port_b; // copy of BADR4+1
+ unsigned short int port_c; // copy of BADR4+2
+ unsigned short int dio_mode; // copy of BADR4+3
+
+};
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((struct cb_pcimdas_private *)dev->private)
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int cb_pcimdas_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int cb_pcimdas_detach(struct comedi_device * dev);
+static struct comedi_driver driver_cb_pcimdas = {
+ driver_name:"cb_pcimdas",
+ module:THIS_MODULE,
+ attach:cb_pcimdas_attach,
+ detach:cb_pcimdas_detach,
+};
+
+static int cb_pcimdas_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int cb_pcimdas_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int cb_pcimdas_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int cb_pcimdas_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ struct pci_dev *pcidev;
+ int index;
+ //int i;
+
+ printk("comedi%d: cb_pcimdas: ", dev->minor);
+
+/*
+ * Allocate the private structure area.
+ */
+ if (alloc_private(dev, sizeof(struct cb_pcimdas_private)) < 0)
+ return -ENOMEM;
+
+/*
+ * Probe the device to determine what device in the series it is.
+ */
+ printk("\n");
+
+ for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pcidev != NULL;
+ pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
+ // is it not a computer boards card?
+ if (pcidev->vendor != PCI_VENDOR_ID_COMPUTERBOARDS)
+ continue;
+ // loop through cards supported by this driver
+ for (index = 0; index < N_BOARDS; index++) {
+ if (cb_pcimdas_boards[index].device_id !=
+ pcidev->device)
+ continue;
+ // was a particular bus/slot requested?
+ if (it->options[0] || it->options[1]) {
+ // are we on the wrong bus/slot?
+ if (pcidev->bus->number != it->options[0] ||
+ PCI_SLOT(pcidev->devfn) !=
+ it->options[1]) {
+ continue;
+ }
+ }
+ devpriv->pci_dev = pcidev;
+ dev->board_ptr = cb_pcimdas_boards + index;
+ goto found;
+ }
+ }
+
+ printk("No supported ComputerBoards/MeasurementComputing card found on "
+ "requested position\n");
+ return -EIO;
+
+ found:
+
+ printk("Found %s on bus %i, slot %i\n", cb_pcimdas_boards[index].name,
+ pcidev->bus->number, PCI_SLOT(pcidev->devfn));
+
+ // Warn about non-tested features
+ switch (thisboard->device_id) {
+ case 0x56:
+ break;
+ default:
+ printk("THIS CARD IS UNSUPPORTED.\n"
+ "PLEASE REPORT USAGE TO <mocelet@sucs.org>\n");
+ };
+
+ if (comedi_pci_enable(pcidev, "cb_pcimdas")) {
+ printk(" Failed to enable PCI device and request regions\n");
+ return -EIO;
+ }
+
+ devpriv->BADR0 = pci_resource_start(devpriv->pci_dev, 0);
+ devpriv->BADR1 = pci_resource_start(devpriv->pci_dev, 1);
+ devpriv->BADR2 = pci_resource_start(devpriv->pci_dev, 2);
+ devpriv->BADR3 = pci_resource_start(devpriv->pci_dev, 3);
+ devpriv->BADR4 = pci_resource_start(devpriv->pci_dev, 4);
+
+#ifdef CBPCIMDAS_DEBUG
+ printk("devpriv->BADR0 = 0x%lx\n", devpriv->BADR0);
+ printk("devpriv->BADR1 = 0x%lx\n", devpriv->BADR1);
+ printk("devpriv->BADR2 = 0x%lx\n", devpriv->BADR2);
+ printk("devpriv->BADR3 = 0x%lx\n", devpriv->BADR3);
+ printk("devpriv->BADR4 = 0x%lx\n", devpriv->BADR4);
+#endif
+
+// Dont support IRQ yet
+// // get irq
+// if(comedi_request_irq(devpriv->pci_dev->irq, cb_pcimdas_interrupt, IRQF_SHARED, "cb_pcimdas", dev ))
+// {
+// printk(" unable to allocate irq %u\n", devpriv->pci_dev->irq);
+// return -EINVAL;
+// }
+// dev->irq = devpriv->pci_dev->irq;
+
+ //Initialize dev->board_name
+ dev->board_name = thisboard->name;
+
+/*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_subdevices(dev, 3) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ //dev->read_subdev=s;
+ // analog input subdevice
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = thisboard->ai_se_chans;
+ s->maxdata = (1 << thisboard->ai_bits) - 1;
+ s->range_table = &range_unknown;
+ s->len_chanlist = 1; // This is the maximum chanlist length that
+ // the board can handle
+ s->insn_read = cb_pcimdas_ai_rinsn;
+
+ s = dev->subdevices + 1;
+ // analog output subdevice
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = thisboard->ao_nchan;
+ s->maxdata = 1 << thisboard->ao_bits;
+ s->range_table = &range_unknown; //ranges are hardware settable, but not software readable.
+ s->insn_write = &cb_pcimdas_ao_winsn;
+ s->insn_read = &cb_pcimdas_ao_rinsn;
+
+ s = dev->subdevices + 2;
+ /* digital i/o subdevice */
+ if (thisboard->has_dio) {
+ subdev_8255_init(dev, s, NULL, devpriv->BADR4);
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ printk("attached\n");
+
+ return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int cb_pcimdas_detach(struct comedi_device * dev)
+{
+#ifdef CBPCIMDAS_DEBUG
+ if (devpriv) {
+ printk("devpriv->BADR0 = 0x%lx\n", devpriv->BADR0);
+ printk("devpriv->BADR1 = 0x%lx\n", devpriv->BADR1);
+ printk("devpriv->BADR2 = 0x%lx\n", devpriv->BADR2);
+ printk("devpriv->BADR3 = 0x%lx\n", devpriv->BADR3);
+ printk("devpriv->BADR4 = 0x%lx\n", devpriv->BADR4);
+ }
+#endif
+ printk("comedi%d: cb_pcimdas: remove\n", dev->minor);
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+ if (devpriv) {
+ if (devpriv->pci_dev) {
+ if (devpriv->BADR0) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * "instructions" read/write data in "one-shot" or "software-triggered"
+ * mode.
+ */
+static int cb_pcimdas_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, i;
+ unsigned int d;
+ unsigned int busy;
+ int chan = CR_CHAN(insn->chanspec);
+ unsigned short chanlims;
+ int maxchans;
+
+ // only support sw initiated reads from a single channel
+
+ //check channel number
+ if ((inb(devpriv->BADR3 + 2) & 0x20) == 0) //differential mode
+ maxchans = thisboard->ai_diff_chans;
+ else
+ maxchans = thisboard->ai_se_chans;
+
+ if (chan > (maxchans - 1))
+ return -ETIMEDOUT; //*** Wrong error code. Fixme.
+
+ //configure for sw initiated read
+ d = inb(devpriv->BADR3 + 5);
+ if ((d & 0x03) > 0) { //only reset if needed.
+ d = d & 0xfd;
+ outb(d, devpriv->BADR3 + 5);
+ }
+ outb(0x01, devpriv->BADR3 + 6); //set bursting off, conversions on
+ outb(0x00, devpriv->BADR3 + 7); //set range to 10V. UP/BP is controlled by a switch on the board
+
+ // write channel limits to multiplexer, set Low (bits 0-3) and High (bits 4-7) channels to chan.
+ chanlims = chan | (chan << 4);
+ outb(chanlims, devpriv->BADR3 + 0);
+
+ /* convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion */
+ outw(0, devpriv->BADR2 + 0);
+
+#define TIMEOUT 1000 //typically takes 5 loops on a lightly loaded Pentium 100MHz,
+ //this is likely to be 100 loops on a 2GHz machine, so set 1000 as the limit.
+
+ /* wait for conversion to end */
+ for (i = 0; i < TIMEOUT; i++) {
+ busy = inb(devpriv->BADR3 + 2) & 0x80;
+ if (!busy)
+ break;
+ }
+ if (i == TIMEOUT) {
+ printk("timeout\n");
+ return -ETIMEDOUT;
+ }
+ /* read data */
+ d = inw(devpriv->BADR2 + 0);
+
+ /* mangle the data as necessary */
+ //d ^= 1<<(thisboard->ai_bits-1); // 16 bit data from ADC, so no mangle needed.
+
+ data[n] = d;
+ }
+
+ /* return the number of samples read/written */
+ return n;
+}
+
+static int cb_pcimdas_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ /* Writing a list of values to an AO channel is probably not
+ * very useful, but that's how the interface is defined. */
+ for (i = 0; i < insn->n; i++) {
+ switch (chan) {
+ case 0:
+ outw(data[i] & 0x0FFF, devpriv->BADR2 + DAC0_OFFSET);
+ break;
+ case 1:
+ outw(data[i] & 0x0FFF, devpriv->BADR2 + DAC1_OFFSET);
+ break;
+ default:
+ return -1;
+ }
+ devpriv->ao_readback[chan] = data[i];
+ }
+
+ /* return the number of samples read/written */
+ return i;
+}
+
+/* AO subdevices should have a read insn as well as a write insn.
+ * Usually this means copying a value stored in devpriv. */
+static int cb_pcimdas_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = devpriv->ao_readback[chan];
+
+ return i;
+}
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_PCI_INITCLEANUP(driver_cb_pcimdas, cb_pcimdas_pci_table);
diff --git a/drivers/staging/comedi/drivers/cb_pcimdda.c b/drivers/staging/comedi/drivers/cb_pcimdda.c
new file mode 100644
index 000000000000..e4c5b460fbd5
--- /dev/null
+++ b/drivers/staging/comedi/drivers/cb_pcimdda.c
@@ -0,0 +1,474 @@
+/*
+ comedi/drivers/cb_pcimdda.c
+ Computer Boards PCIM-DDA06-16 Comedi driver
+ Author: Calin Culianu <calin@ajvar.org>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: cb_pcimdda
+Description: Measurement Computing PCIM-DDA06-16
+Devices: [Measurement Computing] PCIM-DDA06-16 (cb_pcimdda)
+Author: Calin Culianu <calin@ajvar.org>
+Updated: Mon, 14 Apr 2008 15:15:51 +0100
+Status: works
+
+All features of the PCIM-DDA06-16 board are supported. This board
+has 6 16-bit AO channels, and the usual 8255 DIO setup. (24 channels,
+configurable in banks of 8 and 4, etc.). This board does not support commands.
+
+The board has a peculiar way of specifying AO gain/range settings -- You have
+1 jumper bank on the card, which either makes all 6 AO channels either
+5 Volt unipolar, 5V bipolar, 10 Volt unipolar or 10V bipolar.
+
+Since there is absolutely _no_ way to tell in software how this jumper is set
+(well, at least according to the rather thin spec. from Measurement Computing
+ that comes with the board), the driver assumes the jumper is at its factory
+default setting of +/-5V.
+
+Also of note is the fact that this board features another jumper, whose
+state is also completely invisible to software. It toggles two possible AO
+output modes on the board:
+
+ - Update Mode: Writing to an AO channel instantaneously updates the actual
+ signal output by the DAC on the board (this is the factory default).
+ - Simultaneous XFER Mode: Writing to an AO channel has no effect until
+ you read from any one of the AO channels. This is useful for loading
+ all 6 AO values, and then reading from any one of the AO channels on the
+ device to instantly update all 6 AO values in unison. Useful for some
+ control apps, I would assume? If your jumper is in this setting, then you
+ need to issue your comedi_data_write()s to load all the values you want,
+ then issue one comedi_data_read() on any channel on the AO subdevice
+ to initiate the simultaneous XFER.
+
+Configuration Options:
+ [0] PCI bus (optional)
+ [1] PCI slot (optional)
+ [2] analog output range jumper setting
+ 0 == +/- 5 V
+ 1 == +/- 10 V
+*/
+
+/*
+ This is a driver for the Computer Boards PCIM-DDA06-16 Analog Output
+ card. This board has a unique register layout and as such probably
+ deserves its own driver file.
+
+ It is theoretically possible to integrate this board into the cb_pcidda
+ file, but since that isn't my code, I didn't want to significantly
+ modify that file to support this board (I thought it impolite to do so).
+
+ At any rate, if you feel ambitious, please feel free to take
+ the code out of this file and combine it with a more unified driver
+ file.
+
+ I would like to thank Timothy Curry <Timothy.Curry@rdec.redstone.army.mil>
+ for lending me a board so that I could write this driver.
+
+ -Calin Culianu <calin@ajvar.org>
+ */
+
+#include "../comedidev.h"
+
+#include "comedi_pci.h"
+
+#include "8255.h"
+
+/* device ids of the cards we support -- currently only 1 card supported */
+#define PCI_ID_PCIM_DDA06_16 0x0053
+
+/*
+ * This is straight from skel.c -- I did this in case this source file
+ * will someday support more than 1 board...
+ */
+struct board_struct {
+ const char *name;
+ unsigned short device_id;
+ int ao_chans;
+ int ao_bits;
+ int dio_chans;
+ int dio_method;
+ int dio_offset; /* how many bytes into the BADR are the DIO ports */
+ int regs_badrindex; /* IO Region for the control, analog output,
+ and DIO registers */
+ int reg_sz; /* number of bytes of registers in io region */
+};
+
+enum DIO_METHODS {
+ DIO_NONE = 0,
+ DIO_8255,
+ DIO_INTERNAL /* unimplemented */
+};
+
+static const struct board_struct boards[] = {
+ {
+ name: "cb_pcimdda06-16",
+ device_id:PCI_ID_PCIM_DDA06_16,
+ ao_chans:6,
+ ao_bits: 16,
+ dio_chans:24,
+ dio_method:DIO_8255,
+ dio_offset:12,
+ regs_badrindex:3,
+ reg_sz: 16,
+ }
+};
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct board_struct *)dev->board_ptr)
+
+/* Number of boards in boards[] */
+#define N_BOARDS (sizeof(boards) / sizeof(struct board_struct))
+#define REG_SZ (thisboard->reg_sz)
+#define REGS_BADRINDEX (thisboard->regs_badrindex)
+
+/* This is used by modprobe to translate PCI IDs to drivers. Should
+ * only be used for PCI and ISA-PnP devices */
+/* Please add your PCI vendor ID to comedidev.h, and it will be forwarded
+ * upstream. */
+static DEFINE_PCI_DEVICE_TABLE(pci_table) = {
+ {PCI_VENDOR_ID_COMPUTERBOARDS, PCI_ID_PCIM_DDA06_16, PCI_ANY_ID,
+ PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, pci_table);
+
+/* 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 struct comedi_device struct. */
+struct board_private_struct {
+ unsigned long registers; /* set by probe */
+ unsigned long dio_registers;
+ char attached_to_8255; /* boolean */
+ char attached_successfully; /* boolean */
+ /* would be useful for a PCI device */
+ struct pci_dev *pci_dev;
+
+#define MAX_AO_READBACK_CHANNELS 6
+ /* Used for AO readback */
+ unsigned int ao_readback[MAX_AO_READBACK_CHANNELS];
+
+};
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((struct board_private_struct *)dev->private)
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int detach(struct comedi_device * dev);
+static struct comedi_driver cb_pcimdda_driver = {
+ driver_name:"cb_pcimdda",
+ module:THIS_MODULE,
+ attach:attach,
+ detach:detach,
+};
+
+MODULE_AUTHOR("Calin A. Culianu <calin@rtlab.org>");
+MODULE_DESCRIPTION("Comedi low-level driver for the Computerboards PCIM-DDA "
+ "series. Currently only supports PCIM-DDA06-16 (which "
+ "also happens to be the only board in this series. :) ) ");
+MODULE_LICENSE("GPL");
+COMEDI_PCI_INITCLEANUP_NOMODULE(cb_pcimdda_driver, pci_table);
+
+static int ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+/*---------------------------------------------------------------------------
+ HELPER FUNCTION DECLARATIONS
+-----------------------------------------------------------------------------*/
+
+/* returns a maxdata value for a given n_bits */
+static inline unsigned int figure_out_maxdata(int bits)
+{
+ return (((unsigned int) 1 << bits) - 1);
+}
+
+/*
+ * Probes for a supported device.
+ *
+ * Prerequisite: private be allocated already inside dev
+ *
+ * If the device is found, it returns 0 and has the following side effects:
+ *
+ * o assigns a struct pci_dev * to dev->private->pci_dev
+ * o assigns a struct board * to dev->board_ptr
+ * o sets dev->private->registers
+ * o sets dev->private->dio_registers
+ *
+ * Otherwise, returns a -errno on error
+ */
+static int probe(struct comedi_device * dev, const struct comedi_devconfig * it);
+
+/*---------------------------------------------------------------------------
+ FUNCTION DEFINITIONS
+-----------------------------------------------------------------------------*/
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int err;
+
+/*
+ * Allocate the private structure area. alloc_private() is a
+ * convenient macro defined in comedidev.h.
+ * if this function fails (returns negative) then the private area is
+ * kfree'd by comedi
+ */
+ if (alloc_private(dev, sizeof(struct board_private_struct)) < 0)
+ return -ENOMEM;
+
+/*
+ * If you can probe the device to determine what device in a series
+ * it is, this is the place to do it. Otherwise, dev->board_ptr
+ * should already be initialized.
+ */
+ if ((err = probe(dev, it)))
+ return err;
+
+/* Output some info */
+ printk("comedi%d: %s: ", dev->minor, thisboard->name);
+
+/*
+ * Initialize dev->board_name. Note that we can use the "thisboard"
+ * macro now, since we just initialized it in the last line.
+ */
+ dev->board_name = thisboard->name;
+
+/*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_subdevices(dev, 2) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = thisboard->ao_chans;
+ s->maxdata = figure_out_maxdata(thisboard->ao_bits);
+ /* this is hard-coded here */
+ if (it->options[2]) {
+ s->range_table = &range_bipolar10;
+ } else {
+ s->range_table = &range_bipolar5;
+ }
+ s->insn_write = &ao_winsn;
+ s->insn_read = &ao_rinsn;
+
+ s = dev->subdevices + 1;
+ /* digital i/o subdevice */
+ if (thisboard->dio_chans) {
+ switch (thisboard->dio_method) {
+ case DIO_8255:
+ /* this is a straight 8255, so register us with the 8255 driver */
+ subdev_8255_init(dev, s, NULL, devpriv->dio_registers);
+ devpriv->attached_to_8255 = 1;
+ break;
+ case DIO_INTERNAL:
+ default:
+ printk("DIO_INTERNAL not implemented yet!\n");
+ return -ENXIO;
+ break;
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ devpriv->attached_successfully = 1;
+
+ printk("attached\n");
+
+ return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int detach(struct comedi_device * dev)
+{
+ if (devpriv) {
+
+ if (dev->subdevices && devpriv->attached_to_8255) {
+ /* de-register us from the 8255 driver */
+ subdev_8255_cleanup(dev, dev->subdevices + 2);
+ devpriv->attached_to_8255 = 0;
+ }
+
+ if (devpriv->pci_dev) {
+ if (devpriv->registers) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ }
+
+ if (devpriv->attached_successfully && thisboard)
+ printk("comedi%d: %s: detached\n", dev->minor,
+ thisboard->name);
+
+ }
+
+ return 0;
+}
+
+static int ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+ unsigned long offset = devpriv->registers + chan * 2;
+
+ /* Writing a list of values to an AO channel is probably not
+ * very useful, but that's how the interface is defined. */
+ for (i = 0; i < insn->n; i++) {
+ /* first, load the low byte */
+ outb((char)(data[i] & 0x00ff), offset);
+ /* next, write the high byte -- only after this is written is
+ the channel voltage updated in the DAC, unless
+ we're in simultaneous xfer mode (jumper on card)
+ then a rinsn is necessary to actually update the DAC --
+ see ao_rinsn() below... */
+ outb((char)(data[i] >> 8 & 0x00ff), offset + 1);
+
+ /* for testing only.. the actual rinsn SHOULD do an inw!
+ (see the stuff about simultaneous XFER mode on this board) */
+ devpriv->ao_readback[chan] = data[i];
+ }
+
+ /* return the number of samples read/written */
+ return i;
+}
+
+/* AO subdevices should have a read insn as well as a write insn.
+
+ Usually this means copying a value stored in devpriv->ao_readback.
+ However, since this board has this jumper setting called "Simultaneous
+ Xfer mode" (off by default), we will support it. Simultaneaous xfer
+ mode is accomplished by loading ALL the values you want for AO in all the
+ channels, then READing off one of the AO registers to initiate the
+ instantaneous simultaneous update of all DAC outputs, which makes
+ all AO channels update simultaneously. This is useful for some control
+ applications, I would imagine.
+*/
+static int ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++) {
+ inw(devpriv->registers + chan * 2);
+ /* should I set data[i] to the result of the actual read on the register
+ or the cached unsigned int in devpriv->ao_readback[]? */
+ data[i] = devpriv->ao_readback[chan];
+ }
+
+ return i;
+}
+
+/*---------------------------------------------------------------------------
+ HELPER FUNCTION DEFINITIONS
+-----------------------------------------------------------------------------*/
+
+/*
+ * Probes for a supported device.
+ *
+ * Prerequisite: private be allocated already inside dev
+ *
+ * If the device is found, it returns 0 and has the following side effects:
+ *
+ * o assigns a struct pci_dev * to dev->private->pci_dev
+ * o assigns a struct board * to dev->board_ptr
+ * o sets dev->private->registers
+ * o sets dev->private->dio_registers
+ *
+ * Otherwise, returns a -errno on error
+ */
+static int probe(struct comedi_device * dev, const struct comedi_devconfig * it)
+{
+ struct pci_dev *pcidev;
+ int index;
+ unsigned long registers;
+
+ for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pcidev != NULL;
+ pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
+ // is it not a computer boards card?
+ if (pcidev->vendor != PCI_VENDOR_ID_COMPUTERBOARDS)
+ continue;
+ // loop through cards supported by this driver
+ for (index = 0; index < N_BOARDS; index++) {
+ if (boards[index].device_id != pcidev->device)
+ continue;
+ // was a particular bus/slot requested?
+ if (it->options[0] || it->options[1]) {
+ // are we on the wrong bus/slot?
+ if (pcidev->bus->number != it->options[0] ||
+ PCI_SLOT(pcidev->devfn) !=
+ it->options[1]) {
+ continue;
+ }
+ }
+ /* found ! */
+
+ devpriv->pci_dev = pcidev;
+ dev->board_ptr = boards + index;
+ if (comedi_pci_enable(pcidev, thisboard->name)) {
+ printk("cb_pcimdda: Failed to enable PCI device and request regions\n");
+ return -EIO;
+ }
+ registers =
+ pci_resource_start(devpriv->pci_dev,
+ REGS_BADRINDEX);
+ devpriv->registers = registers;
+ devpriv->dio_registers
+ = devpriv->registers + thisboard->dio_offset;
+ return 0;
+ }
+ }
+
+ printk("cb_pcimdda: No supported ComputerBoards/MeasurementComputing "
+ "card found at the requested position\n");
+ return -ENODEV;
+}
diff --git a/drivers/staging/comedi/drivers/comedi_bond.c b/drivers/staging/comedi/drivers/comedi_bond.c
index 9e5496f4f1ae..1ee489864d3b 100644
--- a/drivers/staging/comedi/drivers/comedi_bond.c
+++ b/drivers/staging/comedi/drivers/comedi_bond.c
@@ -59,7 +59,7 @@ Configuration Options:
* Devices: a full list of the boards that attempt to be supported by
* the driver. Format is "(manufacturer) board name [comedi name]",
* where comedi_name is the name that is used to configure the board.
- * See the comment near board_name: in the comedi_driver structure
+ * See the comment near board_name: in the struct comedi_driver structure
* below. If (manufacturer) or [comedi name] is missing, the previous
* value is used.
* Author: you
@@ -142,7 +142,7 @@ static const struct BondingBoard bondingBoards[] = {
#define thisboard ((const struct BondingBoard *)dev->board_ptr)
struct BondedDevice {
- comedi_t *dev;
+ void *dev;
unsigned minor;
unsigned subdev;
unsigned subdev_type;
@@ -154,7 +154,7 @@ struct BondedDevice {
/* 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. */
+ feel free to suggest moving the variable to the struct comedi_device struct. */
struct Private {
# define MAX_BOARD_NAME 256
char name[MAX_BOARD_NAME];
@@ -171,21 +171,21 @@ struct Private {
#define devpriv ((struct Private *)dev->private)
/*
- * The comedi_driver structure tells the Comedi core module
+ * The struct comedi_driver structure tells the Comedi core module
* which functions to call to configure/deconfigure (attach/detach)
* the board, and also about the kernel module that contains
* the device code.
*/
-static int bonding_attach(comedi_device *dev, comedi_devconfig *it);
-static int bonding_detach(comedi_device *dev);
+static int bonding_attach(struct comedi_device *dev, struct comedi_devconfig *it);
+static int bonding_detach(struct comedi_device *dev);
/** Build Private array of all devices.. */
-static int doDevConfig(comedi_device *dev, comedi_devconfig *it);
-static void doDevUnconfig(comedi_device *dev);
+static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it);
+static void doDevUnconfig(struct comedi_device *dev);
/* Ugly implementation of realloc that always copies memory around -- I'm lazy,
* what can I say? I like to do wasteful memcopies.. :) */
static void *Realloc(const void *ptr, size_t len, size_t old_len);
-static comedi_driver driver_bonding = {
+static struct comedi_driver driver_bonding = {
.driver_name = MODULE_NAME,
.module = THIS_MODULE,
.attach = bonding_attach,
@@ -213,10 +213,10 @@ static comedi_driver driver_bonding = {
.num_names = sizeof(bondingBoards) / sizeof(struct BondingBoard),
};
-static int bonding_dio_insn_bits(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data);
-static int bonding_dio_insn_config(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data);
+static int bonding_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int bonding_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
/*
* Attach is called by the Comedi core to configure the driver
@@ -224,9 +224,9 @@ static int bonding_dio_insn_config(comedi_device *dev, comedi_subdevice *s,
* in the driver structure, dev->board_ptr contains that
* address.
*/
-static int bonding_attach(comedi_device *dev, comedi_devconfig *it)
+static int bonding_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
- comedi_subdevice *s;
+ struct comedi_subdevice *s;
LOG_MSG("comedi%d\n", dev->minor);
@@ -281,7 +281,7 @@ static int bonding_attach(comedi_device *dev, comedi_devconfig *it)
* allocated by _attach(). dev->private and dev->subdevices are
* deallocated automatically by the core.
*/
-static int bonding_detach(comedi_device *dev)
+static int bonding_detach(struct comedi_device *dev)
{
LOG_MSG("comedi%d: remove\n", dev->minor);
doDevUnconfig(dev);
@@ -293,10 +293,10 @@ static int bonding_detach(comedi_device *dev)
* useful to applications if you implement the insn_bits interface.
* This allows packed reading/writing of the DIO channels. The
* comedi core can convert between insn_bits and insn_read/write */
-static int bonding_dio_insn_bits(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int bonding_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
-#define LSAMPL_BITS (sizeof(lsampl_t)*8)
+#define LSAMPL_BITS (sizeof(unsigned int)*8)
unsigned nchans = LSAMPL_BITS, num_done = 0, i;
if (insn->n != 2)
return -EINVAL;
@@ -312,12 +312,12 @@ static int bonding_dio_insn_bits(comedi_device *dev, comedi_subdevice *s,
to this subdevice.. need to shift them to zero position of
course. */
/* Bits corresponding to this subdev. */
- lsampl_t subdevMask = ((1 << bdev->nchans) - 1);
- lsampl_t writeMask, dataBits;
+ unsigned int subdevMask = ((1 << bdev->nchans) - 1);
+ unsigned int writeMask, dataBits;
/* Argh, we have >= LSAMPL_BITS chans.. take all bits */
if (bdev->nchans >= LSAMPL_BITS)
- subdevMask = (lsampl_t) (-1);
+ subdevMask = (unsigned int) (-1);
writeMask = (data[0] >> num_done) & subdevMask;
dataBits = (data[1] >> num_done) & subdevMask;
@@ -340,8 +340,8 @@ static int bonding_dio_insn_bits(comedi_device *dev, comedi_subdevice *s,
return insn->n;
}
-static int bonding_dio_insn_config(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int bonding_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int chan = CR_CHAN(insn->chanspec), ret, io_bits = s->io_bits;
unsigned int io;
@@ -394,10 +394,10 @@ static void *Realloc(const void *oldmem, size_t newlen, size_t oldlen)
return newmem;
}
-static int doDevConfig(comedi_device *dev, comedi_devconfig *it)
+static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it)
{
int i;
- comedi_t *devs_opened[COMEDI_NUM_BOARD_MINORS];
+ void *devs_opened[COMEDI_NUM_BOARD_MINORS];
memset(devs_opened, 0, sizeof(devs_opened));
devpriv->name[0] = 0;;
@@ -406,7 +406,7 @@ static int doDevConfig(comedi_device *dev, comedi_devconfig *it)
for (i = 0; i < COMEDI_NDEVCONFOPTS && (!i || it->options[i]); ++i) {
char file[] = "/dev/comediXXXXXX";
int minor = it->options[i];
- comedi_t *d;
+ void *d;
int sdev = -1, nchans, tmp;
struct BondedDevice *bdev = NULL;
@@ -497,7 +497,7 @@ static int doDevConfig(comedi_device *dev, comedi_devconfig *it)
return 1;
}
-static void doDevUnconfig(comedi_device *dev)
+static void doDevUnconfig(struct comedi_device *dev)
{
unsigned long devs_closed = 0;
diff --git a/drivers/staging/comedi/drivers/comedi_fc.c b/drivers/staging/comedi/drivers/comedi_fc.c
index cd74dbe5417d..9fa4cdc84d44 100644
--- a/drivers/staging/comedi/drivers/comedi_fc.c
+++ b/drivers/staging/comedi/drivers/comedi_fc.c
@@ -28,10 +28,10 @@
#include "comedi_fc.h"
-static void increment_scan_progress(comedi_subdevice *subd,
+static void increment_scan_progress(struct comedi_subdevice *subd,
unsigned int num_bytes)
{
- comedi_async *async = subd->async;
+ struct comedi_async *async = subd->async;
unsigned int scan_length = cfc_bytes_per_scan(subd);
async->scan_progress += num_bytes;
@@ -42,10 +42,10 @@ static void increment_scan_progress(comedi_subdevice *subd,
}
/* Writes an array of data points to comedi's buffer */
-unsigned int cfc_write_array_to_buffer(comedi_subdevice *subd, void *data,
+unsigned int cfc_write_array_to_buffer(struct comedi_subdevice *subd, void *data,
unsigned int num_bytes)
{
- comedi_async *async = subd->async;
+ struct comedi_async *async = subd->async;
unsigned int retval;
if (num_bytes == 0)
@@ -67,10 +67,10 @@ unsigned int cfc_write_array_to_buffer(comedi_subdevice *subd, void *data,
}
EXPORT_SYMBOL(cfc_write_array_to_buffer);
-unsigned int cfc_read_array_from_buffer(comedi_subdevice *subd, void *data,
+unsigned int cfc_read_array_from_buffer(struct comedi_subdevice *subd, void *data,
unsigned int num_bytes)
{
- comedi_async *async = subd->async;
+ struct comedi_async *async = subd->async;
if (num_bytes == 0)
return 0;
@@ -85,7 +85,7 @@ unsigned int cfc_read_array_from_buffer(comedi_subdevice *subd, void *data,
}
EXPORT_SYMBOL(cfc_read_array_from_buffer);
-unsigned int cfc_handle_events(comedi_device *dev, comedi_subdevice *subd)
+unsigned int cfc_handle_events(struct comedi_device *dev, struct comedi_subdevice *subd)
{
unsigned int events = subd->async->events;
diff --git a/drivers/staging/comedi/drivers/comedi_fc.h b/drivers/staging/comedi/drivers/comedi_fc.h
index 6952fe20f273..494ae3f2aff6 100644
--- a/drivers/staging/comedi/drivers/comedi_fc.h
+++ b/drivers/staging/comedi/drivers/comedi_fc.h
@@ -30,30 +30,30 @@
#include "../comedidev.h"
/* Writes an array of data points to comedi's buffer */
-extern unsigned int cfc_write_array_to_buffer(comedi_subdevice *subd,
+extern unsigned int cfc_write_array_to_buffer(struct comedi_subdevice *subd,
void *data,
unsigned int num_bytes);
-static inline unsigned int cfc_write_to_buffer(comedi_subdevice *subd,
- sampl_t data)
+static inline unsigned int cfc_write_to_buffer(struct comedi_subdevice *subd,
+ short data)
{
return cfc_write_array_to_buffer(subd, &data, sizeof(data));
};
-static inline unsigned int cfc_write_long_to_buffer(comedi_subdevice *subd,
- lsampl_t data)
+static inline unsigned int cfc_write_long_to_buffer(struct comedi_subdevice *subd,
+ unsigned int data)
{
return cfc_write_array_to_buffer(subd, &data, sizeof(data));
};
-extern unsigned int cfc_read_array_from_buffer(comedi_subdevice *subd,
+extern unsigned int cfc_read_array_from_buffer(struct comedi_subdevice *subd,
void *data,
unsigned int num_bytes);
-extern unsigned int cfc_handle_events(comedi_device *dev,
- comedi_subdevice *subd);
+extern unsigned int cfc_handle_events(struct comedi_device *dev,
+ struct comedi_subdevice *subd);
-static inline unsigned int cfc_bytes_per_scan(comedi_subdevice *subd)
+static inline unsigned int cfc_bytes_per_scan(struct comedi_subdevice *subd)
{
int num_samples;
int bits_per_sample;
diff --git a/drivers/staging/comedi/drivers/comedi_parport.c b/drivers/staging/comedi/drivers/comedi_parport.c
index ba838fffa29e..a23339155ffc 100644
--- a/drivers/staging/comedi/drivers/comedi_parport.c
+++ b/drivers/staging/comedi/drivers/comedi_parport.c
@@ -90,9 +90,9 @@ pin, which can be used to wake up tasks.
#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);
-static comedi_driver driver_parport = {
+static int parport_attach(struct comedi_device *dev, struct comedi_devconfig *it);
+static int parport_detach(struct comedi_device *dev);
+static struct comedi_driver driver_parport = {
.driver_name = "comedi_parport",
.module = THIS_MODULE,
.attach = parport_attach,
@@ -108,8 +108,8 @@ struct parport_private {
};
#define devpriv ((struct parport_private *)(dev->private))
-static int parport_insn_a(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int parport_insn_a(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
if (data[0]) {
devpriv->a_data &= ~data[0];
@@ -123,8 +123,8 @@ static int parport_insn_a(comedi_device *dev, comedi_subdevice *s,
return 2;
}
-static int parport_insn_config_a(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int parport_insn_config_a(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
if (data[0]) {
s->io_bits = 0xff;
@@ -138,8 +138,8 @@ static int parport_insn_config_a(comedi_device *dev, comedi_subdevice *s,
return 1;
}
-static int parport_insn_b(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int parport_insn_b(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
if (data[0]) {
/* should writes be ignored? */
@@ -151,8 +151,8 @@ static int parport_insn_b(comedi_device *dev, comedi_subdevice *s,
return 2;
}
-static int parport_insn_c(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int parport_insn_c(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
data[0] &= 0x0f;
if (data[0]) {
@@ -167,8 +167,8 @@ static int parport_insn_c(comedi_device *dev, comedi_subdevice *s,
return 2;
}
-static int parport_intr_insn(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int parport_intr_insn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
if (insn->n < 1)
return -EINVAL;
@@ -177,8 +177,8 @@ static int parport_intr_insn(comedi_device *dev, comedi_subdevice *s,
return 2;
}
-static int parport_intr_cmdtest(comedi_device *dev, comedi_subdevice *s,
- comedi_cmd *cmd)
+static int parport_intr_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
{
int err = 0;
int tmp;
@@ -252,7 +252,7 @@ static int parport_intr_cmdtest(comedi_device *dev, comedi_subdevice *s,
return 0;
}
-static int parport_intr_cmd(comedi_device *dev, comedi_subdevice *s)
+static int parport_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
devpriv->c_data |= 0x10;
outb(devpriv->c_data, dev->iobase + PARPORT_C);
@@ -262,7 +262,7 @@ static int parport_intr_cmd(comedi_device *dev, comedi_subdevice *s)
return 0;
}
-static int parport_intr_cancel(comedi_device *dev, comedi_subdevice *s)
+static int parport_intr_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
printk(KERN_DEBUG "parport_intr_cancel()\n");
@@ -276,8 +276,8 @@ static int parport_intr_cancel(comedi_device *dev, comedi_subdevice *s)
static irqreturn_t parport_interrupt(int irq, void *d PT_REGS_ARG)
{
- comedi_device *dev = d;
- comedi_subdevice *s = dev->subdevices + 3;
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 3;
if (!devpriv->enable_irq) {
printk(KERN_ERR "comedi_parport: bogus irq, ignored\n");
@@ -291,12 +291,12 @@ static irqreturn_t parport_interrupt(int irq, void *d PT_REGS_ARG)
return IRQ_HANDLED;
}
-static int parport_attach(comedi_device *dev, comedi_devconfig *it)
+static int parport_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
int ret;
unsigned int irq;
unsigned long iobase;
- comedi_subdevice *s;
+ struct comedi_subdevice *s;
iobase = it->options[0];
printk(KERN_INFO "comedi%d: parport: 0x%04lx ", dev->minor, iobase);
@@ -376,7 +376,7 @@ static int parport_attach(comedi_device *dev, comedi_devconfig *it)
return 1;
}
-static int parport_detach(comedi_device *dev)
+static int parport_detach(struct comedi_device *dev)
{
printk("comedi%d: parport: remove\n", dev->minor);
diff --git a/drivers/staging/comedi/drivers/comedi_rt_timer.c b/drivers/staging/comedi/drivers/comedi_rt_timer.c
new file mode 100644
index 000000000000..f40c8cffd4bb
--- /dev/null
+++ b/drivers/staging/comedi/drivers/comedi_rt_timer.c
@@ -0,0 +1,728 @@
+/*
+ comedi/drivers/comedi_rt_timer.c
+ virtual driver for using RTL timing sources
+
+ Authors: David A. Schleef, Frank M. Hess
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999,2001 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+**************************************************************************
+*/
+/*
+Driver: comedi_rt_timer
+Description: Command emulator using real-time tasks
+Author: ds, fmhess
+Devices:
+Status: works
+
+This driver requires RTAI or RTLinux to work correctly. It doesn't
+actually drive hardware directly, but calls other drivers and uses
+a real-time task to emulate commands for drivers and devices that
+are incapable of native commands. Thus, you can get accurately
+timed I/O on any device.
+
+Since the timing is all done in software, sampling jitter is much
+higher than with a device that has an on-board timer, and maximum
+sample rate is much lower.
+
+Configuration options:
+ [0] - minor number of device you wish to emulate commands for
+ [1] - subdevice number you wish to emulate commands for
+*/
+/*
+TODO:
+ Support for digital io commands could be added, except I can't see why
+ anyone would want to use them
+ What happens if device we are emulating for is de-configured?
+*/
+
+#include "../comedidev.h"
+#include "../comedilib.h"
+
+#include "comedi_fc.h"
+
+#ifdef CONFIG_COMEDI_RTL_V1
+#include <rtl_sched.h>
+#include <asm/rt_irq.h>
+#endif
+#ifdef CONFIG_COMEDI_RTL
+#include <rtl.h>
+#include <rtl_sched.h>
+#include <rtl_compat.h>
+#include <asm/div64.h>
+
+#ifndef RTLINUX_VERSION_CODE
+#define RTLINUX_VERSION_CODE 0
+#endif
+#ifndef RTLINUX_VERSION
+#define RTLINUX_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+// begin hack to workaround broken HRT_TO_8254() function on rtlinux
+#if RTLINUX_VERSION_CODE <= RTLINUX_VERSION(3,0,100)
+// this function sole purpose is to divide a long long by 838
+static inline RTIME nano2count(long long ns)
+{
+ do_div(ns, 838);
+ return ns;
+}
+
+#ifdef rt_get_time()
+#undef rt_get_time()
+#endif
+#define rt_get_time() nano2count(gethrtime())
+
+#else
+
+#define nano2count(x) HRT_TO_8254(x)
+#endif
+// end hack
+
+// rtl-rtai compatibility
+#define rt_task_wait_period() rt_task_wait()
+#define rt_pend_linux_srq(irq) rtl_global_pend_irq(irq)
+#define rt_free_srq(irq) rtl_free_soft_irq(irq)
+#define rt_request_srq(x,y,z) rtl_get_soft_irq(y,"timer")
+#define rt_task_init(a,b,c,d,e,f,g) rt_task_init(a,b,c,d,(e)+1)
+#define rt_task_resume(x) rt_task_wakeup(x)
+#define rt_set_oneshot_mode()
+#define start_rt_timer(x)
+#define stop_rt_timer()
+
+#endif
+#ifdef CONFIG_COMEDI_RTAI
+#include <rtai.h>
+#include <rtai_sched.h>
+
+#if RTAI_VERSION_CODE < RTAI_MANGLE_VERSION(3,3,0)
+#define comedi_rt_task_context_t int
+#else
+#define comedi_rt_task_context_t long
+#endif
+
+#endif
+
+/* This defines the fastest speed we will emulate. Note that
+ * without a watchdog (like in RTAI), we could easily overrun our
+ * task period because analog input tends to be slow. */
+#define SPEED_LIMIT 100000 /* in nanoseconds */
+
+static int timer_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int timer_detach(struct comedi_device * dev);
+static int timer_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trig_num);
+static int timer_start_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+
+static struct comedi_driver driver_timer = {
+ module:THIS_MODULE,
+ driver_name:"comedi_rt_timer",
+ attach:timer_attach,
+ detach:timer_detach,
+// open: timer_open,
+};
+
+COMEDI_INITCLEANUP(driver_timer);
+
+struct timer_private {
+ comedi_t *device; // device we are emulating commands for
+ int subd; // subdevice we are emulating commands for
+ RT_TASK *rt_task; // rt task that starts scans
+ RT_TASK *scan_task; // rt task that controls conversion timing in a scan
+ /* io_function can point to either an input or output function
+ * depending on what kind of subdevice we are emulating for */
+ int (*io_function) (struct comedi_device * dev, struct comedi_cmd * cmd,
+ unsigned int index);
+ // RTIME has units of 1 = 838 nanoseconds
+ // time at which first scan started, used to check scan timing
+ RTIME start;
+ // time between scans
+ RTIME scan_period;
+ // time between conversions in a scan
+ RTIME convert_period;
+ // flags
+ volatile int stop; // indicates we should stop
+ volatile int rt_task_active; // indicates rt_task is servicing a struct comedi_cmd
+ volatile int scan_task_active; // indicates scan_task is servicing a struct comedi_cmd
+ unsigned timer_running:1;
+};
+#define devpriv ((struct timer_private *)dev->private)
+
+static int timer_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ devpriv->stop = 1;
+
+ return 0;
+}
+
+// checks for scan timing error
+inline static int check_scan_timing(struct comedi_device * dev,
+ unsigned long long scan)
+{
+ RTIME now, timing_error;
+
+ now = rt_get_time();
+ timing_error = now - (devpriv->start + scan * devpriv->scan_period);
+ if (timing_error > devpriv->scan_period) {
+ comedi_error(dev, "timing error");
+ rt_printk("scan started %i ns late\n", timing_error * 838);
+ return -1;
+ }
+
+ return 0;
+}
+
+// checks for conversion timing error
+inline static int check_conversion_timing(struct comedi_device * dev,
+ RTIME scan_start, unsigned int conversion)
+{
+ RTIME now, timing_error;
+
+ now = rt_get_time();
+ timing_error =
+ now - (scan_start + conversion * devpriv->convert_period);
+ if (timing_error > devpriv->convert_period) {
+ comedi_error(dev, "timing error");
+ rt_printk("conversion started %i ns late\n",
+ timing_error * 838);
+ return -1;
+ }
+
+ return 0;
+}
+
+// devpriv->io_function for an input subdevice
+static int timer_data_read(struct comedi_device * dev, struct comedi_cmd * cmd,
+ unsigned int index)
+{
+ struct comedi_subdevice *s = dev->read_subdev;
+ int ret;
+ unsigned int data;
+
+ ret = comedi_data_read(devpriv->device, devpriv->subd,
+ CR_CHAN(cmd->chanlist[index]),
+ CR_RANGE(cmd->chanlist[index]),
+ CR_AREF(cmd->chanlist[index]), &data);
+ if (ret < 0) {
+ comedi_error(dev, "read error");
+ return -EIO;
+ }
+ if (s->flags & SDF_LSAMPL) {
+ cfc_write_long_to_buffer(s, data);
+ } else {
+ comedi_buf_put(s->async, data);
+ }
+
+ return 0;
+}
+
+// devpriv->io_function for an output subdevice
+static int timer_data_write(struct comedi_device * dev, struct comedi_cmd * cmd,
+ unsigned int index)
+{
+ struct comedi_subdevice *s = dev->write_subdev;
+ unsigned int num_bytes;
+ short data;
+ unsigned int long_data;
+ int ret;
+
+ if (s->flags & SDF_LSAMPL) {
+ num_bytes =
+ cfc_read_array_from_buffer(s, &long_data,
+ sizeof(long_data));
+ } else {
+ num_bytes = cfc_read_array_from_buffer(s, &data, sizeof(data));
+ long_data = data;
+ }
+
+ if (num_bytes == 0) {
+ comedi_error(dev, "buffer underrun");
+ return -EAGAIN;
+ }
+ ret = comedi_data_write(devpriv->device, devpriv->subd,
+ CR_CHAN(cmd->chanlist[index]),
+ CR_RANGE(cmd->chanlist[index]),
+ CR_AREF(cmd->chanlist[index]), long_data);
+ if (ret < 0) {
+ comedi_error(dev, "write error");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+// devpriv->io_function for DIO subdevices
+static int timer_dio_read(struct comedi_device * dev, struct comedi_cmd * cmd,
+ unsigned int index)
+{
+ struct comedi_subdevice *s = dev->read_subdev;
+ int ret;
+ unsigned int data;
+
+ ret = comedi_dio_bitfield(devpriv->device, devpriv->subd, 0, &data);
+ if (ret < 0) {
+ comedi_error(dev, "read error");
+ return -EIO;
+ }
+
+ if (s->flags & SDF_LSAMPL)
+ cfc_write_long_to_buffer(s, data);
+ else
+ cfc_write_to_buffer(s, data);
+
+ return 0;
+}
+
+// performs scans
+static void scan_task_func(comedi_rt_task_context_t d)
+{
+ struct comedi_device *dev = (struct comedi_device *) d;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ int i, ret;
+ unsigned long long n;
+ RTIME scan_start;
+
+ // every struct comedi_cmd causes one execution of while loop
+ while (1) {
+ devpriv->scan_task_active = 1;
+ // each for loop completes one scan
+ for (n = 0; n < cmd->stop_arg || cmd->stop_src == TRIG_NONE;
+ n++) {
+ if (n) {
+ // suspend task until next scan
+ ret = rt_task_suspend(devpriv->scan_task);
+ if (ret < 0) {
+ comedi_error(dev,
+ "error suspending scan task");
+ async->events |= COMEDI_CB_ERROR;
+ goto cleanup;
+ }
+ }
+ // check if stop flag was set (by timer_cancel())
+ if (devpriv->stop)
+ goto cleanup;
+ ret = check_scan_timing(dev, n);
+ if (ret < 0) {
+ async->events |= COMEDI_CB_ERROR;
+ goto cleanup;
+ }
+ scan_start = rt_get_time();
+ for (i = 0; i < cmd->scan_end_arg; i++) {
+ // conversion timing
+ if (cmd->convert_src == TRIG_TIMER && i) {
+ rt_task_wait_period();
+ ret = check_conversion_timing(dev,
+ scan_start, i);
+ if (ret < 0) {
+ async->events |=
+ COMEDI_CB_ERROR;
+ goto cleanup;
+ }
+ }
+ ret = devpriv->io_function(dev, cmd, i);
+ if (ret < 0) {
+ async->events |= COMEDI_CB_ERROR;
+ goto cleanup;
+ }
+ }
+ s->async->events |= COMEDI_CB_BLOCK;
+ comedi_event(dev, s);
+ s->async->events = 0;
+ }
+
+ cleanup:
+
+ comedi_unlock(devpriv->device, devpriv->subd);
+ async->events |= COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ async->events = 0;
+ devpriv->scan_task_active = 0;
+ // suspend task until next struct comedi_cmd
+ rt_task_suspend(devpriv->scan_task);
+ }
+}
+
+static void timer_task_func(comedi_rt_task_context_t d)
+{
+ struct comedi_device *dev = (struct comedi_device *) d;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int ret;
+ unsigned long long n;
+
+ // every struct comedi_cmd causes one execution of while loop
+ while (1) {
+ devpriv->rt_task_active = 1;
+ devpriv->scan_task_active = 1;
+ devpriv->start = rt_get_time();
+
+ for (n = 0; n < cmd->stop_arg || cmd->stop_src == TRIG_NONE;
+ n++) {
+ // scan timing
+ if (n)
+ rt_task_wait_period();
+ if (devpriv->scan_task_active == 0) {
+ goto cleanup;
+ }
+ ret = rt_task_make_periodic(devpriv->scan_task,
+ devpriv->start + devpriv->scan_period * n,
+ devpriv->convert_period);
+ if (ret < 0) {
+ comedi_error(dev, "bug!");
+ }
+ }
+
+ cleanup:
+
+ devpriv->rt_task_active = 0;
+ // suspend until next struct comedi_cmd
+ rt_task_suspend(devpriv->rt_task);
+ }
+}
+
+static int timer_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ struct comedi_insn xinsn = *insn;
+
+ xinsn.data = data;
+ xinsn.subdev = devpriv->subd;
+
+ return comedi_do_insn(devpriv->device, &xinsn);
+}
+
+static int cmdtest_helper(struct comedi_cmd * cmd,
+ unsigned int start_src,
+ unsigned int scan_begin_src,
+ unsigned int convert_src,
+ unsigned int scan_end_src, unsigned int stop_src)
+{
+ int err = 0;
+ int tmp;
+
+ tmp = cmd->start_src;
+ cmd->start_src &= start_src;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= scan_begin_src;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= convert_src;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= scan_end_src;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= stop_src;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ return err;
+}
+
+static int timer_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ unsigned int start_src = 0;
+
+ if (s->type == COMEDI_SUBD_AO)
+ start_src = TRIG_INT;
+ else
+ start_src = TRIG_NOW;
+
+ err = cmdtest_helper(cmd, start_src, /* start_src */
+ TRIG_TIMER | TRIG_FOLLOW, /* scan_begin_src */
+ TRIG_NOW | TRIG_TIMER, /* convert_src */
+ TRIG_COUNT, /* scan_end_src */
+ TRIG_COUNT | TRIG_NONE); /* stop_src */
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually
+ * compatible */
+
+ if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_INT)
+ err++;
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_FOLLOW)
+ err++;
+ if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_NOW)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+ if (cmd->scan_begin_src == TRIG_FOLLOW
+ && cmd->convert_src != TRIG_TIMER)
+ err++;
+ if (cmd->convert_src == TRIG_NOW && cmd->scan_begin_src != TRIG_TIMER)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+ // limit frequency, this is fairly arbitrary
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->scan_begin_arg < SPEED_LIMIT) {
+ cmd->scan_begin_arg = SPEED_LIMIT;
+ err++;
+ }
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < SPEED_LIMIT) {
+ cmd->convert_arg = SPEED_LIMIT;
+ err++;
+ }
+ }
+ // make sure conversion and scan frequencies are compatible
+ if (cmd->convert_src == TRIG_TIMER && cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->convert_arg * cmd->scan_end_arg > cmd->scan_begin_arg) {
+ cmd->scan_begin_arg =
+ cmd->convert_arg * cmd->scan_end_arg;
+ err++;
+ }
+ }
+ if (err)
+ return 3;
+
+ /* step 4: fix up and arguments */
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int timer_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int ret;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ /* hack attack: drivers are not supposed to do this: */
+ dev->rt = 1;
+
+ // make sure tasks have finished cleanup of last struct comedi_cmd
+ if (devpriv->rt_task_active || devpriv->scan_task_active)
+ return -EBUSY;
+
+ ret = comedi_lock(devpriv->device, devpriv->subd);
+ if (ret < 0) {
+ comedi_error(dev, "failed to obtain lock");
+ return ret;
+ }
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ devpriv->scan_period = nano2count(cmd->scan_begin_arg);
+ break;
+ case TRIG_FOLLOW:
+ devpriv->scan_period =
+ nano2count(cmd->convert_arg * cmd->scan_end_arg);
+ break;
+ default:
+ comedi_error(dev, "bug setting scan period!");
+ return -1;
+ break;
+ }
+ switch (cmd->convert_src) {
+ case TRIG_TIMER:
+ devpriv->convert_period = nano2count(cmd->convert_arg);
+ break;
+ case TRIG_NOW:
+ devpriv->convert_period = 1;
+ break;
+ default:
+ comedi_error(dev, "bug setting conversion period!");
+ return -1;
+ break;
+ }
+
+ if (cmd->start_src == TRIG_NOW)
+ return timer_start_cmd(dev, s);
+
+ s->async->inttrig = timer_inttrig;
+
+ return 0;
+}
+
+static int timer_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trig_num)
+{
+ if (trig_num != 0)
+ return -EINVAL;
+
+ s->async->inttrig = NULL;
+
+ return timer_start_cmd(dev, s);
+}
+
+static int timer_start_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ RTIME now, delay, period;
+ int ret;
+
+ devpriv->stop = 0;
+ s->async->events = 0;
+
+ if (cmd->start_src == TRIG_NOW)
+ delay = nano2count(cmd->start_arg);
+ else
+ delay = 0;
+
+ now = rt_get_time();
+ /* Using 'period' this way gets around some weird bug in gcc-2.95.2
+ * that generates the compile error 'internal error--unrecognizable insn'
+ * when rt_task_make_period() is called (observed with rtlinux-3.1, linux-2.2.19).
+ * - fmhess */
+ period = devpriv->scan_period;
+ ret = rt_task_make_periodic(devpriv->rt_task, now + delay, period);
+ if (ret < 0) {
+ comedi_error(dev, "error starting rt_task");
+ return ret;
+ }
+ return 0;
+}
+
+static int timer_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int ret;
+ struct comedi_subdevice *s, *emul_s;
+ struct comedi_device *emul_dev;
+ /* These should probably be devconfig options[] */
+ const int timer_priority = 4;
+ const int scan_priority = timer_priority + 1;
+ char path[20];
+
+ printk("comedi%d: timer: ", dev->minor);
+
+ dev->board_name = "timer";
+
+ if ((ret = alloc_subdevices(dev, 1)) < 0)
+ return ret;
+ if ((ret = alloc_private(dev, sizeof(struct timer_private))) < 0)
+ return ret;
+
+ sprintf(path, "/dev/comedi%d", it->options[0]);
+ devpriv->device = comedi_open(path);
+ devpriv->subd = it->options[1];
+
+ printk("emulating commands for minor %i, subdevice %d\n",
+ it->options[0], devpriv->subd);
+
+ emul_dev = devpriv->device;
+ emul_s = emul_dev->subdevices + devpriv->subd;
+
+ // input or output subdevice
+ s = dev->subdevices + 0;
+ s->type = emul_s->type;
+ s->subdev_flags = emul_s->subdev_flags; /* SDF_GROUND (to fool check_driver) */
+ s->n_chan = emul_s->n_chan;
+ s->len_chanlist = 1024;
+ s->do_cmd = timer_cmd;
+ s->do_cmdtest = timer_cmdtest;
+ s->cancel = timer_cancel;
+ s->maxdata = emul_s->maxdata;
+ s->range_table = emul_s->range_table;
+ s->range_table_list = emul_s->range_table_list;
+ switch (emul_s->type) {
+ case COMEDI_SUBD_AI:
+ s->insn_read = timer_insn;
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ devpriv->io_function = timer_data_read;
+ break;
+ case COMEDI_SUBD_AO:
+ s->insn_write = timer_insn;
+ s->insn_read = timer_insn;
+ dev->write_subdev = s;
+ s->subdev_flags |= SDF_CMD_WRITE;
+ devpriv->io_function = timer_data_write;
+ break;
+ case COMEDI_SUBD_DIO:
+ s->insn_write = timer_insn;
+ s->insn_read = timer_insn;
+ s->insn_bits = timer_insn;
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ devpriv->io_function = timer_dio_read;
+ break;
+ default:
+ comedi_error(dev, "failed to determine subdevice type!");
+ return -EINVAL;
+ }
+
+ rt_set_oneshot_mode();
+ start_rt_timer(1);
+ devpriv->timer_running = 1;
+
+ devpriv->rt_task = kzalloc(sizeof(RT_TASK), GFP_KERNEL);
+
+ // initialize real-time tasks
+ ret = rt_task_init(devpriv->rt_task, timer_task_func,
+ (comedi_rt_task_context_t) dev, 3000, timer_priority, 0, 0);
+ if (ret < 0) {
+ comedi_error(dev, "error initalizing rt_task");
+ kfree(devpriv->rt_task);
+ devpriv->rt_task = 0;
+ return ret;
+ }
+
+ devpriv->scan_task = kzalloc(sizeof(RT_TASK), GFP_KERNEL);
+
+ ret = rt_task_init(devpriv->scan_task, scan_task_func,
+ (comedi_rt_task_context_t) dev, 3000, scan_priority, 0, 0);
+ if (ret < 0) {
+ comedi_error(dev, "error initalizing scan_task");
+ kfree(devpriv->scan_task);
+ devpriv->scan_task = 0;
+ return ret;
+ }
+
+ return 1;
+}
+
+// free allocated resources
+static int timer_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: timer: remove\n", dev->minor);
+
+ if (devpriv) {
+ if (devpriv->rt_task) {
+ rt_task_delete(devpriv->rt_task);
+ kfree(devpriv->rt_task);
+ }
+ if (devpriv->scan_task) {
+ rt_task_delete(devpriv->scan_task);
+ kfree(devpriv->scan_task);
+ }
+ if (devpriv->timer_running)
+ stop_rt_timer();
+ if (devpriv->device)
+ comedi_close(devpriv->device);
+ }
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/comedi_test.c b/drivers/staging/comedi/drivers/comedi_test.c
index 4b4c37d07482..e679328a4f8f 100644
--- a/drivers/staging/comedi/drivers/comedi_test.c
+++ b/drivers/staging/comedi/drivers/comedi_test.c
@@ -89,13 +89,13 @@ struct waveform_private {
unsigned int scan_period; /* scan period in usec */
unsigned int convert_period; /* conversion period in usec */
unsigned timer_running:1;
- lsampl_t ao_loopbacks[N_CHANS];
+ unsigned int ao_loopbacks[N_CHANS];
};
#define devpriv ((struct waveform_private *)dev->private)
-static int waveform_attach(comedi_device *dev, comedi_devconfig *it);
-static int waveform_detach(comedi_device *dev);
-static comedi_driver driver_waveform = {
+static int waveform_attach(struct comedi_device *dev, struct comedi_devconfig *it);
+static int waveform_detach(struct comedi_device *dev);
+static struct comedi_driver driver_waveform = {
.driver_name = "comedi_test",
.module = THIS_MODULE,
.attach = waveform_attach,
@@ -107,28 +107,28 @@ static comedi_driver driver_waveform = {
COMEDI_INITCLEANUP(driver_waveform);
-static int waveform_ai_cmdtest(comedi_device *dev, comedi_subdevice *s,
- comedi_cmd *cmd);
-static int waveform_ai_cmd(comedi_device *dev, comedi_subdevice *s);
-static int waveform_ai_cancel(comedi_device *dev, comedi_subdevice *s);
-static int waveform_ai_insn_read(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data);
-static int waveform_ao_insn_write(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data);
-static sampl_t fake_sawtooth(comedi_device *dev, unsigned int range,
+static int waveform_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd);
+static int waveform_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
+static int waveform_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
+static int waveform_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int waveform_ao_insn_write(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static short fake_sawtooth(struct comedi_device *dev, unsigned int range,
unsigned long current_time);
-static sampl_t fake_squarewave(comedi_device *dev, unsigned int range,
+static short fake_squarewave(struct comedi_device *dev, unsigned int range,
unsigned long current_time);
-static sampl_t fake_flatline(comedi_device *dev, unsigned int range,
+static short fake_flatline(struct comedi_device *dev, unsigned int range,
unsigned long current_time);
-static sampl_t fake_waveform(comedi_device *dev, unsigned int channel,
+static short fake_waveform(struct comedi_device *dev, unsigned int channel,
unsigned int range, unsigned long current_time);
/* 1000 nanosec in a microsec */
static const int nano_per_micro = 1000;
/* fake analog input ranges */
-static const comedi_lrange waveform_ai_ranges = {
+static const struct comedi_lrange waveform_ai_ranges = {
2,
{
BIP_RANGE(10),
@@ -143,9 +143,9 @@ static const comedi_lrange waveform_ai_ranges = {
*/
static void waveform_ai_interrupt(unsigned long arg)
{
- comedi_device *dev = (comedi_device *) arg;
- comedi_async *async = dev->read_subdev->async;
- comedi_cmd *cmd = &async->cmd;
+ struct comedi_device *dev = (struct comedi_device *) arg;
+ struct comedi_async *async = dev->read_subdev->async;
+ struct comedi_cmd *cmd = &async->cmd;
unsigned int i, j;
/* all times in microsec */
unsigned long elapsed_time;
@@ -192,9 +192,9 @@ static void waveform_ai_interrupt(unsigned long arg)
comedi_event(dev, dev->read_subdev);
}
-static int waveform_attach(comedi_device *dev, comedi_devconfig *it)
+static int waveform_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
- comedi_subdevice *s;
+ struct comedi_subdevice *s;
int amplitude = it->options[0];
int period = it->options[1];
int i;
@@ -259,7 +259,7 @@ static int waveform_attach(comedi_device *dev, comedi_devconfig *it)
return 1;
}
-static int waveform_detach(comedi_device *dev)
+static int waveform_detach(struct comedi_device *dev)
{
printk("comedi%d: comedi_test: remove\n", dev->minor);
@@ -269,8 +269,8 @@ static int waveform_detach(comedi_device *dev)
return 0;
}
-static int waveform_ai_cmdtest(comedi_device *dev, comedi_subdevice *s,
- comedi_cmd *cmd)
+static int waveform_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
{
int err = 0;
int tmp;
@@ -397,9 +397,9 @@ static int waveform_ai_cmdtest(comedi_device *dev, comedi_subdevice *s,
return 0;
}
-static int waveform_ai_cmd(comedi_device *dev, comedi_subdevice *s)
+static int waveform_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
- comedi_cmd *cmd = &s->async->cmd;
+ struct comedi_cmd *cmd = &s->async->cmd;
if (cmd->flags & TRIG_RT) {
comedi_error(dev,
@@ -429,20 +429,20 @@ static int waveform_ai_cmd(comedi_device *dev, comedi_subdevice *s)
return 0;
}
-static int waveform_ai_cancel(comedi_device *dev, comedi_subdevice *s)
+static int waveform_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
devpriv->timer_running = 0;
del_timer(&devpriv->timer);
return 0;
}
-static sampl_t fake_sawtooth(comedi_device *dev, unsigned int range_index,
+static short fake_sawtooth(struct comedi_device *dev, unsigned int range_index,
unsigned long current_time)
{
- comedi_subdevice *s = dev->read_subdev;
+ struct comedi_subdevice *s = dev->read_subdev;
unsigned int offset = s->maxdata / 2;
u64 value;
- const comedi_krange *krange = &s->range_table->range[range_index];
+ const struct comedi_krange *krange = &s->range_table->range[range_index];
u64 binary_amplitude;
binary_amplitude = s->maxdata;
@@ -457,13 +457,13 @@ static sampl_t fake_sawtooth(comedi_device *dev, unsigned int range_index,
return offset + value;
}
-static sampl_t fake_squarewave(comedi_device *dev, unsigned int range_index,
+static short fake_squarewave(struct comedi_device *dev, unsigned int range_index,
unsigned long current_time)
{
- comedi_subdevice *s = dev->read_subdev;
+ struct comedi_subdevice *s = dev->read_subdev;
unsigned int offset = s->maxdata / 2;
u64 value;
- const comedi_krange *krange = &s->range_table->range[range_index];
+ const struct comedi_krange *krange = &s->range_table->range[range_index];
current_time %= devpriv->usec_period;
value = s->maxdata;
@@ -476,14 +476,14 @@ static sampl_t fake_squarewave(comedi_device *dev, unsigned int range_index,
return offset + value;
}
-static sampl_t fake_flatline(comedi_device *dev, unsigned int range_index,
+static short fake_flatline(struct comedi_device *dev, unsigned int range_index,
unsigned long current_time)
{
return dev->read_subdev->maxdata / 2;
}
/* generates a different waveform depending on what channel is read */
-static sampl_t fake_waveform(comedi_device *dev, unsigned int channel,
+static short fake_waveform(struct comedi_device *dev, unsigned int channel,
unsigned int range, unsigned long current_time)
{
enum {
@@ -504,8 +504,8 @@ static sampl_t fake_waveform(comedi_device *dev, unsigned int channel,
return fake_flatline(dev, range, current_time);
}
-static int waveform_ai_insn_read(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int waveform_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int i, chan = CR_CHAN(insn->chanspec);
@@ -515,8 +515,8 @@ static int waveform_ai_insn_read(comedi_device *dev, comedi_subdevice *s,
return insn->n;
}
-static int waveform_ao_insn_write(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int waveform_ao_insn_write(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int i, chan = CR_CHAN(insn->chanspec);
diff --git a/drivers/staging/comedi/drivers/contec_pci_dio.c b/drivers/staging/comedi/drivers/contec_pci_dio.c
new file mode 100644
index 000000000000..1f194a0a45d6
--- /dev/null
+++ b/drivers/staging/comedi/drivers/contec_pci_dio.c
@@ -0,0 +1,230 @@
+/*
+ comedi/drivers/contec_pci_dio.c
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: contec_pci_dio
+Description: Contec PIO1616L digital I/O board
+Devices: [Contec] PIO1616L (contec_pci_dio)
+Author: Stefano Rivoir <s.rivoir@gts.it>
+Updated: Wed, 27 Jun 2007 13:00:06 +0100
+Status: works
+
+Configuration Options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first supported
+ PCI device found will be used.
+*/
+
+#include "../comedidev.h"
+
+#include "comedi_pci.h"
+
+enum contec_model {
+ PIO1616L = 0,
+};
+
+struct contec_board {
+ const char *name;
+ int model;
+ int in_ports;
+ int out_ports;
+ int in_offs;
+ int out_offs;
+ int out_boffs;
+};
+static const struct contec_board contec_boards[] = {
+ {"PIO1616L", PIO1616L, 16, 16, 0, 2, 10},
+};
+
+#define PCI_DEVICE_ID_PIO1616L 0x8172
+static DEFINE_PCI_DEVICE_TABLE(contec_pci_table) = {
+ {PCI_VENDOR_ID_CONTEC, PCI_DEVICE_ID_PIO1616L, PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, PIO1616L},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, contec_pci_table);
+
+#define thisboard ((const struct contec_board *)dev->board_ptr)
+
+struct contec_private {
+ int data;
+
+ struct pci_dev *pci_dev;
+
+};
+
+#define devpriv ((struct contec_private *)dev->private)
+
+static int contec_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int contec_detach(struct comedi_device * dev);
+static struct comedi_driver driver_contec = {
+ driver_name:"contec_pci_dio",
+ module:THIS_MODULE,
+ attach:contec_attach,
+ detach:contec_detach,
+};
+
+/* Classic digital IO */
+static int contec_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int contec_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+#if 0
+static int contec_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+
+static int contec_ns_to_timer(unsigned int *ns, int round);
+#endif
+
+static int contec_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct pci_dev *pcidev;
+ struct comedi_subdevice *s;
+
+ printk("comedi%d: contec: ", dev->minor);
+
+ dev->board_name = thisboard->name;
+
+ if (alloc_private(dev, sizeof(struct contec_private)) < 0)
+ return -ENOMEM;
+
+ if (alloc_subdevices(dev, 2) < 0)
+ return -ENOMEM;
+
+ for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pcidev != NULL;
+ pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
+
+ if (pcidev->vendor == PCI_VENDOR_ID_CONTEC &&
+ pcidev->device == PCI_DEVICE_ID_PIO1616L) {
+ if (it->options[0] || it->options[1]) {
+ /* Check bus and slot. */
+ if (it->options[0] != pcidev->bus->number ||
+ it->options[1] !=
+ PCI_SLOT(pcidev->devfn)) {
+ continue;
+ }
+ }
+ devpriv->pci_dev = pcidev;
+ if (comedi_pci_enable(pcidev, "contec_pci_dio")) {
+ printk("error enabling PCI device and request regions!\n");
+ return -EIO;
+ }
+ dev->iobase = pci_resource_start(pcidev, 0);
+ printk(" base addr %lx ", dev->iobase);
+
+ dev->board_ptr = contec_boards + 0;
+
+ s = dev->subdevices + 0;
+
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = contec_di_insn_bits;
+
+ s = dev->subdevices + 1;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = contec_do_insn_bits;
+
+ printk("attached\n");
+
+ return 1;
+ }
+ }
+
+ printk("card not present!\n");
+
+ return -EIO;
+}
+
+static int contec_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: contec: remove\n", dev->minor);
+
+ if (devpriv && devpriv->pci_dev) {
+ if (dev->iobase) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ }
+
+ return 0;
+}
+
+#if 0
+static int contec_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ printk("contec_cmdtest called\n");
+ return 0;
+}
+
+static int contec_ns_to_timer(unsigned int *ns, int round)
+{
+ return *ns;
+}
+#endif
+
+static int contec_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ printk("contec_do_insn_bits called\n");
+ printk(" data: %d %d\n", data[0], data[1]);
+
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+ rt_printk(" out: %d on %lx\n", s->state,
+ dev->iobase + thisboard->out_offs);
+ outw(s->state, dev->iobase + thisboard->out_offs);
+ }
+ return 2;
+}
+
+static int contec_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ rt_printk("contec_di_insn_bits called\n");
+ rt_printk(" data: %d %d\n", data[0], data[1]);
+
+ if (insn->n != 2)
+ return -EINVAL;
+
+ data[1] = inw(dev->iobase + thisboard->in_offs);
+
+ return 2;
+}
+
+COMEDI_PCI_INITCLEANUP(driver_contec, contec_pci_table);
diff --git a/drivers/staging/comedi/drivers/daqboard2000.c b/drivers/staging/comedi/drivers/daqboard2000.c
new file mode 100644
index 000000000000..3b8444f09f28
--- /dev/null
+++ b/drivers/staging/comedi/drivers/daqboard2000.c
@@ -0,0 +1,877 @@
+/*
+ comedi/drivers/daqboard2000.c
+ hardware driver for IOtech DAQboard/2000
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+/*
+Driver: daqboard2000
+Description: IOTech DAQBoard/2000
+Author: Anders Blomdell <anders.blomdell@control.lth.se>
+Status: works
+Updated: Mon, 14 Apr 2008 15:28:52 +0100
+Devices: [IOTech] DAQBoard/2000 (daqboard2000)
+
+Much of the functionality of this driver was determined from reading
+the source code for the Windows driver.
+
+The FPGA on the board requires initialization code, which can
+be loaded by comedi_config using the -i
+option. The initialization code is available from http://www.comedi.org
+in the comedi_nonfree_firmware tarball.
+
+Configuration options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first supported
+ PCI device found will be used.
+*/
+/*
+ This card was obviously never intended to leave the Windows world,
+ since it lacked all kind of hardware documentation (except for cable
+ pinouts, plug and pray has something to catch up with yet).
+
+ With some help from our swedish distributor, we got the Windows sourcecode
+ for the card, and here are the findings so far.
+
+ 1. A good document that describes the PCI interface chip is found at:
+ http://plx.plxtech.com/download/9080/databook/9080db-106.pdf
+
+ 2. The initialization done so far is:
+ a. program the FPGA (windows code sans a lot of error messages)
+ b.
+
+ 3. Analog out seems to work OK with DAC's disabled, if DAC's are enabled,
+ you have to output values to all enabled DAC's until result appears, I
+ guess that it has something to do with pacer clocks, but the source
+ gives me no clues. I'll keep it simple so far.
+
+ 4. Analog in.
+ Each channel in the scanlist seems to be controlled by four
+ control words:
+
+ Word0:
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! | | | ! | | | ! | | | ! | | | !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Word1:
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! | | | ! | | | ! | | | ! | | | !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | | | | | | |
+ +------+------+ | | | | +-- Digital input (??)
+ | | | | +---- 10 us settling time
+ | | | +------ Suspend acquisition (last to scan)
+ | | +-------- Simultaneous sample and hold
+ | +---------- Signed data format
+ +------------------------- Correction offset low
+
+ Word2:
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! | | | ! | | | ! | | | ! | | | !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | | | | | | | | | |
+ +-----+ +--+--+ +++ +++ +--+--+
+ | | | | +----- Expansion channel
+ | | | +----------- Expansion gain
+ | | +--------------- Channel (low)
+ | +--------------------- Correction offset high
+ +----------------------------- Correction gain low
+ Word3:
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! | | | ! | | | ! | | | ! | | | !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | | | | | | | | |
+ +------+------+ | | +-+-+ | | +-- Low bank enable
+ | | | | | +---- High bank enable
+ | | | | +------ Hi/low select
+ | | | +---------- Gain (1,?,2,4,8,16,32,64)
+ | | +-------------- differential/single ended
+ | +---------------- Unipolar
+ +------------------------- Correction gain high
+
+
+
+ 999. The card seems to have an incredible amount of capabilities, but
+ trying to reverse engineer them from the Windows source is beyond my
+ patience.
+
+
+ */
+
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+
+#include "comedi_pci.h"
+#include "8255.h"
+
+#define DAQBOARD2000_SUBSYSTEM_IDS2 0x00021616 /* Daqboard/2000 - 2 Dacs */
+#define DAQBOARD2000_SUBSYSTEM_IDS4 0x00041616 /* Daqboard/2000 - 4 Dacs */
+
+#define DAQBOARD2000_DAQ_SIZE 0x1002
+#define DAQBOARD2000_PLX_SIZE 0x100
+
+// Initialization bits for the Serial EEPROM Control Register
+#define DAQBOARD2000_SECRProgPinHi 0x8001767e
+#define DAQBOARD2000_SECRProgPinLo 0x8000767e
+#define DAQBOARD2000_SECRLocalBusHi 0xc000767e
+#define DAQBOARD2000_SECRLocalBusLo 0x8000767e
+#define DAQBOARD2000_SECRReloadHi 0xa000767e
+#define DAQBOARD2000_SECRReloadLo 0x8000767e
+
+// SECR status bits
+#define DAQBOARD2000_EEPROM_PRESENT 0x10000000
+
+// CPLD status bits
+#define DAQBOARD2000_CPLD_INIT 0x0002
+#define DAQBOARD2000_CPLD_DONE 0x0004
+
+// Available ranges
+static const struct comedi_lrange range_daqboard2000_ai = { 13, {
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ RANGE(-2.5, 2.5),
+ RANGE(-1.25, 1.25),
+ RANGE(-0.625, 0.625),
+ RANGE(-0.3125, 0.3125),
+ RANGE(-0.156, 0.156),
+ RANGE(0, 10),
+ RANGE(0, 5),
+ RANGE(0, 2.5),
+ RANGE(0, 1.25),
+ RANGE(0, 0.625),
+ RANGE(0, 0.3125)
+ }
+};
+
+static const struct comedi_lrange range_daqboard2000_ao = { 1, {
+ RANGE(-10, 10)
+ }
+};
+
+struct daqboard2000_hw {
+ volatile u16 acqControl; // 0x00
+ volatile u16 acqScanListFIFO; // 0x02
+ volatile u32 acqPacerClockDivLow; // 0x04
+
+ volatile u16 acqScanCounter; // 0x08
+ volatile u16 acqPacerClockDivHigh; // 0x0a
+ volatile u16 acqTriggerCount; // 0x0c
+ volatile u16 fill2; // 0x0e
+ volatile u16 acqResultsFIFO; // 0x10
+ volatile u16 fill3; // 0x12
+ volatile u16 acqResultsShadow; // 0x14
+ volatile u16 fill4; // 0x16
+ volatile u16 acqAdcResult; // 0x18
+ volatile u16 fill5; // 0x1a
+ volatile u16 dacScanCounter; // 0x1c
+ volatile u16 fill6; // 0x1e
+
+ volatile u16 dacControl; // 0x20
+ volatile u16 fill7; // 0x22
+ volatile s16 dacFIFO; // 0x24
+ volatile u16 fill8[2]; // 0x26
+ volatile u16 dacPacerClockDiv; // 0x2a
+ volatile u16 refDacs; // 0x2c
+ volatile u16 fill9; // 0x2e
+
+ volatile u16 dioControl; // 0x30
+ volatile s16 dioP3hsioData; // 0x32
+ volatile u16 dioP3Control; // 0x34
+ volatile u16 calEepromControl; // 0x36
+ volatile s16 dacSetting[4]; // 0x38
+ volatile s16 dioP2ExpansionIO8Bit[32]; // 0x40
+
+ volatile u16 ctrTmrControl; // 0x80
+ volatile u16 fill10[3]; // 0x82
+ volatile s16 ctrInput[4]; // 0x88
+ volatile u16 fill11[8]; // 0x90
+ volatile u16 timerDivisor[2]; // 0xa0
+ volatile u16 fill12[6]; // 0xa4
+
+ volatile u16 dmaControl; // 0xb0
+ volatile u16 trigControl; // 0xb2
+ volatile u16 fill13[2]; // 0xb4
+ volatile u16 calEeprom; // 0xb8
+ volatile u16 acqDigitalMark; // 0xba
+ volatile u16 trigDacs; // 0xbc
+ volatile u16 fill14; // 0xbe
+ volatile s16 dioP2ExpansionIO16Bit[32]; // 0xc0
+};
+
+/* Scan Sequencer programming */
+#define DAQBOARD2000_SeqStartScanList 0x0011
+#define DAQBOARD2000_SeqStopScanList 0x0010
+
+// Prepare for acquisition
+#define DAQBOARD2000_AcqResetScanListFifo 0x0004
+#define DAQBOARD2000_AcqResetResultsFifo 0x0002
+#define DAQBOARD2000_AcqResetConfigPipe 0x0001
+
+// Acqusition status bits
+#define DAQBOARD2000_AcqResultsFIFOMore1Sample 0x0001
+#define DAQBOARD2000_AcqResultsFIFOHasValidData 0x0002
+#define DAQBOARD2000_AcqResultsFIFOOverrun 0x0004
+#define DAQBOARD2000_AcqLogicScanning 0x0008
+#define DAQBOARD2000_AcqConfigPipeFull 0x0010
+#define DAQBOARD2000_AcqScanListFIFOEmpty 0x0020
+#define DAQBOARD2000_AcqAdcNotReady 0x0040
+#define DAQBOARD2000_ArbitrationFailure 0x0080
+#define DAQBOARD2000_AcqPacerOverrun 0x0100
+#define DAQBOARD2000_DacPacerOverrun 0x0200
+#define DAQBOARD2000_AcqHardwareError 0x01c0
+
+// Scan Sequencer programming
+#define DAQBOARD2000_SeqStartScanList 0x0011
+#define DAQBOARD2000_SeqStopScanList 0x0010
+
+/* Pacer Clock Control */
+#define DAQBOARD2000_AdcPacerInternal 0x0030
+#define DAQBOARD2000_AdcPacerExternal 0x0032
+#define DAQBOARD2000_AdcPacerEnable 0x0031
+#define DAQBOARD2000_AdcPacerEnableDacPacer 0x0034
+#define DAQBOARD2000_AdcPacerDisable 0x0030
+#define DAQBOARD2000_AdcPacerNormalMode 0x0060
+#define DAQBOARD2000_AdcPacerCompatibilityMode 0x0061
+#define DAQBOARD2000_AdcPacerInternalOutEnable 0x0008
+#define DAQBOARD2000_AdcPacerExternalRising 0x0100
+
+// DAC status
+#define DAQBOARD2000_DacFull 0x0001
+#define DAQBOARD2000_RefBusy 0x0002
+#define DAQBOARD2000_TrgBusy 0x0004
+#define DAQBOARD2000_CalBusy 0x0008
+#define DAQBOARD2000_Dac0Busy 0x0010
+#define DAQBOARD2000_Dac1Busy 0x0020
+#define DAQBOARD2000_Dac2Busy 0x0040
+#define DAQBOARD2000_Dac3Busy 0x0080
+
+// DAC control
+#define DAQBOARD2000_Dac0Enable 0x0021
+#define DAQBOARD2000_Dac1Enable 0x0031
+#define DAQBOARD2000_Dac2Enable 0x0041
+#define DAQBOARD2000_Dac3Enable 0x0051
+#define DAQBOARD2000_DacEnableBit 0x0001
+#define DAQBOARD2000_Dac0Disable 0x0020
+#define DAQBOARD2000_Dac1Disable 0x0030
+#define DAQBOARD2000_Dac2Disable 0x0040
+#define DAQBOARD2000_Dac3Disable 0x0050
+#define DAQBOARD2000_DacResetFifo 0x0004
+#define DAQBOARD2000_DacPatternDisable 0x0060
+#define DAQBOARD2000_DacPatternEnable 0x0061
+#define DAQBOARD2000_DacSelectSignedData 0x0002
+#define DAQBOARD2000_DacSelectUnsignedData 0x0000
+
+/* Trigger Control */
+#define DAQBOARD2000_TrigAnalog 0x0000
+#define DAQBOARD2000_TrigTTL 0x0010
+#define DAQBOARD2000_TrigTransHiLo 0x0004
+#define DAQBOARD2000_TrigTransLoHi 0x0000
+#define DAQBOARD2000_TrigAbove 0x0000
+#define DAQBOARD2000_TrigBelow 0x0004
+#define DAQBOARD2000_TrigLevelSense 0x0002
+#define DAQBOARD2000_TrigEdgeSense 0x0000
+#define DAQBOARD2000_TrigEnable 0x0001
+#define DAQBOARD2000_TrigDisable 0x0000
+
+// Reference Dac Selection
+#define DAQBOARD2000_PosRefDacSelect 0x0100
+#define DAQBOARD2000_NegRefDacSelect 0x0000
+
+static int daqboard2000_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int daqboard2000_detach(struct comedi_device * dev);
+
+static struct comedi_driver driver_daqboard2000 = {
+ driver_name:"daqboard2000",
+ module:THIS_MODULE,
+ attach:daqboard2000_attach,
+ detach:daqboard2000_detach,
+};
+
+struct daq200_boardtype {
+ const char *name;
+ int id;
+};
+static const struct daq200_boardtype boardtypes[] = {
+ {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
+ {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(struct daq200_boardtype))
+#define this_board ((const struct daq200_boardtype *)dev->board_ptr)
+
+static DEFINE_PCI_DEVICE_TABLE(daqboard2000_pci_table) = {
+ {0x1616, 0x0409, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
+
+struct daqboard2000_private {
+ enum {
+ card_daqboard_2000
+ } card;
+ struct pci_dev *pci_dev;
+ void *daq;
+ void *plx;
+ int got_regions;
+ unsigned int ao_readback[2];
+};
+
+#define devpriv ((struct daqboard2000_private *)dev->private)
+
+static void writeAcqScanListEntry(struct comedi_device * dev, u16 entry)
+{
+ struct daqboard2000_hw *fpga = devpriv->daq;
+
+// comedi_udelay(4);
+ fpga->acqScanListFIFO = entry & 0x00ff;
+// comedi_udelay(4);
+ fpga->acqScanListFIFO = (entry >> 8) & 0x00ff;
+}
+
+static void setup_sampling(struct comedi_device * dev, int chan, int gain)
+{
+ u16 word0, word1, word2, word3;
+
+ /* Channel 0-7 diff, channel 8-23 single ended */
+ word0 = 0;
+ word1 = 0x0004; /* Last scan */
+ word2 = (chan << 6) & 0x00c0;
+ switch (chan / 4) {
+ case 0:
+ word3 = 0x0001;
+ break;
+ case 1:
+ word3 = 0x0002;
+ break;
+ case 2:
+ word3 = 0x0005;
+ break;
+ case 3:
+ word3 = 0x0006;
+ break;
+ case 4:
+ word3 = 0x0041;
+ break;
+ case 5:
+ word3 = 0x0042;
+ break;
+ default:
+ word3 = 0;
+ break;
+ }
+/*
+ dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
+ dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
+*/
+ /* These should be read from EEPROM */
+ word2 |= 0x0800;
+ word3 |= 0xc000;
+/* printk("%d %4.4x %4.4x %4.4x %4.4x\n", chan, word0, word1, word2, word3);*/
+ writeAcqScanListEntry(dev, word0);
+ writeAcqScanListEntry(dev, word1);
+ writeAcqScanListEntry(dev, word2);
+ writeAcqScanListEntry(dev, word3);
+}
+
+static int daqboard2000_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ struct daqboard2000_hw *fpga = devpriv->daq;
+ int gain, chan, timeout;
+
+ fpga->acqControl =
+ DAQBOARD2000_AcqResetScanListFifo |
+ DAQBOARD2000_AcqResetResultsFifo |
+ DAQBOARD2000_AcqResetConfigPipe;
+
+ /* If pacer clock is not set to some high value (> 10 us), we
+ risk multiple samples to be put into the result FIFO. */
+ fpga->acqPacerClockDivLow = 1000000; /* 1 second, should be long enough */
+ fpga->acqPacerClockDivHigh = 0;
+
+ gain = CR_RANGE(insn->chanspec);
+ chan = CR_CHAN(insn->chanspec);
+
+ /* This doesn't look efficient. I decided to take the conservative
+ * approach when I did the insn conversion. Perhaps it would be
+ * better to have broken it completely, then someone would have been
+ * forced to fix it. --ds */
+ for (i = 0; i < insn->n; i++) {
+ setup_sampling(dev, chan, gain);
+ /* Enable reading from the scanlist FIFO */
+ fpga->acqControl = DAQBOARD2000_SeqStartScanList;
+ for (timeout = 0; timeout < 20; timeout++) {
+ if (fpga->acqControl & DAQBOARD2000_AcqConfigPipeFull) {
+ break;
+ }
+ //comedi_udelay(2);
+ }
+ fpga->acqControl = DAQBOARD2000_AdcPacerEnable;
+ for (timeout = 0; timeout < 20; timeout++) {
+ if (fpga->acqControl & DAQBOARD2000_AcqLogicScanning) {
+ break;
+ }
+ //comedi_udelay(2);
+ }
+ for (timeout = 0; timeout < 20; timeout++) {
+ if (fpga->
+ acqControl &
+ DAQBOARD2000_AcqResultsFIFOHasValidData) {
+ break;
+ }
+ //comedi_udelay(2);
+ }
+ data[i] = fpga->acqResultsFIFO;
+ fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
+ fpga->acqControl = DAQBOARD2000_SeqStopScanList;
+ }
+
+ return i;
+}
+
+static int daqboard2000_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++) {
+ data[i] = devpriv->ao_readback[chan];
+ }
+
+ return i;
+}
+
+static int daqboard2000_ao_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+ struct daqboard2000_hw *fpga = devpriv->daq;
+ int timeout;
+
+ for (i = 0; i < insn->n; i++) {
+ /*
+ * OK, since it works OK without enabling the DAC's, let's keep
+ * it as simple as possible...
+ */
+ //fpga->dacControl = (chan + 2) * 0x0010 | 0x0001; comedi_udelay(1000);
+ fpga->dacSetting[chan] = data[i];
+ for (timeout = 0; timeout < 20; timeout++) {
+ if ((fpga->dacControl & ((chan + 1) * 0x0010)) == 0) {
+ break;
+ }
+ //comedi_udelay(2);
+ }
+ devpriv->ao_readback[chan] = data[i];
+ /*
+ * Since we never enabled the DAC's, we don't need to disable it...
+ * fpga->dacControl = (chan + 2) * 0x0010 | 0x0000; comedi_udelay(1000);
+ */
+ }
+
+ return i;
+}
+
+static void daqboard2000_resetLocalBus(struct comedi_device * dev)
+{
+ printk("daqboard2000_resetLocalBus\n");
+ writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
+ comedi_udelay(10000);
+ writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
+ comedi_udelay(10000);
+}
+
+static void daqboard2000_reloadPLX(struct comedi_device * dev)
+{
+ printk("daqboard2000_reloadPLX\n");
+ writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
+ comedi_udelay(10000);
+ writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
+ comedi_udelay(10000);
+ writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
+ comedi_udelay(10000);
+}
+
+static void daqboard2000_pulseProgPin(struct comedi_device * dev)
+{
+ printk("daqboard2000_pulseProgPin 1\n");
+ writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
+ comedi_udelay(10000);
+ writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
+ comedi_udelay(10000); /* Not in the original code, but I like symmetry... */
+}
+
+static int daqboard2000_pollCPLD(struct comedi_device * dev, int mask)
+{
+ int result = 0;
+ int i;
+ int cpld;
+
+ /* timeout after 50 tries -> 5ms */
+ for (i = 0; i < 50; i++) {
+ cpld = readw(devpriv->daq + 0x1000);
+ if ((cpld & mask) == mask) {
+ result = 1;
+ break;
+ }
+ comedi_udelay(100);
+ }
+ comedi_udelay(5);
+ return result;
+}
+
+static int daqboard2000_writeCPLD(struct comedi_device * dev, int data)
+{
+ int result = 0;
+
+ comedi_udelay(10);
+ writew(data, devpriv->daq + 0x1000);
+ if ((readw(devpriv->daq + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
+ DAQBOARD2000_CPLD_INIT) {
+ result = 1;
+ }
+ return result;
+}
+
+static int initialize_daqboard2000(struct comedi_device * dev,
+ unsigned char *cpld_array, int len)
+{
+ int result = -EIO;
+ /* Read the serial EEPROM control register */
+ int secr;
+ int retry;
+ int i;
+
+ /* Check to make sure the serial eeprom is present on the board */
+ secr = readl(devpriv->plx + 0x6c);
+ if (!(secr & DAQBOARD2000_EEPROM_PRESENT)) {
+#ifdef DEBUG_EEPROM
+ printk("no serial eeprom\n");
+#endif
+ return -EIO;
+ }
+
+ for (retry = 0; retry < 3; retry++) {
+#ifdef DEBUG_EEPROM
+ printk("Programming EEPROM try %x\n", retry);
+#endif
+
+ daqboard2000_resetLocalBus(dev);
+ daqboard2000_reloadPLX(dev);
+ daqboard2000_pulseProgPin(dev);
+ if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
+ for (i = 0; i < len; i++) {
+ if (cpld_array[i] == 0xff
+ && cpld_array[i + 1] == 0x20) {
+#ifdef DEBUG_EEPROM
+ printk("Preamble found at %d\n", i);
+#endif
+ break;
+ }
+ }
+ for (; i < len; i += 2) {
+ int data =
+ (cpld_array[i] << 8) + cpld_array[i +
+ 1];
+ if (!daqboard2000_writeCPLD(dev, data)) {
+ break;
+ }
+ }
+ if (i >= len) {
+#ifdef DEBUG_EEPROM
+ printk("Programmed\n");
+#endif
+ daqboard2000_resetLocalBus(dev);
+ daqboard2000_reloadPLX(dev);
+ result = 0;
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+static void daqboard2000_adcStopDmaTransfer(struct comedi_device * dev)
+{
+/* printk("Implement: daqboard2000_adcStopDmaTransfer\n");*/
+}
+
+static void daqboard2000_adcDisarm(struct comedi_device * dev)
+{
+ struct daqboard2000_hw *fpga = devpriv->daq;
+
+ /* Disable hardware triggers */
+ comedi_udelay(2);
+ fpga->trigControl = DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable;
+ comedi_udelay(2);
+ fpga->trigControl = DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable;
+
+ /* Stop the scan list FIFO from loading the configuration pipe */
+ comedi_udelay(2);
+ fpga->acqControl = DAQBOARD2000_SeqStopScanList;
+
+ /* Stop the pacer clock */
+ comedi_udelay(2);
+ fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
+
+ /* Stop the input dma (abort channel 1) */
+ daqboard2000_adcStopDmaTransfer(dev);
+}
+
+static void daqboard2000_activateReferenceDacs(struct comedi_device * dev)
+{
+ struct daqboard2000_hw *fpga = devpriv->daq;
+ int timeout;
+
+ // Set the + reference dac value in the FPGA
+ fpga->refDacs = 0x80 | DAQBOARD2000_PosRefDacSelect;
+ for (timeout = 0; timeout < 20; timeout++) {
+ if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0) {
+ break;
+ }
+ comedi_udelay(2);
+ }
+/* printk("DAQBOARD2000_PosRefDacSelect %d\n", timeout);*/
+
+ // Set the - reference dac value in the FPGA
+ fpga->refDacs = 0x80 | DAQBOARD2000_NegRefDacSelect;
+ for (timeout = 0; timeout < 20; timeout++) {
+ if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0) {
+ break;
+ }
+ comedi_udelay(2);
+ }
+/* printk("DAQBOARD2000_NegRefDacSelect %d\n", timeout);*/
+}
+
+static void daqboard2000_initializeCtrs(struct comedi_device * dev)
+{
+/* printk("Implement: daqboard2000_initializeCtrs\n");*/
+}
+
+static void daqboard2000_initializeTmrs(struct comedi_device * dev)
+{
+/* printk("Implement: daqboard2000_initializeTmrs\n");*/
+}
+
+static void daqboard2000_dacDisarm(struct comedi_device * dev)
+{
+/* printk("Implement: daqboard2000_dacDisarm\n");*/
+}
+
+static void daqboard2000_initializeAdc(struct comedi_device * dev)
+{
+ daqboard2000_adcDisarm(dev);
+ daqboard2000_activateReferenceDacs(dev);
+ daqboard2000_initializeCtrs(dev);
+ daqboard2000_initializeTmrs(dev);
+}
+
+static void daqboard2000_initializeDac(struct comedi_device * dev)
+{
+ daqboard2000_dacDisarm(dev);
+}
+
+/*
+The test command, REMOVE!!:
+
+rmmod daqboard2000 ; rmmod comedi; make install ; modprobe daqboard2000; /usr/sbin/comedi_config /dev/comedi0 daqboard/2000 ; tail -40 /var/log/messages
+*/
+
+static int daqboard2000_8255_cb(int dir, int port, int data,
+ unsigned long ioaddr)
+{
+ int result = 0;
+ if (dir) {
+ writew(data, ((void *)ioaddr) + port * 2);
+ result = 0;
+ } else {
+ result = readw(((void *)ioaddr) + port * 2);
+ }
+/*
+ printk("daqboard2000_8255_cb %x %d %d %2.2x -> %2.2x\n",
+ arg, dir, port, data, result);
+*/
+ return result;
+}
+
+static int daqboard2000_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int result = 0;
+ struct comedi_subdevice *s;
+ struct pci_dev *card = NULL;
+ void *aux_data;
+ unsigned int aux_len;
+ int bus, slot;
+
+ printk("comedi%d: daqboard2000:", dev->minor);
+
+ bus = it->options[0];
+ slot = it->options[1];
+
+ result = alloc_private(dev, sizeof(struct daqboard2000_private));
+ if (result < 0) {
+ return -ENOMEM;
+ }
+ for (card = pci_get_device(0x1616, 0x0409, NULL);
+ card != NULL;
+ card = pci_get_device(0x1616, 0x0409, card)) {
+ if (bus || slot) {
+ /* requested particular bus/slot */
+ if (card->bus->number != bus ||
+ PCI_SLOT(card->devfn) != slot) {
+ continue;
+ }
+ }
+ break; /* found one */
+ }
+ if (!card) {
+ if (bus || slot)
+ printk(" no daqboard2000 found at bus/slot: %d/%d\n",
+ bus, slot);
+ else
+ printk(" no daqboard2000 found\n");
+ return -EIO;
+ } else {
+ u32 id;
+ int i;
+ devpriv->pci_dev = card;
+ id = ((u32) card->subsystem_device << 16) | card->
+ subsystem_vendor;
+ for (i = 0; i < n_boardtypes; i++) {
+ if (boardtypes[i].id == id) {
+ printk(" %s", boardtypes[i].name);
+ dev->board_ptr = boardtypes + i;
+ }
+ }
+ if (!dev->board_ptr) {
+ printk(" unknown subsystem id %08x (pretend it is an ids2)", id);
+ dev->board_ptr = boardtypes;
+ }
+ }
+
+ if ((result = comedi_pci_enable(card, "daqboard2000")) < 0) {
+ printk(" failed to enable PCI device and request regions\n");
+ return -EIO;
+ }
+ devpriv->got_regions = 1;
+ devpriv->plx =
+ ioremap(pci_resource_start(card, 0), DAQBOARD2000_PLX_SIZE);
+ devpriv->daq =
+ ioremap(pci_resource_start(card, 2), DAQBOARD2000_DAQ_SIZE);
+ if (!devpriv->plx || !devpriv->daq) {
+ return -ENOMEM;
+ }
+
+ result = alloc_subdevices(dev, 3);
+ if (result < 0)
+ goto out;
+
+ readl(devpriv->plx + 0x6c);
+
+ /*
+ u8 interrupt;
+ Windows code does restore interrupts, but since we don't use them...
+ pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
+ printk("Interrupt before is: %x\n", interrupt);
+ */
+
+ aux_data = comedi_aux_data(it->options, 0);
+ aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
+
+ if (aux_data && aux_len) {
+ result = initialize_daqboard2000(dev, aux_data, aux_len);
+ } else {
+ printk("no FPGA initialization code, aborting\n");
+ result = -EIO;
+ }
+ if (result < 0)
+ goto out;
+ daqboard2000_initializeAdc(dev);
+ daqboard2000_initializeDac(dev);
+ /*
+ Windows code does restore interrupts, but since we don't use them...
+ pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
+ printk("Interrupt after is: %x\n", interrupt);
+ */
+
+ dev->iobase = (unsigned long)devpriv->daq;
+
+ dev->board_name = this_board->name;
+
+ s = dev->subdevices + 0;
+ /* ai subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 24;
+ s->maxdata = 0xffff;
+ s->insn_read = daqboard2000_ai_insn_read;
+ s->range_table = &range_daqboard2000_ai;
+
+ s = dev->subdevices + 1;
+ /* ao subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = 0xffff;
+ s->insn_read = daqboard2000_ao_insn_read;
+ s->insn_write = daqboard2000_ao_insn_write;
+ s->range_table = &range_daqboard2000_ao;
+
+ s = dev->subdevices + 2;
+ result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
+ (unsigned long)(dev->iobase + 0x40));
+
+ printk("\n");
+ out:
+ return result;
+}
+
+static int daqboard2000_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: daqboard2000: remove\n", dev->minor);
+
+ if (dev->subdevices)
+ subdev_8255_cleanup(dev, dev->subdevices + 2);
+
+ if (dev->irq) {
+ free_irq(dev->irq, dev);
+ }
+ if (devpriv) {
+ if (devpriv->daq)
+ iounmap(devpriv->daq);
+ if (devpriv->plx)
+ iounmap(devpriv->plx);
+ if (devpriv->pci_dev) {
+ if (devpriv->got_regions) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ }
+ }
+ return 0;
+}
+
+COMEDI_PCI_INITCLEANUP(driver_daqboard2000, daqboard2000_pci_table);
diff --git a/drivers/staging/comedi/drivers/das08.c b/drivers/staging/comedi/drivers/das08.c
new file mode 100644
index 000000000000..e4563331e963
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das08.c
@@ -0,0 +1,1068 @@
+/*
+ comedi/drivers/das08.c
+ DAS08 driver
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ Copyright (C) 2001,2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+ Copyright (C) 2004 Salvador E. Tropea <set@users.sf.net> <set@ieee.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*****************************************************************
+
+*/
+/*
+Driver: das08
+Description: DAS-08 compatible boards
+Author: Warren Jasper, ds, Frank Hess
+Devices: [Keithley Metrabyte] DAS08 (isa-das08), [ComputerBoards] DAS08 (isa-das08),
+ DAS08-PGM (das08-pgm),
+ DAS08-PGH (das08-pgh), DAS08-PGL (das08-pgl), DAS08-AOH (das08-aoh),
+ DAS08-AOL (das08-aol), DAS08-AOM (das08-aom), DAS08/JR-AO (das08/jr-ao),
+ DAS08/JR-16-AO (das08jr-16-ao), PCI-DAS08 (das08),
+ PC104-DAS08 (pc104-das08), DAS08/JR/16 (das08jr/16)
+Status: works
+
+This is a rewrite of the das08 and das08jr drivers.
+
+Options (for ISA cards):
+ [0] - base io address
+
+Options (for pci-das08):
+ [0] - bus (optional)
+ [1] = slot (optional)
+
+The das08 driver doesn't support asynchronous commands, since
+the cheap das08 hardware doesn't really support them. The
+comedi_rt_timer driver can be used to emulate commands for this
+driver.
+*/
+
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+
+#include "comedi_pci.h"
+#include "8255.h"
+#include "das08.h"
+
+#define DRV_NAME "das08"
+
+#define PCI_VENDOR_ID_COMPUTERBOARDS 0x1307
+#define PCI_DEVICE_ID_PCIDAS08 0x29
+#define PCIDAS08_SIZE 0x54
+
+// pci configuration registers
+#define INTCSR 0x4c
+#define INTR1_ENABLE 0x1
+#define INTR1_HIGH_POLARITY 0x2
+#define PCI_INTR_ENABLE 0x40
+#define INTR1_EDGE_TRIG 0x100 // requires high polarity
+#define CNTRL 0x50
+#define CNTRL_DIR 0x2
+#define CNTRL_INTR 0x4
+
+/*
+ cio-das08.pdf
+
+ "isa-das08"
+
+ 0 a/d bits 0-3 start 8 bit
+ 1 a/d bits 4-11 start 12 bit
+ 2 eoc, ip1-3, irq, mux op1-4, inte, mux
+ 3 unused unused
+ 4567 8254
+ 89ab 8255
+
+ requires hard-wiring for async ai
+
+*/
+
+#define DAS08_LSB 0
+#define DAS08_MSB 1
+#define DAS08_TRIG_12BIT 1
+#define DAS08_STATUS 2
+#define DAS08_EOC (1<<7)
+#define DAS08_IRQ (1<<3)
+#define DAS08_IP(x) (((x)>>4)&0x7)
+#define DAS08_CONTROL 2
+#define DAS08_MUX_MASK 0x7
+#define DAS08_MUX(x) ((x) & DAS08_MUX_MASK)
+#define DAS08_INTE (1<<3)
+#define DAS08_DO_MASK 0xf0
+#define DAS08_OP(x) (((x) << 4) & DAS08_DO_MASK)
+
+/*
+ cio-das08jr.pdf
+
+ "das08/jr-ao"
+
+ 0 a/d bits 0-3 unused
+ 1 a/d bits 4-11 start 12 bit
+ 2 eoc, mux mux
+ 3 di do
+ 4 unused ao0_lsb
+ 5 unused ao0_msb
+ 6 unused ao1_lsb
+ 7 unused ao1_msb
+
+*/
+
+#define DAS08JR_DIO 3
+#define DAS08JR_AO_LSB(x) ((x)?6:4)
+#define DAS08JR_AO_MSB(x) ((x)?7:5)
+
+/*
+ cio-das08_aox.pdf
+
+ "das08-aoh"
+ "das08-aol"
+ "das08-aom"
+
+ 0 a/d bits 0-3 start 8 bit
+ 1 a/d bits 4-11 start 12 bit
+ 2 eoc, ip1-3, irq, mux op1-4, inte, mux
+ 3 mux, gain status gain control
+ 4567 8254
+ 8 unused ao0_lsb
+ 9 unused ao0_msb
+ a unused ao1_lsb
+ b unused ao1_msb
+ 89ab
+ cdef 8255
+*/
+
+#define DAS08AO_GAIN_CONTROL 3
+#define DAS08AO_GAIN_STATUS 3
+
+#define DAS08AO_AO_LSB(x) ((x)?0xa:8)
+#define DAS08AO_AO_MSB(x) ((x)?0xb:9)
+#define DAS08AO_AO_UPDATE 8
+
+/* gainlist same as _pgx_ below */
+
+static int das08_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das08_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das08_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das08jr_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das08jr_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das08jr_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das08ao_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static void i8254_set_mode_low(unsigned int base, int channel,
+ unsigned int mode);
+
+static const struct comedi_lrange range_das08_pgl = { 9, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+static const struct comedi_lrange range_das08_pgh = { 12, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(1),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.01),
+ BIP_RANGE(0.005),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01),
+ }
+};
+static const struct comedi_lrange range_das08_pgm = { 9, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.01),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01)
+ }
+}; /*
+ cio-das08jr.pdf
+
+ "das08/jr-ao"
+
+ 0 a/d bits 0-3 unused
+ 1 a/d bits 4-11 start 12 bit
+ 2 eoc, mux mux
+ 3 di do
+ 4 unused ao0_lsb
+ 5 unused ao0_msb
+ 6 unused ao1_lsb
+ 7 unused ao1_msb
+
+ */
+
+static const struct comedi_lrange *const das08_ai_lranges[] = {
+ &range_unknown,
+ &range_bipolar5,
+ &range_das08_pgh,
+ &range_das08_pgl,
+ &range_das08_pgm,
+};
+
+static const int das08_pgh_gainlist[] =
+ { 8, 0, 10, 2, 12, 4, 14, 6, 1, 3, 5, 7 };
+static const int das08_pgl_gainlist[] = { 8, 0, 2, 4, 6, 1, 3, 5, 7 };
+static const int das08_pgm_gainlist[] = { 8, 0, 10, 12, 14, 9, 11, 13, 15 };
+
+static const int *const das08_gainlists[] = {
+ NULL,
+ NULL,
+ das08_pgh_gainlist,
+ das08_pgl_gainlist,
+ das08_pgm_gainlist,
+};
+
+static const struct das08_board_struct das08_boards[] = {
+ {
+ name: "isa-das08", // cio-das08.pdf
+ bustype: isa,
+ ai: das08_ai_rinsn,
+ ai_nbits:12,
+ ai_pg: das08_pg_none,
+ ai_encoding:das08_encode12,
+ ao: NULL,
+ ao_nbits:12,
+ di: das08_di_rbits,
+ do_: das08_do_wbits,
+ do_nchan:4,
+ i8255_offset:8,
+ i8254_offset:4,
+ iosize: 16, // unchecked
+ },
+ {
+ name: "das08-pgm", // cio-das08pgx.pdf
+ bustype: isa,
+ ai: das08_ai_rinsn,
+ ai_nbits:12,
+ ai_pg: das08_pgm,
+ ai_encoding:das08_encode12,
+ ao: NULL,
+ di: das08_di_rbits,
+ do_: das08_do_wbits,
+ do_nchan:4,
+ i8255_offset:0,
+ i8254_offset:0x04,
+ iosize: 16, // unchecked
+ },
+ {
+ name: "das08-pgh", // cio-das08pgx.pdf
+ bustype: isa,
+ ai: das08_ai_rinsn,
+ ai_nbits:12,
+ ai_pg: das08_pgh,
+ ai_encoding:das08_encode12,
+ ao: NULL,
+ di: das08_di_rbits,
+ do_: das08_do_wbits,
+ do_nchan:4,
+ i8255_offset:0,
+ i8254_offset:0x04,
+ iosize: 16, // unchecked
+ },
+ {
+ name: "das08-pgl", // cio-das08pgx.pdf
+ bustype: isa,
+ ai: das08_ai_rinsn,
+ ai_nbits:12,
+ ai_pg: das08_pgl,
+ ai_encoding:das08_encode12,
+ ao: NULL,
+ di: das08_di_rbits,
+ do_: das08_do_wbits,
+ do_nchan:4,
+ i8255_offset:0,
+ i8254_offset:0x04,
+ iosize: 16, // unchecked
+ },
+ {
+ name: "das08-aoh", // cio-das08_aox.pdf
+ bustype: isa,
+ ai: das08_ai_rinsn,
+ ai_nbits:12,
+ ai_pg: das08_pgh,
+ ai_encoding:das08_encode12,
+ ao: das08ao_ao_winsn, // 8
+ ao_nbits:12,
+ di: das08_di_rbits,
+ do_: das08_do_wbits,
+ do_nchan:4,
+ i8255_offset:0x0c,
+ i8254_offset:0x04,
+ iosize: 16, // unchecked
+ },
+ {
+ name: "das08-aol", // cio-das08_aox.pdf
+ bustype: isa,
+ ai: das08_ai_rinsn,
+ ai_nbits:12,
+ ai_pg: das08_pgl,
+ ai_encoding:das08_encode12,
+ ao: das08ao_ao_winsn, // 8
+ ao_nbits:12,
+ di: das08_di_rbits,
+ do_: das08_do_wbits,
+ do_nchan:4,
+ i8255_offset:0x0c,
+ i8254_offset:0x04,
+ iosize: 16, // unchecked
+ },
+ {
+ name: "das08-aom", // cio-das08_aox.pdf
+ bustype: isa,
+ ai: das08_ai_rinsn,
+ ai_nbits:12,
+ ai_pg: das08_pgm,
+ ai_encoding:das08_encode12,
+ ao: das08ao_ao_winsn, // 8
+ ao_nbits:12,
+ di: das08_di_rbits,
+ do_: das08_do_wbits,
+ do_nchan:4,
+ i8255_offset:0x0c,
+ i8254_offset:0x04,
+ iosize: 16, // unchecked
+ },
+ {
+ name: "das08/jr-ao", // cio-das08-jr-ao.pdf
+ bustype: isa,
+ ai: das08_ai_rinsn,
+ ai_nbits:12,
+ ai_pg: das08_pg_none,
+ ai_encoding:das08_encode12,
+ ao: das08jr_ao_winsn,
+ ao_nbits:12,
+ di: das08jr_di_rbits,
+ do_: das08jr_do_wbits,
+ do_nchan:8,
+ i8255_offset:0,
+ i8254_offset:0,
+ iosize: 16, // unchecked
+ },
+ {
+ name: "das08jr-16-ao", // cio-das08jr-16-ao.pdf
+ bustype: isa,
+ ai: das08_ai_rinsn,
+ ai_nbits:16,
+ ai_pg: das08_pg_none,
+ ai_encoding:das08_encode12,
+ ao: das08jr_ao_winsn,
+ ao_nbits:16,
+ di: das08jr_di_rbits,
+ do_: das08jr_do_wbits,
+ do_nchan:8,
+ i8255_offset:0,
+ i8254_offset:0x04,
+ iosize: 16, // unchecked
+ },
+#ifdef CONFIG_COMEDI_PCI
+ {
+ name: "das08", // pci-das08
+ id: PCI_DEVICE_ID_PCIDAS08,
+ bustype: pci,
+ ai: das08_ai_rinsn,
+ ai_nbits:12,
+ ai_pg: das08_bipolar5,
+ ai_encoding:das08_encode12,
+ ao: NULL,
+ ao_nbits:0,
+ di: das08_di_rbits,
+ do_: das08_do_wbits,
+ do_nchan:4,
+ i8255_offset:0,
+ i8254_offset:4,
+ iosize: 8,
+ },
+#endif
+ {
+ name: "pc104-das08",
+ bustype: pc104,
+ ai: das08_ai_rinsn,
+ ai_nbits:12,
+ ai_pg: das08_pg_none,
+ ai_encoding:das08_encode12,
+ ao: NULL,
+ ao_nbits:0,
+ di: das08_di_rbits,
+ do_: das08_do_wbits,
+ do_nchan:4,
+ i8255_offset:0,
+ i8254_offset:4,
+ iosize: 16, // unchecked
+ },
+#if 0
+ {
+ name: "das08/f",
+ },
+ {
+ name: "das08jr",
+ },
+#endif
+ {
+ name: "das08jr/16",
+ bustype: isa,
+ ai: das08_ai_rinsn,
+ ai_nbits:16,
+ ai_pg: das08_pg_none,
+ ai_encoding:das08_encode16,
+ ao: NULL,
+ ao_nbits:0,
+ di: das08jr_di_rbits,
+ do_: das08jr_do_wbits,
+ do_nchan:8,
+ i8255_offset:0,
+ i8254_offset:0,
+ iosize: 16, // unchecked
+ },
+#if 0
+ {
+ name: "das48-pga", // cio-das48-pga.pdf
+ },
+ {
+ name: "das08-pga-g2", // a KM board
+ },
+#endif
+};
+
+#ifdef CONFIG_COMEDI_PCMCIA
+struct das08_board_struct das08_cs_boards[NUM_DAS08_CS_BOARDS] = {
+ {
+ name: "pcm-das08",
+ id: 0x0, // XXX
+ bustype: pcmcia,
+ ai: das08_ai_rinsn,
+ ai_nbits:12,
+ ai_pg: das08_bipolar5,
+ ai_encoding:das08_pcm_encode12,
+ ao: NULL,
+ ao_nbits:0,
+ di: das08_di_rbits,
+ do_: das08_do_wbits,
+ do_nchan:3,
+ i8255_offset:0,
+ i8254_offset:0,
+ iosize: 16,
+ },
+ // duplicate so driver name can be used also
+ {
+ name: "das08_cs",
+ id: 0x0, // XXX
+ bustype: pcmcia,
+ ai: das08_ai_rinsn,
+ ai_nbits:12,
+ ai_pg: das08_bipolar5,
+ ai_encoding:das08_pcm_encode12,
+ ao: NULL,
+ ao_nbits:0,
+ di: das08_di_rbits,
+ do_: das08_do_wbits,
+ do_nchan:3,
+ i8255_offset:0,
+ i8254_offset:0,
+ iosize: 16,
+ },
+};
+#endif
+
+#ifdef CONFIG_COMEDI_PCI
+static DEFINE_PCI_DEVICE_TABLE(das08_pci_table) = {
+ {PCI_VENDOR_ID_COMPUTERBOARDS, PCI_DEVICE_ID_PCIDAS08, PCI_ANY_ID,
+ PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, das08_pci_table);
+#endif
+
+#define devpriv ((struct das08_private_struct *)dev->private)
+#define thisboard ((const struct das08_board_struct *)dev->board_ptr)
+
+#define TIMEOUT 100000
+
+static int das08_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i, n;
+ int chan;
+ int range;
+ int lsb, msb;
+
+ chan = CR_CHAN(insn->chanspec);
+ range = CR_RANGE(insn->chanspec);
+
+ /* clear crap */
+ inb(dev->iobase + DAS08_LSB);
+ inb(dev->iobase + DAS08_MSB);
+
+ /* set multiplexer */
+ spin_lock(&dev->spinlock); // lock to prevent race with digital output
+ devpriv->do_mux_bits &= ~DAS08_MUX_MASK;
+ devpriv->do_mux_bits |= DAS08_MUX(chan);
+ outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
+ spin_unlock(&dev->spinlock);
+
+ if (s->range_table->length > 1) {
+ /* set gain/range */
+ range = CR_RANGE(insn->chanspec);
+ outb(devpriv->pg_gainlist[range],
+ dev->iobase + DAS08AO_GAIN_CONTROL);
+ }
+
+ for (n = 0; n < insn->n; n++) {
+ /* clear over-range bits for 16-bit boards */
+ if (thisboard->ai_nbits == 16)
+ if (inb(dev->iobase + DAS08_MSB) & 0x80)
+ rt_printk("das08: over-range\n");
+
+ /* trigger conversion */
+ outb_p(0, dev->iobase + DAS08_TRIG_12BIT);
+
+ for (i = 0; i < TIMEOUT; i++) {
+ if (!(inb(dev->iobase + DAS08_STATUS) & DAS08_EOC))
+ break;
+ }
+ if (i == TIMEOUT) {
+ rt_printk("das08: timeout\n");
+ return -ETIME;
+ }
+ msb = inb(dev->iobase + DAS08_MSB);
+ lsb = inb(dev->iobase + DAS08_LSB);
+ if (thisboard->ai_encoding == das08_encode12) {
+ data[n] = (lsb >> 4) | (msb << 4);
+ } else if (thisboard->ai_encoding == das08_pcm_encode12) {
+ data[n] = (msb << 8) + lsb;
+ } else if (thisboard->ai_encoding == das08_encode16) {
+ /* FPOS 16-bit boards are sign-magnitude */
+ if (msb & 0x80)
+ data[n] = (1 << 15) | lsb | ((msb & 0x7f) << 8);
+ else
+ data[n] = (1 << 15) - (lsb | (msb & 0x7f) << 8);
+ } else {
+ comedi_error(dev, "bug! unknown ai encoding");
+ return -1;
+ }
+ }
+
+ return n;
+}
+
+static int das08_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = 0;
+ data[1] = DAS08_IP(inb(dev->iobase + DAS08_STATUS));
+
+ return 2;
+}
+
+static int das08_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int wbits;
+
+ // get current settings of digital output lines
+ wbits = (devpriv->do_mux_bits >> 4) & 0xf;
+ // null bits we are going to set
+ wbits &= ~data[0];
+ // set new bit values
+ wbits |= data[0] & data[1];
+ // remember digital output bits
+ spin_lock(&dev->spinlock); // prevent race with setting of analog input mux
+ devpriv->do_mux_bits &= ~DAS08_DO_MASK;
+ devpriv->do_mux_bits |= DAS08_OP(wbits);
+ outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
+ spin_unlock(&dev->spinlock);
+
+ data[1] = wbits;
+
+ return 2;
+}
+
+static int das08jr_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = 0;
+ data[1] = inb(dev->iobase + DAS08JR_DIO);
+
+ return 2;
+}
+
+static int das08jr_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ // null bits we are going to set
+ devpriv->do_bits &= ~data[0];
+ // set new bit values
+ devpriv->do_bits |= data[0] & data[1];
+ outb(devpriv->do_bits, dev->iobase + DAS08JR_DIO);
+
+ data[1] = devpriv->do_bits;
+
+ return 2;
+}
+
+static int das08jr_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int lsb, msb;
+ int chan;
+
+ lsb = data[0] & 0xff;
+ msb = (data[0] >> 8) & 0xf;
+
+ chan = CR_CHAN(insn->chanspec);
+
+ for (n = 0; n < insn->n; n++) {
+#if 0
+ outb(lsb, dev->iobase + devpriv->ao_offset_lsb[chan]);
+ outb(msb, dev->iobase + devpriv->ao_offset_msb[chan]);
+#else
+ outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan));
+ outb(msb, dev->iobase + DAS08JR_AO_MSB(chan));
+#endif
+
+ /* load DACs */
+ inb(dev->iobase + DAS08JR_DIO);
+ }
+
+ return n;
+}
+
+/*
+ *
+ * The -aox boards have the DACs at a different offset and use
+ * a different method to force an update.
+ *
+ */
+static int das08ao_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int lsb, msb;
+ int chan;
+
+ lsb = data[0] & 0xff;
+ msb = (data[0] >> 8) & 0xf;
+
+ chan = CR_CHAN(insn->chanspec);
+
+ for (n = 0; n < insn->n; n++) {
+#if 0
+ outb(lsb, dev->iobase + devpriv->ao_offset_lsb[chan]);
+ outb(msb, dev->iobase + devpriv->ao_offset_msb[chan]);
+#else
+ outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan));
+ outb(msb, dev->iobase + DAS08AO_AO_MSB(chan));
+#endif
+
+ /* load DACs */
+ inb(dev->iobase + DAS08AO_AO_UPDATE);
+ }
+
+ return n;
+}
+
+static unsigned int i8254_read_channel_low(unsigned int base, int chan)
+{
+ unsigned int msb, lsb;
+
+ /* The following instructions must be in order.
+ We must avoid other process reading the counter's value in the
+ middle.
+ The spin_lock isn't needed since ioctl calls grab the big kernel
+ lock automatically */
+ /*spin_lock(sp); */
+ outb(chan << 6, base + I8254_CTRL);
+ base += chan;
+ lsb = inb(base);
+ msb = inb(base);
+ /*spin_unlock(sp); */
+
+ return lsb | (msb << 8);
+}
+
+static void i8254_write_channel_low(unsigned int base, int chan,
+ unsigned int value)
+{
+ unsigned int msb, lsb;
+
+ lsb = value & 0xFF;
+ msb = value >> 8;
+
+ /* write lsb, then msb */
+ base += chan;
+ /* See comments in i8254_read_channel_low */
+ /*spin_lock(sp); */
+ outb(lsb, base);
+ outb(msb, base);
+ /*spin_unlock(sp); */
+}
+
+static unsigned int i8254_read_channel(struct i8254_struct *st, int channel)
+{
+ int chan = st->logic2phys[channel];
+
+ return i8254_read_channel_low(st->iobase, chan);
+}
+
+static void i8254_write_channel(struct i8254_struct *st, int channel,
+ unsigned int value)
+{
+ int chan = st->logic2phys[channel];
+
+ i8254_write_channel_low(st->iobase, chan, value);
+}
+
+static void i8254_initialize(struct i8254_struct *st)
+{
+ int i;
+ for (i = 0; i < 3; ++i)
+ i8254_set_mode_low(st->iobase, i, st->mode[i]);
+}
+
+static void i8254_set_mode_low(unsigned int base, int channel,
+ unsigned int mode)
+{
+ outb((channel << 6) | 0x30 | (mode & 0x0F), base + I8254_CTRL);
+}
+
+static void i8254_set_mode(struct i8254_struct *st, int channel,
+ unsigned int mode)
+{
+ int chan = st->logic2phys[channel];
+
+ st->mode[chan] = mode;
+ return i8254_set_mode_low(st->iobase, chan, mode);
+}
+
+static unsigned int i8254_read_status_low(unsigned int base, int channel)
+{
+ outb(0xE0 | (2 << channel), base + I8254_CTRL);
+ return inb(base + channel);
+}
+
+static unsigned int i8254_read_status(struct i8254_struct *st, int channel)
+{
+ int chan = st->logic2phys[channel];
+
+ return i8254_read_status_low(st->iobase, chan);
+}
+
+static int das08_counter_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = insn->chanspec;
+
+ //printk("Reading counter channel %d ",chan);
+ data[0] = i8254_read_channel(&devpriv->i8254, chan);
+ //printk("=> 0x%08X\n",data[0]);
+
+ return 1;
+}
+
+static int das08_counter_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = insn->chanspec;
+
+ //printk("Writing counter channel %d with 0x%04X\n",chan,data[0]);
+ i8254_write_channel(&devpriv->i8254, chan, data[0]);
+
+ return 1;
+}
+
+static int das08_counter_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = insn->chanspec;
+
+ if (insn->n != 2)
+ return -EINVAL;
+
+ switch (data[0]) {
+ case INSN_CONFIG_SET_COUNTER_MODE:
+ i8254_set_mode(&devpriv->i8254, chan, data[1]);
+ break;
+ case INSN_CONFIG_8254_READ_STATUS:
+ data[1] = i8254_read_status(&devpriv->i8254, chan);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return 2;
+}
+
+static int das08_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+
+static struct comedi_driver driver_das08 = {
+ driver_name: DRV_NAME,
+ module:THIS_MODULE,
+ attach:das08_attach,
+ detach:das08_common_detach,
+ board_name:&das08_boards[0].name,
+ num_names:sizeof(das08_boards) /
+ sizeof(struct das08_board_struct),
+ offset:sizeof(struct das08_board_struct),
+};
+
+int das08_common_attach(struct comedi_device * dev, unsigned long iobase)
+{
+ struct comedi_subdevice *s;
+ int ret;
+
+ // allocate ioports for non-pcmcia, non-pci boards
+ if ((thisboard->bustype != pcmcia) && (thisboard->bustype != pci)) {
+ printk(" iobase 0x%lx\n", iobase);
+ if (!request_region(iobase, thisboard->iosize, DRV_NAME)) {
+ printk(" I/O port conflict\n");
+ return -EIO;
+ }
+ }
+ dev->iobase = iobase;
+
+ dev->board_name = thisboard->name;
+
+ if ((ret = alloc_subdevices(dev, 6)) < 0)
+ return ret;
+
+ s = dev->subdevices + 0;
+ /* ai */
+ if (thisboard->ai) {
+ s->type = COMEDI_SUBD_AI;
+ /* XXX some boards actually have differential inputs instead of single ended.
+ * The driver does nothing with arefs though, so it's no big deal. */
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 8;
+ s->maxdata = (1 << thisboard->ai_nbits) - 1;
+ s->range_table = das08_ai_lranges[thisboard->ai_pg];
+ s->insn_read = thisboard->ai;
+ devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg];
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = dev->subdevices + 1;
+ /* ao */
+ if (thisboard->ao) {
+ s->type = COMEDI_SUBD_AO;
+// XXX lacks read-back insn
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = (1 << thisboard->ao_nbits) - 1;
+ s->range_table = &range_bipolar5;
+ s->insn_write = thisboard->ao;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = dev->subdevices + 2;
+ /* di */
+ if (thisboard->di) {
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = (thisboard->di == das08_di_rbits) ? 3 : 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = thisboard->di;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = dev->subdevices + 3;
+ /* do */
+ if (thisboard->do_) {
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = thisboard->do_nchan;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = thisboard->do_;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = dev->subdevices + 4;
+ /* 8255 */
+ if (thisboard->i8255_offset != 0) {
+ subdev_8255_init(dev, s, NULL, (unsigned long)(dev->iobase +
+ thisboard->i8255_offset));
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = dev->subdevices + 5;
+ /* 8254 */
+ if (thisboard->i8254_offset != 0) {
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 3;
+ s->maxdata = 0xFFFF;
+ s->insn_read = das08_counter_read;
+ s->insn_write = das08_counter_write;
+ s->insn_config = das08_counter_config;
+ /* Set-up the 8254 structure */
+ devpriv->i8254.channels = 3;
+ devpriv->i8254.logic2phys[0] = 0;
+ devpriv->i8254.logic2phys[1] = 1;
+ devpriv->i8254.logic2phys[2] = 2;
+ devpriv->i8254.iobase = iobase + thisboard->i8254_offset;
+ devpriv->i8254.mode[0] =
+ devpriv->i8254.mode[1] =
+ devpriv->i8254.mode[2] = I8254_MODE0 | I8254_BINARY;
+ i8254_initialize(&devpriv->i8254);
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ return 0;
+}
+
+static int das08_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int ret;
+ unsigned long iobase;
+#ifdef CONFIG_COMEDI_PCI
+ unsigned long pci_iobase = 0;
+ struct pci_dev *pdev;
+#endif
+
+ if ((ret = alloc_private(dev, sizeof(struct das08_private_struct))) < 0)
+ return ret;
+
+ printk("comedi%d: das08: ", dev->minor);
+ // deal with a pci board
+ if (thisboard->bustype == pci) {
+#ifdef CONFIG_COMEDI_PCI
+ if (it->options[0] || it->options[1]) {
+ printk("bus %i slot %i ",
+ it->options[0], it->options[1]);
+ }
+ printk("\n");
+ // find card
+ for (pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pdev != NULL;
+ pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) {
+ if (pdev->vendor == PCI_VENDOR_ID_COMPUTERBOARDS
+ && pdev->device == PCI_DEVICE_ID_PCIDAS08) {
+ if (it->options[0] || it->options[1]) {
+ if (pdev->bus->number == it->options[0]
+ && PCI_SLOT(pdev->devfn) ==
+ it->options[1]) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ if (!pdev) {
+ printk("No pci das08 cards found\n");
+ return -EIO;
+ }
+ devpriv->pdev = pdev;
+ // enable PCI device and reserve I/O spaces
+ if (comedi_pci_enable(pdev, DRV_NAME)) {
+ printk(" Error enabling PCI device and requesting regions\n");
+ return -EIO;
+ }
+ // read base addresses
+ pci_iobase = pci_resource_start(pdev, 1);
+ iobase = pci_resource_start(pdev, 2);
+ printk("pcibase 0x%lx iobase 0x%lx\n", pci_iobase, iobase);
+ devpriv->pci_iobase = pci_iobase;
+#if 0
+/* We could enable to pci-das08's interrupt here to make it possible
+ * to do timed input in this driver, but there is little point since
+ * conversions would have to be started by the interrupt handler
+ * so you might as well use comedi_rt_timer to emulate commands
+ */
+ /* set source of interrupt trigger to counter2 output */
+ outb(CNTRL_INTR | CNTRL_DIR, pci_iobase + CNTRL);
+ /* Enable local interrupt 1 and pci interrupt */
+ outw(INTR1_ENABLE | PCI_INTR_ENABLE, pci_iobase + INTCSR);
+#endif
+#else /* CONFIG_COMEDI_PCI */
+ printk("this driver has not been built with PCI support.\n");
+ return -EINVAL;
+#endif /* CONFIG_COMEDI_PCI */
+ } else {
+ iobase = it->options[0];
+ }
+ printk("\n");
+
+ return das08_common_attach(dev, iobase);
+}
+
+int das08_common_detach(struct comedi_device * dev)
+{
+ printk(KERN_INFO "comedi%d: das08: remove\n", dev->minor);
+
+ if (dev->subdevices)
+ subdev_8255_cleanup(dev, dev->subdevices + 4);
+
+ // deallocate ioports for non-pcmcia, non-pci boards
+ if ((thisboard->bustype != pcmcia) && (thisboard->bustype != pci)) {
+ if (dev->iobase)
+ release_region(dev->iobase, thisboard->iosize);
+ }
+
+#ifdef CONFIG_COMEDI_PCI
+ if (devpriv) {
+ if (devpriv->pdev) {
+ if (devpriv->pci_iobase) {
+ comedi_pci_disable(devpriv->pdev);
+ }
+ pci_dev_put(devpriv->pdev);
+ }
+ }
+#endif
+
+ return 0;
+}
+
+#ifdef CONFIG_COMEDI_PCI
+COMEDI_PCI_INITCLEANUP(driver_das08, das08_pci_table);
+#else
+COMEDI_INITCLEANUP(driver_das08);
+#endif
+
+EXPORT_SYMBOL_GPL(das08_common_attach);
+EXPORT_SYMBOL_GPL(das08_common_detach);
+#ifdef CONFIG_COMEDI_PCMCIA
+EXPORT_SYMBOL_GPL(das08_cs_boards);
+#endif
diff --git a/drivers/staging/comedi/drivers/das08.h b/drivers/staging/comedi/drivers/das08.h
new file mode 100644
index 000000000000..69089ce690f2
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das08.h
@@ -0,0 +1,78 @@
+/*
+ das08.h
+
+ Header for das08.c and das08_cs.c
+
+ Copyright (C) 2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ 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 _DAS08_H
+#define _DAS08_H
+
+enum das08_bustype { isa, pci, pcmcia, pc104 };
+// different ways ai data is encoded in first two registers
+enum das08_ai_encoding { das08_encode12, das08_encode16, das08_pcm_encode12 };
+enum das08_lrange { das08_pg_none, das08_bipolar5, das08_pgh, das08_pgl,
+ das08_pgm };
+
+struct das08_board_struct {
+ const char *name;
+ unsigned int id; // id for pci/pcmcia boards
+ enum das08_bustype bustype;
+ void *ai;
+ unsigned int ai_nbits;
+ enum das08_lrange ai_pg;
+ enum das08_ai_encoding ai_encoding;
+ void *ao;
+ unsigned int ao_nbits;
+ void *di;
+ void *do_;
+ unsigned int do_nchan;
+ unsigned int i8255_offset;
+ unsigned int i8254_offset;
+ unsigned int iosize; // number of ioports used
+};
+
+struct i8254_struct {
+ int channels; // available channels. Some could be used internally.
+ int logic2phys[3]; // to know which physical channel is.
+ int mode[3]; // the index is the real counter.
+ unsigned int iobase;
+};
+
+#define I8254_CNT0 0
+#define I8254_CNT1 1
+#define I8254_CNT2 2
+#define I8254_CTRL 3
+
+struct das08_private_struct {
+ unsigned int do_mux_bits; // bits for do/mux register on boards without seperate do register
+ unsigned int do_bits; // bits for do register on boards with register dedicated to digital out only
+ const unsigned int *pg_gainlist;
+ struct pci_dev *pdev; // struct for pci-das08
+ unsigned int pci_iobase; // additional base address for pci-das08
+ struct i8254_struct i8254;
+};
+
+#define NUM_DAS08_CS_BOARDS 2
+extern struct das08_board_struct das08_cs_boards[NUM_DAS08_CS_BOARDS];
+
+int das08_common_attach(struct comedi_device * dev, unsigned long iobase);
+int das08_common_detach(struct comedi_device * dev);
+
+#endif /* _DAS08_H */
diff --git a/drivers/staging/comedi/drivers/das08_cs.c b/drivers/staging/comedi/drivers/das08_cs.c
new file mode 100644
index 000000000000..be6c88788c4b
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das08_cs.c
@@ -0,0 +1,488 @@
+/*
+ comedi/drivers/das08_cs.c
+ DAS08 driver
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ Copyright (C) 2001,2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ 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.
+
+*****************************************************************
+
+*/
+/*
+Driver: das08_cs
+Description: DAS-08 PCMCIA boards
+Author: Warren Jasper, ds, Frank Hess
+Devices: [ComputerBoards] PCM-DAS08 (pcm-das08)
+Status: works
+
+This is the PCMCIA-specific support split off from the
+das08 driver.
+
+Options (for pcm-das08):
+ NONE
+
+Command support does not exist, but could be added for this board.
+*/
+
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include "das08.h"
+
+// pcmcia includes
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+static struct pcmcia_device *cur_dev = NULL;
+
+#define thisboard ((const struct das08_board_struct *)dev->board_ptr)
+
+static int das08_cs_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+
+static struct comedi_driver driver_das08_cs = {
+ driver_name:"das08_cs",
+ module:THIS_MODULE,
+ attach:das08_cs_attach,
+ detach:das08_common_detach,
+ board_name:&das08_cs_boards[0].name,
+ num_names:sizeof(das08_cs_boards) /
+ sizeof(struct das08_board_struct),
+ offset:sizeof(struct das08_board_struct),
+};
+
+static int das08_cs_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int ret;
+ unsigned long iobase;
+ struct pcmcia_device *link = cur_dev; // XXX hack
+
+ if ((ret = alloc_private(dev, sizeof(struct das08_private_struct))) < 0)
+ return ret;
+
+ printk("comedi%d: das08_cs: ", dev->minor);
+ // deal with a pci board
+
+ if (thisboard->bustype == pcmcia) {
+ if (link == NULL) {
+ printk(" no pcmcia cards found\n");
+ return -EIO;
+ }
+ iobase = link->io.BasePort1;
+ } else {
+ printk(" bug! board does not have PCMCIA bustype\n");
+ return -EINVAL;
+ }
+
+ printk("\n");
+
+ return das08_common_attach(dev, iobase);
+}
+
+/*======================================================================
+
+ The following pcmcia code for the pcm-das08 is adapted from the
+ dummy_cs.c driver of the Linux PCMCIA Card Services package.
+
+ The initial developer of the original code is David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+======================================================================*/
+
+/*
+ All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
+ you do not define PCMCIA_DEBUG at all, all the debug code will be
+ left out. If you compile with PCMCIA_DEBUG=0, the debug code will
+ be present but disabled -- but it can then be enabled for specific
+ modules at load time with a 'pc_debug=#' option to insmod.
+*/
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0644);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static const char *version =
+ "das08.c pcmcia code (Frank Hess), modified from dummy_cs.c 1.31 2001/08/24 12:13:13 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+static void das08_pcmcia_config(struct pcmcia_device *link);
+static void das08_pcmcia_release(struct pcmcia_device *link);
+static int das08_pcmcia_suspend(struct pcmcia_device *p_dev);
+static int das08_pcmcia_resume(struct pcmcia_device *p_dev);
+
+/*
+ The attach() and detach() entry points are used to create and destroy
+ "instances" of the driver, where each instance represents everything
+ needed to manage one actual PCMCIA card.
+*/
+
+static int das08_pcmcia_attach(struct pcmcia_device *);
+static void das08_pcmcia_detach(struct pcmcia_device *);
+
+/*
+ You'll also need to prototype all the functions that will actually
+ be used to talk to your device. See 'memory_cs' for a good example
+ of a fully self-sufficient driver; the other drivers rely more or
+ less on other parts of the kernel.
+*/
+
+/*
+ The dev_info variable is the "key" that is used to match up this
+ device driver with appropriate cards, through the card configuration
+ database.
+*/
+
+static const dev_info_t dev_info = "pcm-das08";
+
+struct local_info_t {
+ struct pcmcia_device *link;
+ dev_node_t node;
+ int stop;
+ struct bus_operations *bus;
+};
+
+/*======================================================================
+
+ das08_pcmcia_attach() creates an "instance" of the driver, allocating
+ local data structures for one device. The device is registered
+ with Card Services.
+
+ The dev_link structure is initialized, but we don't actually
+ configure the card at this point -- we wait until we receive a
+ card insertion event.
+
+======================================================================*/
+
+static int das08_pcmcia_attach(struct pcmcia_device *link)
+{
+ struct local_info_t *local;
+
+ DEBUG(0, "das08_pcmcia_attach()\n");
+
+ /* Allocate space for private device-specific data */
+ local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
+ if (!local)
+ return -ENOMEM;
+ local->link = link;
+ link->priv = local;
+
+ /* Interrupt setup */
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Handler = NULL;
+
+ /*
+ General socket configuration defaults can go here. In this
+ client, we assume very little, and rely on the CIS for almost
+ everything. In most clients, many details (i.e., number, sizes,
+ and attributes of IO windows) are fixed by the nature of the
+ device, and can be hard-wired here.
+ */
+ link->conf.Attributes = 0;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ cur_dev = link;
+
+ das08_pcmcia_config(link);
+
+ return 0;
+} /* das08_pcmcia_attach */
+
+/*======================================================================
+
+ This deletes a driver "instance". The device is de-registered
+ with Card Services. If it has been released, all local data
+ structures are freed. Otherwise, the structures will be freed
+ when the device is released.
+
+======================================================================*/
+
+static void das08_pcmcia_detach(struct pcmcia_device *link)
+{
+
+ DEBUG(0, "das08_pcmcia_detach(0x%p)\n", link);
+
+ if (link->dev_node) {
+ ((struct local_info_t *) link->priv)->stop = 1;
+ das08_pcmcia_release(link);
+ }
+
+ /* This points to the parent struct local_info_t struct */
+ if (link->priv)
+ kfree(link->priv);
+
+} /* das08_pcmcia_detach */
+
+/*======================================================================
+
+ das08_pcmcia_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ device available to the system.
+
+======================================================================*/
+
+static void das08_pcmcia_config(struct pcmcia_device *link)
+{
+ struct local_info_t *dev = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ int last_fn, last_ret;
+ u_char buf[64];
+ cistpl_cftable_entry_t dflt = { 0 };
+
+ DEBUG(0, "das08_pcmcia_config(0x%p)\n", link);
+
+ /*
+ This reads the card's CONFIG tuple to find its configuration
+ registers.
+ */
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ tuple.Attributes = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.TupleOffset = 0;
+ last_fn = GetFirstTuple;
+ if ((last_ret = pcmcia_get_first_tuple(link, &tuple)) != 0)
+ goto cs_failed;
+ last_fn = GetTupleData;
+ if ((last_ret = pcmcia_get_tuple_data(link, &tuple)) != 0)
+ goto cs_failed;
+ last_fn = ParseTuple;
+ if ((last_ret = pcmcia_parse_tuple(&tuple, &parse)) != 0)
+ goto cs_failed;
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+
+ /*
+ In this loop, we scan the CIS for configuration table entries,
+ each of which describes a valid card configuration, including
+ voltage, IO window, memory window, and interrupt settings.
+
+ We make no assumptions about the card to be configured: we use
+ just the information available in the CIS. In an ideal world,
+ this would work for any PCMCIA card, but it requires a complete
+ and accurate CIS. In practice, a driver usually "knows" most of
+ these things without consulting the CIS, and most client drivers
+ will only use the CIS to fill in implementation-defined details.
+ */
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ last_fn = GetFirstTuple;
+ if ((last_ret = pcmcia_get_first_tuple(link, &tuple)) != 0)
+ goto cs_failed;
+ while (1) {
+ cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
+ if ((last_ret = pcmcia_get_tuple_data(link, &tuple)) != 0)
+ goto next_entry;
+ if ((last_ret = pcmcia_parse_tuple(&tuple, &parse)) != 0)
+ goto next_entry;
+
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+ dflt = *cfg;
+ if (cfg->index == 0)
+ goto next_entry;
+ link->conf.ConfigIndex = cfg->index;
+
+ /* Does this card need audio output? */
+/* if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ link->conf.Attributes |= CONF_ENABLE_SPKR;
+ link->conf.Status = CCSR_AUDIO_ENA;
+ }
+*/
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
+ link->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ link->io.NumPorts1 = link->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ link->io.BasePort1 = io->win[0].base;
+ link->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ link->io.Attributes2 = link->io.Attributes1;
+ link->io.BasePort2 = io->win[1].base;
+ link->io.NumPorts2 = io->win[1].len;
+ }
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(link, &link->io) != 0)
+ goto next_entry;
+ }
+
+ /* If we got this far, we're cool! */
+ break;
+
+ next_entry:
+ last_fn = GetNextTuple;
+ if ((last_ret = pcmcia_get_next_tuple(link, &tuple)) != 0)
+ goto cs_failed;
+ }
+
+ if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+ last_fn = RequestIRQ;
+ if ((last_ret = pcmcia_request_irq(link, &link->irq)) != 0)
+ goto cs_failed;
+ }
+
+ /*
+ This actually configures the PCMCIA socket -- setting up
+ the I/O windows and the interrupt mapping, and putting the
+ card and host interface into "Memory and IO" mode.
+ */
+ last_fn = RequestConfiguration;
+ if ((last_ret = pcmcia_request_configuration(link, &link->conf)) != 0)
+ goto cs_failed;
+
+ /*
+ At this point, the dev_node_t structure(s) need to be
+ initialized and arranged in a linked list at link->dev.
+ */
+ sprintf(dev->node.dev_name, "pcm-das08");
+ dev->node.major = dev->node.minor = 0;
+ link->dev_node = &dev->node;
+
+ /* Finally, report what we've done */
+ printk(KERN_INFO "%s: index 0x%02x",
+ dev->node.dev_name, link->conf.ConfigIndex);
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ printk(", irq %u", link->irq.AssignedIRQ);
+ if (link->io.NumPorts1)
+ printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+ link->io.BasePort1 + link->io.NumPorts1 - 1);
+ if (link->io.NumPorts2)
+ printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+ link->io.BasePort2 + link->io.NumPorts2 - 1);
+ printk("\n");
+
+ return;
+
+ cs_failed:
+ cs_error(link, last_fn, last_ret);
+ das08_pcmcia_release(link);
+
+} /* das08_pcmcia_config */
+
+/*======================================================================
+
+ After a card is removed, das08_pcmcia_release() will unregister the
+ device, and release the PCMCIA configuration. If the device is
+ still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void das08_pcmcia_release(struct pcmcia_device *link)
+{
+ DEBUG(0, "das08_pcmcia_release(0x%p)\n", link);
+ pcmcia_disable_device(link);
+} /* das08_pcmcia_release */
+
+/*======================================================================
+
+ The card status event handler. Mostly, this schedules other
+ stuff to run after an event is received.
+
+ When a CARD_REMOVAL event is received, we immediately set a
+ private flag to block future accesses to this device. All the
+ functions that actually access the device should check this flag
+ to make sure the card is still present.
+
+======================================================================*/
+
+static int das08_pcmcia_suspend(struct pcmcia_device *link)
+{
+ struct local_info_t *local = link->priv;
+ /* Mark the device as stopped, to block IO until later */
+ local->stop = 1;
+
+ return 0;
+} /* das08_pcmcia_suspend */
+
+static int das08_pcmcia_resume(struct pcmcia_device *link)
+{
+ struct local_info_t *local = link->priv;
+
+ local->stop = 0;
+ return 0;
+} /* das08_pcmcia_resume */
+
+/*====================================================================*/
+
+static struct pcmcia_device_id das08_cs_id_table[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4001),
+ PCMCIA_DEVICE_NULL
+};
+
+MODULE_DEVICE_TABLE(pcmcia, das08_cs_id_table);
+
+struct pcmcia_driver das08_cs_driver = {
+ .probe = das08_pcmcia_attach,
+ .remove = das08_pcmcia_detach,
+ .suspend = das08_pcmcia_suspend,
+ .resume = das08_pcmcia_resume,
+ .id_table = das08_cs_id_table,
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = dev_info,
+ },
+};
+
+static int __init init_das08_pcmcia_cs(void)
+{
+ DEBUG(0, "%s\n", version);
+ pcmcia_register_driver(&das08_cs_driver);
+ return 0;
+}
+
+static void __exit exit_das08_pcmcia_cs(void)
+{
+ DEBUG(0, "das08_pcmcia_cs: unloading\n");
+ pcmcia_unregister_driver(&das08_cs_driver);
+}
+
+static int __init das08_cs_init_module(void)
+{
+ int ret;
+
+ ret = init_das08_pcmcia_cs();
+ if (ret < 0)
+ return ret;
+
+ return comedi_driver_register(&driver_das08_cs);
+}
+
+static void __exit das08_cs_exit_module(void)
+{
+ exit_das08_pcmcia_cs();
+ comedi_driver_unregister(&driver_das08_cs);
+}
+
+MODULE_LICENSE("GPL");
+module_init(das08_cs_init_module);
+module_exit(das08_cs_exit_module);
diff --git a/drivers/staging/comedi/drivers/das16.c b/drivers/staging/comedi/drivers/das16.c
new file mode 100644
index 000000000000..6b6b042099de
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das16.c
@@ -0,0 +1,1730 @@
+/*
+ comedi/drivers/das16.c
+ DAS16 driver
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ Copyright (C) 2000 Chris R. Baugher <baugher@enteract.com>
+ Copyright (C) 2001,2002 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ 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.
+
+************************************************************************
+*/
+/*
+Driver: das16
+Description: DAS16 compatible boards
+Author: Sam Moore, Warren Jasper, ds, Chris Baugher, Frank Hess, Roman Fietze
+Devices: [Keithley Metrabyte] DAS-16 (das-16), DAS-16G (das-16g),
+ DAS-16F (das-16f), DAS-1201 (das-1201), DAS-1202 (das-1202),
+ DAS-1401 (das-1401), DAS-1402 (das-1402), DAS-1601 (das-1601),
+ DAS-1602 (das-1602),
+ [ComputerBoards] PC104-DAS16/JR (pc104-das16jr),
+ PC104-DAS16JR/16 (pc104-das16jr/16),
+ CIO-DAS16JR/16 (cio-das16jr/16),
+ CIO-DAS16/JR (cio-das16/jr), CIO-DAS1401/12 (cio-das1401/12),
+ CIO-DAS1402/12 (cio-das1402/12), CIO-DAS1402/16 (cio-das1402/16),
+ CIO-DAS1601/12 (cio-das1601/12), CIO-DAS1602/12 (cio-das1602/12),
+ CIO-DAS1602/16 (cio-das1602/16), CIO-DAS16/330 (cio-das16/330)
+Status: works
+Updated: 2003-10-12
+
+A rewrite of the das16 and das1600 drivers.
+Options:
+ [0] - base io address
+ [1] - irq (does nothing, irq is not used anymore)
+ [2] - dma (optional, required for comedi_command support)
+ [3] - master clock speed in MHz (optional, 1 or 10, ignored if
+ board can probe clock, defaults to 1)
+ [4] - analog input range lowest voltage in microvolts (optional,
+ only useful if your board does not have software
+ programmable gain)
+ [5] - analog input range highest voltage in microvolts (optional,
+ only useful if board does not have software programmable
+ gain)
+ [6] - analog output range lowest voltage in microvolts (optional)
+ [7] - analog output range highest voltage in microvolts (optional)
+ [8] - use timer mode for DMA. Timer mode is needed e.g. for
+ buggy DMA controllers in NS CS5530A (Geode Companion), and for
+ 'jr' cards that lack a hardware fifo. This option is no
+ longer needed, since timer mode is _always_ used.
+
+Passing a zero for an option is the same as leaving it unspecified.
+
+*/
+/*
+
+Testing and debugging help provided by Daniel Koch.
+
+Keithley Manuals:
+ 2309.PDF (das16)
+ 4919.PDF (das1400, 1600)
+ 4922.PDF (das-1400)
+ 4923.PDF (das1200, 1400, 1600)
+
+Computer boards manuals also available from their website www.measurementcomputing.com
+
+*/
+
+#include <linux/pci.h>
+#include <asm/dma.h>
+#include "../comedidev.h"
+
+#include "8253.h"
+#include "8255.h"
+#include "comedi_fc.h"
+
+#undef DEBUG
+//#define DEBUG
+
+#ifdef DEBUG
+#define DEBUG_PRINT(format, args...) rt_printk("das16: " format, ## args)
+#else
+#define DEBUG_PRINT(format, args...)
+#endif
+
+#define DAS16_SIZE 20 // number of ioports
+#define DAS16_DMA_SIZE 0xff00 // size in bytes of allocated dma buffer
+
+/*
+ cio-das16.pdf
+
+ "das16"
+ "das16/f"
+
+ 0 a/d bits 0-3 start 12 bit
+ 1 a/d bits 4-11 unused
+ 2 mux read mux set
+ 3 di 4 bit do 4 bit
+ 4 unused ao0_lsb
+ 5 unused ao0_msb
+ 6 unused ao1_lsb
+ 7 unused ao1_msb
+ 8 status eoc uni/bip interrupt reset
+ 9 dma, int, trig ctrl set dma, int
+ a pacer control unused
+ b reserved reserved
+ cdef 8254
+ 0123 8255
+
+*/
+
+/*
+ cio-das16jr.pdf
+
+ "das16jr"
+
+ 0 a/d bits 0-3 start 12 bit
+ 1 a/d bits 4-11 unused
+ 2 mux read mux set
+ 3 di 4 bit do 4 bit
+ 4567 unused unused
+ 8 status eoc uni/bip interrupt reset
+ 9 dma, int, trig ctrl set dma, int
+ a pacer control unused
+ b gain status gain control
+ cdef 8254
+
+*/
+
+/*
+ cio-das16jr_16.pdf
+
+ "das16jr_16"
+
+ 0 a/d bits 0-7 start 16 bit
+ 1 a/d bits 8-15 unused
+ 2 mux read mux set
+ 3 di 4 bit do 4 bit
+ 4567 unused unused
+ 8 status eoc uni/bip interrupt reset
+ 9 dma, int, trig ctrl set dma, int
+ a pacer control unused
+ b gain status gain control
+ cdef 8254
+
+*/
+/*
+ cio-das160x-1x.pdf
+
+ "das1601/12"
+ "das1602/12"
+ "das1602/16"
+
+ 0 a/d bits 0-3 start 12 bit
+ 1 a/d bits 4-11 unused
+ 2 mux read mux set
+ 3 di 4 bit do 4 bit
+ 4 unused ao0_lsb
+ 5 unused ao0_msb
+ 6 unused ao1_lsb
+ 7 unused ao1_msb
+ 8 status eoc uni/bip interrupt reset
+ 9 dma, int, trig ctrl set dma, int
+ a pacer control unused
+ b gain status gain control
+ cdef 8254
+ 400 8255
+ 404 unused conversion enable
+ 405 unused burst enable
+ 406 unused das1600 enable
+ 407 status
+
+*/
+
+static const int sample_size = 2; // size in bytes of a sample from board
+
+#define DAS16_TRIG 0
+#define DAS16_AI_LSB 0
+#define DAS16_AI_MSB 1
+#define DAS16_MUX 2
+#define DAS16_DIO 3
+#define DAS16_AO_LSB(x) ((x)?6:4)
+#define DAS16_AO_MSB(x) ((x)?7:5)
+#define DAS16_STATUS 8
+#define BUSY (1<<7)
+#define UNIPOLAR (1<<6)
+#define DAS16_MUXBIT (1<<5)
+#define DAS16_INT (1<<4)
+#define DAS16_CONTROL 9
+#define DAS16_INTE (1<<7)
+#define DAS16_IRQ(x) (((x) & 0x7) << 4)
+#define DMA_ENABLE (1<<2)
+#define PACING_MASK 0x3
+#define INT_PACER 0x03
+#define EXT_PACER 0x02
+#define DAS16_SOFT 0x00
+#define DAS16_PACER 0x0A
+#define DAS16_CTR0 (1<<1)
+#define DAS16_TRIG0 (1<<0)
+#define BURST_LEN_BITS(x) (((x) & 0xf) << 4)
+#define DAS16_GAIN 0x0B
+#define DAS16_CNTR0_DATA 0x0C
+#define DAS16_CNTR1_DATA 0x0D
+#define DAS16_CNTR2_DATA 0x0E
+#define DAS16_CNTR_CONTROL 0x0F
+#define DAS16_TERM_CNT 0x00
+#define DAS16_ONE_SHOT 0x02
+#define DAS16_RATE_GEN 0x04
+#define DAS16_CNTR_LSB_MSB 0x30
+#define DAS16_CNTR0 0x00
+#define DAS16_CNTR1 0x40
+#define DAS16_CNTR2 0x80
+
+#define DAS1600_CONV 0x404
+#define DAS1600_CONV_DISABLE 0x40
+#define DAS1600_BURST 0x405
+#define DAS1600_BURST_VAL 0x40
+#define DAS1600_ENABLE 0x406
+#define DAS1600_ENABLE_VAL 0x40
+#define DAS1600_STATUS_B 0x407
+#define DAS1600_BME 0x40
+#define DAS1600_ME 0x20
+#define DAS1600_CD 0x10
+#define DAS1600_WS 0x02
+#define DAS1600_CLK_10MHZ 0x01
+
+static const struct comedi_lrange range_das1x01_bip = { 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.01),
+ }
+};
+static const struct comedi_lrange range_das1x01_unip = { 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01),
+ }
+};
+static const struct comedi_lrange range_das1x02_bip = { 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ }
+};
+static const struct comedi_lrange range_das1x02_unip = { 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ }
+};
+static const struct comedi_lrange range_das16jr = { 9, {
+ // also used by 16/330
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ }
+};
+static const struct comedi_lrange range_das16jr_16 = { 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ }
+};
+
+static const int das16jr_gainlist[] = { 8, 0, 1, 2, 3, 4, 5, 6, 7 };
+static const int das16jr_16_gainlist[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+static const int das1600_gainlist[] = { 0, 1, 2, 3 };
+enum {
+ das16_pg_none = 0,
+ das16_pg_16jr,
+ das16_pg_16jr_16,
+ das16_pg_1601,
+ das16_pg_1602,
+};
+static const int *const das16_gainlists[] = {
+ NULL,
+ das16jr_gainlist,
+ das16jr_16_gainlist,
+ das1600_gainlist,
+ das1600_gainlist,
+};
+static const struct comedi_lrange *const das16_ai_uni_lranges[] = {
+ &range_unknown,
+ &range_das16jr,
+ &range_das16jr_16,
+ &range_das1x01_unip,
+ &range_das1x02_unip,
+};
+static const struct comedi_lrange *const das16_ai_bip_lranges[] = {
+ &range_unknown,
+ &range_das16jr,
+ &range_das16jr_16,
+ &range_das1x01_bip,
+ &range_das1x02_bip,
+};
+
+struct munge_info {
+ uint8_t byte;
+ unsigned have_byte:1;
+};
+
+static int das16_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das16_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das16_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das16_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int das16_cmd_test(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int das16_cmd_exec(struct comedi_device * dev, struct comedi_subdevice * s);
+static int das16_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static void das16_ai_munge(struct comedi_device * dev, struct comedi_subdevice * s,
+ void *array, unsigned int num_bytes, unsigned int start_chan_index);
+
+static void das16_reset(struct comedi_device * dev);
+static irqreturn_t das16_dma_interrupt(int irq, void *d PT_REGS_ARG);
+static void das16_timer_interrupt(unsigned long arg);
+static void das16_interrupt(struct comedi_device * dev);
+
+static unsigned int das16_set_pacer(struct comedi_device * dev, unsigned int ns,
+ int flags);
+static int das1600_mode_detect(struct comedi_device * dev);
+static unsigned int das16_suggest_transfer_size(struct comedi_device * dev,
+ struct comedi_cmd cmd);
+
+static void reg_dump(struct comedi_device * dev);
+
+struct das16_board {
+ const char *name;
+ void *ai;
+ unsigned int ai_nbits;
+ unsigned int ai_speed; // max conversion speed in nanosec
+ unsigned int ai_pg;
+ void *ao;
+ unsigned int ao_nbits;
+ void *di;
+ void *do_;
+
+ unsigned int i8255_offset;
+ unsigned int i8254_offset;
+
+ unsigned int size;
+ unsigned int id;
+};
+
+static const struct das16_board das16_boards[] = {
+ {
+ name: "das-16",
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:15000,
+ ai_pg: das16_pg_none,
+ ao: das16_ao_winsn,
+ ao_nbits:12,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0x10,
+ i8254_offset:0x0c,
+ size: 0x14,
+ id: 0x00,
+ },
+ {
+ name: "das-16g",
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:15000,
+ ai_pg: das16_pg_none,
+ ao: das16_ao_winsn,
+ ao_nbits:12,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0x10,
+ i8254_offset:0x0c,
+ size: 0x14,
+ id: 0x00,
+ },
+ {
+ name: "das-16f",
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:8500,
+ ai_pg: das16_pg_none,
+ ao: das16_ao_winsn,
+ ao_nbits:12,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0x10,
+ i8254_offset:0x0c,
+ size: 0x14,
+ id: 0x00,
+ },
+ {
+ name: "cio-das16", // cio-das16.pdf
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:20000,
+ ai_pg: das16_pg_none,
+ ao: das16_ao_winsn,
+ ao_nbits:12,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0x10,
+ i8254_offset:0x0c,
+ size: 0x14,
+ id: 0x80,
+ },
+ {
+ name: "cio-das16/f", // das16.pdf
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:10000,
+ ai_pg: das16_pg_none,
+ ao: das16_ao_winsn,
+ ao_nbits:12,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0x10,
+ i8254_offset:0x0c,
+ size: 0x14,
+ id: 0x80,
+ },
+ {
+ name: "cio-das16/jr", // cio-das16jr.pdf
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:7692,
+ ai_pg: das16_pg_16jr,
+ ao: NULL,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0,
+ i8254_offset:0x0c,
+ size: 0x10,
+ id: 0x00,
+ },
+ {
+ name: "pc104-das16jr", // pc104-das16jr_xx.pdf
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:3300,
+ ai_pg: das16_pg_16jr,
+ ao: NULL,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0,
+ i8254_offset:0x0c,
+ size: 0x10,
+ id: 0x00,
+ },
+ {
+ name: "cio-das16jr/16", // cio-das16jr_16.pdf
+ ai: das16_ai_rinsn,
+ ai_nbits:16,
+ ai_speed:10000,
+ ai_pg: das16_pg_16jr_16,
+ ao: NULL,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0,
+ i8254_offset:0x0c,
+ size: 0x10,
+ id: 0x00,
+ },
+ {
+ name: "pc104-das16jr/16", // pc104-das16jr_xx.pdf
+ ai: das16_ai_rinsn,
+ ai_nbits:16,
+ ai_speed:10000,
+ ai_pg: das16_pg_16jr_16,
+ ao: NULL,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0,
+ i8254_offset:0x0c,
+ size: 0x10,
+ id: 0x00,
+ },
+ {
+ name: "das-1201", // 4924.pdf (keithley user's manual)
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:20000,
+ ai_pg: das16_pg_none,
+ ao: NULL,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0x400,
+ i8254_offset:0x0c,
+ size: 0x408,
+ id: 0x20,
+ },
+ {
+ name: "das-1202", // 4924.pdf (keithley user's manual)
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:10000,
+ ai_pg: das16_pg_none,
+ ao: NULL,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0x400,
+ i8254_offset:0x0c,
+ size: 0x408,
+ id: 0x20,
+ },
+ {
+ name: "das-1401", // 4919.pdf and 4922.pdf (keithley user's manual)
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:10000,
+ ai_pg: das16_pg_1601,
+ ao: NULL,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0x0,
+ i8254_offset:0x0c,
+ size: 0x408,
+ id: 0xc0 // 4919.pdf says id bits are 0xe0, 4922.pdf says 0xc0
+ },
+ {
+ name: "das-1402", // 4919.pdf and 4922.pdf (keithley user's manual)
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:10000,
+ ai_pg: das16_pg_1602,
+ ao: NULL,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0x0,
+ i8254_offset:0x0c,
+ size: 0x408,
+ id: 0xc0 // 4919.pdf says id bits are 0xe0, 4922.pdf says 0xc0
+ },
+ {
+ name: "das-1601", // 4919.pdf
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:10000,
+ ai_pg: das16_pg_1601,
+ ao: das16_ao_winsn,
+ ao_nbits:12,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0x400,
+ i8254_offset:0x0c,
+ size: 0x408,
+ id: 0xc0},
+ {
+ name: "das-1602", // 4919.pdf
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:10000,
+ ai_pg: das16_pg_1602,
+ ao: das16_ao_winsn,
+ ao_nbits:12,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0x400,
+ i8254_offset:0x0c,
+ size: 0x408,
+ id: 0xc0},
+ {
+ name: "cio-das1401/12", // cio-das1400_series.pdf
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:6250,
+ ai_pg: das16_pg_1601,
+ ao: NULL,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0,
+ i8254_offset:0x0c,
+ size: 0x408,
+ id: 0xc0},
+ {
+ name: "cio-das1402/12", // cio-das1400_series.pdf
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:6250,
+ ai_pg: das16_pg_1602,
+ ao: NULL,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0,
+ i8254_offset:0x0c,
+ size: 0x408,
+ id: 0xc0},
+ {
+ name: "cio-das1402/16", // cio-das1400_series.pdf
+ ai: das16_ai_rinsn,
+ ai_nbits:16,
+ ai_speed:10000,
+ ai_pg: das16_pg_1602,
+ ao: NULL,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0,
+ i8254_offset:0x0c,
+ size: 0x408,
+ id: 0xc0},
+ {
+ name: "cio-das1601/12", // cio-das160x-1x.pdf
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:6250,
+ ai_pg: das16_pg_1601,
+ ao: das16_ao_winsn,
+ ao_nbits:12,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0x400,
+ i8254_offset:0x0c,
+ size: 0x408,
+ id: 0xc0},
+ {
+ name: "cio-das1602/12", // cio-das160x-1x.pdf
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:10000,
+ ai_pg: das16_pg_1602,
+ ao: das16_ao_winsn,
+ ao_nbits:12,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0x400,
+ i8254_offset:0x0c,
+ size: 0x408,
+ id: 0xc0},
+ {
+ name: "cio-das1602/16", // cio-das160x-1x.pdf
+ ai: das16_ai_rinsn,
+ ai_nbits:16,
+ ai_speed:10000,
+ ai_pg: das16_pg_1602,
+ ao: das16_ao_winsn,
+ ao_nbits:12,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0x400,
+ i8254_offset:0x0c,
+ size: 0x408,
+ id: 0xc0},
+ {
+ name: "cio-das16/330", // ?
+ ai: das16_ai_rinsn,
+ ai_nbits:12,
+ ai_speed:3030,
+ ai_pg: das16_pg_16jr,
+ ao: NULL,
+ di: das16_di_rbits,
+ do_: das16_do_wbits,
+ i8255_offset:0,
+ i8254_offset:0x0c,
+ size: 0x14,
+ id: 0xf0},
+#if 0
+ {
+ name: "das16/330i", // ?
+ },
+ {
+ name: "das16/jr/ctr5", // ?
+ },
+ {
+ name: "cio-das16/m1/16", // cio-das16_m1_16.pdf, this board is a bit quirky, no dma
+ },
+#endif
+};
+
+#define n_das16_boards ((sizeof(das16_boards))/(sizeof(struct das16_board)))
+
+static int das16_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int das16_detach(struct comedi_device * dev);
+static struct comedi_driver driver_das16 = {
+ driver_name:"das16",
+ module:THIS_MODULE,
+ attach:das16_attach,
+ detach:das16_detach,
+ board_name:&das16_boards[0].name,
+ num_names:n_das16_boards,
+ offset:sizeof(das16_boards[0]),
+};
+
+#define DAS16_TIMEOUT 1000
+
+/* Period for timer interrupt in jiffies. It's a function
+ * to deal with possibility of dynamic HZ patches */
+static inline int timer_period(void)
+{
+ return HZ / 20;
+}
+struct das16_private_struct {
+ unsigned int ai_unipolar; // unipolar flag
+ unsigned int ai_singleended; // single ended flag
+ unsigned int clockbase; // master clock speed in ns
+ volatile unsigned int control_state; // dma, interrupt and trigger control bits
+ volatile unsigned long adc_byte_count; // number of bytes remaining
+ unsigned int divisor1; // divisor dividing master clock to get conversion frequency
+ unsigned int divisor2; // divisor dividing master clock to get conversion frequency
+ unsigned int dma_chan; // dma channel
+ uint16_t *dma_buffer[2];
+ dma_addr_t dma_buffer_addr[2];
+ unsigned int current_buffer;
+ volatile unsigned int dma_transfer_size; // target number of bytes to transfer per dma shot
+ // user-defined analog input and output ranges defined from config options
+ struct comedi_lrange *user_ai_range_table;
+ struct comedi_lrange *user_ao_range_table;
+
+ struct timer_list timer; // for timed interrupt
+ volatile short timer_running;
+ volatile short timer_mode; // true if using timer mode
+};
+#define devpriv ((struct das16_private_struct *)(dev->private))
+#define thisboard ((struct das16_board *)(dev->board_ptr))
+
+static int das16_cmd_test(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0, tmp;
+ int gain, start_chan, i;
+ int mask;
+
+ /* make sure triggers are valid */
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ mask = TRIG_FOLLOW;
+ // if board supports burst mode
+ if (thisboard->size > 0x400)
+ mask |= TRIG_TIMER | TRIG_EXT;
+ cmd->scan_begin_src &= mask;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ mask = TRIG_TIMER | TRIG_EXT;
+ // if board supports burst mode
+ if (thisboard->size > 0x400)
+ mask |= TRIG_NOW;
+ cmd->convert_src &= mask;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_EXT &&
+ cmd->scan_begin_src != TRIG_FOLLOW)
+ err++;
+ if (cmd->convert_src != TRIG_TIMER &&
+ cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW)
+ err++;
+ if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
+ err++;
+
+ // make sure scan_begin_src and convert_src dont conflict
+ if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW)
+ err++;
+ if (cmd->scan_begin_src != TRIG_FOLLOW && cmd->convert_src != TRIG_NOW)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ /* internal trigger */
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ // check against maximum frequency
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->scan_begin_arg <
+ thisboard->ai_speed * cmd->chanlist_len) {
+ cmd->scan_begin_arg =
+ thisboard->ai_speed * cmd->chanlist_len;
+ err++;
+ }
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < thisboard->ai_speed) {
+ cmd->convert_arg = thisboard->ai_speed;
+ err++;
+ }
+ }
+
+ if (cmd->stop_src == TRIG_NONE) {
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+ if (err)
+ return 3;
+
+ // step 4: fix up arguments
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ unsigned int tmp = cmd->scan_begin_arg;
+ // set divisors, correct timing arguments
+ i8253_cascade_ns_to_timer_2div(devpriv->clockbase,
+ &(devpriv->divisor1), &(devpriv->divisor2),
+ &(cmd->scan_begin_arg), cmd->flags & TRIG_ROUND_MASK);
+ err += (tmp != cmd->scan_begin_arg);
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ unsigned int tmp = cmd->convert_arg;
+ // set divisors, correct timing arguments
+ i8253_cascade_ns_to_timer_2div(devpriv->clockbase,
+ &(devpriv->divisor1), &(devpriv->divisor2),
+ &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK);
+ err += (tmp != cmd->convert_arg);
+ }
+ if (err)
+ return 4;
+
+ // check channel/gain list against card's limitations
+ if (cmd->chanlist) {
+ gain = CR_RANGE(cmd->chanlist[0]);
+ start_chan = CR_CHAN(cmd->chanlist[0]);
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ if (CR_CHAN(cmd->chanlist[i]) !=
+ (start_chan + i) % s->n_chan) {
+ comedi_error(dev,
+ "entries in chanlist must be consecutive channels, counting upwards\n");
+ err++;
+ }
+ if (CR_RANGE(cmd->chanlist[i]) != gain) {
+ comedi_error(dev,
+ "entries in chanlist must all have the same gain\n");
+ err++;
+ }
+ }
+ }
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int das16_cmd_exec(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int byte;
+ unsigned long flags;
+ int range;
+
+ if (devpriv->dma_chan == 0 || (dev->irq == 0
+ && devpriv->timer_mode == 0)) {
+ comedi_error(dev,
+ "irq (or use of 'timer mode') dma required to execute comedi_cmd");
+ return -1;
+ }
+ if (cmd->flags & TRIG_RT) {
+ comedi_error(dev,
+ "isa dma transfers cannot be performed with TRIG_RT, aborting");
+ return -1;
+ }
+
+ devpriv->adc_byte_count =
+ cmd->stop_arg * cmd->chanlist_len * sizeof(uint16_t);
+
+ // disable conversions for das1600 mode
+ if (thisboard->size > 0x400) {
+ outb(DAS1600_CONV_DISABLE, dev->iobase + DAS1600_CONV);
+ }
+ // set scan limits
+ byte = CR_CHAN(cmd->chanlist[0]);
+ byte |= CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]) << 4;
+ outb(byte, dev->iobase + DAS16_MUX);
+
+ /* set gain (this is also burst rate register but according to
+ * computer boards manual, burst rate does nothing, even on keithley cards) */
+ if (thisboard->ai_pg != das16_pg_none) {
+ range = CR_RANGE(cmd->chanlist[0]);
+ outb((das16_gainlists[thisboard->ai_pg])[range],
+ dev->iobase + DAS16_GAIN);
+ }
+
+ /* set counter mode and counts */
+ cmd->convert_arg =
+ das16_set_pacer(dev, cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ DEBUG_PRINT("pacer period: %d ns\n", cmd->convert_arg);
+
+ /* enable counters */
+ byte = 0;
+ /* Enable burst mode if appropriate. */
+ if (thisboard->size > 0x400) {
+ if (cmd->convert_src == TRIG_NOW) {
+ outb(DAS1600_BURST_VAL, dev->iobase + DAS1600_BURST);
+ // set burst length
+ byte |= BURST_LEN_BITS(cmd->chanlist_len - 1);
+ } else {
+ outb(0, dev->iobase + DAS1600_BURST);
+ }
+ }
+ outb(byte, dev->iobase + DAS16_PACER);
+
+ // set up dma transfer
+ flags = claim_dma_lock();
+ disable_dma(devpriv->dma_chan);
+ /* clear flip-flop to make sure 2-byte registers for
+ * count and address get set correctly */
+ clear_dma_ff(devpriv->dma_chan);
+ devpriv->current_buffer = 0;
+ set_dma_addr(devpriv->dma_chan,
+ devpriv->dma_buffer_addr[devpriv->current_buffer]);
+ // set appropriate size of transfer
+ devpriv->dma_transfer_size = das16_suggest_transfer_size(dev, *cmd);
+ set_dma_count(devpriv->dma_chan, devpriv->dma_transfer_size);
+ enable_dma(devpriv->dma_chan);
+ release_dma_lock(flags);
+
+ // set up interrupt
+ if (devpriv->timer_mode) {
+ devpriv->timer_running = 1;
+ devpriv->timer.expires = jiffies + timer_period();
+ add_timer(&devpriv->timer);
+ devpriv->control_state &= ~DAS16_INTE;
+ } else {
+ /* clear interrupt bit */
+ outb(0x00, dev->iobase + DAS16_STATUS);
+ /* enable interrupts */
+ devpriv->control_state |= DAS16_INTE;
+ }
+ devpriv->control_state |= DMA_ENABLE;
+ devpriv->control_state &= ~PACING_MASK;
+ if (cmd->convert_src == TRIG_EXT)
+ devpriv->control_state |= EXT_PACER;
+ else
+ devpriv->control_state |= INT_PACER;
+ outb(devpriv->control_state, dev->iobase + DAS16_CONTROL);
+
+ /* Enable conversions if using das1600 mode */
+ if (thisboard->size > 0x400) {
+ outb(0, dev->iobase + DAS1600_CONV);
+ }
+
+ return 0;
+}
+
+static int das16_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ /* disable interrupts, dma and pacer clocked conversions */
+ devpriv->control_state &= ~DAS16_INTE & ~PACING_MASK & ~DMA_ENABLE;
+ outb(devpriv->control_state, dev->iobase + DAS16_CONTROL);
+ if (devpriv->dma_chan)
+ disable_dma(devpriv->dma_chan);
+
+ // disable SW timer
+ if (devpriv->timer_mode && devpriv->timer_running) {
+ devpriv->timer_running = 0;
+ del_timer(&devpriv->timer);
+ }
+
+ /* disable burst mode */
+ if (thisboard->size > 0x400) {
+ outb(0, dev->iobase + DAS1600_BURST);
+ }
+
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return 0;
+}
+
+static void das16_reset(struct comedi_device * dev)
+{
+ outb(0, dev->iobase + DAS16_STATUS);
+ outb(0, dev->iobase + DAS16_CONTROL);
+ outb(0, dev->iobase + DAS16_PACER);
+ outb(0, dev->iobase + DAS16_CNTR_CONTROL);
+}
+
+static int das16_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i, n;
+ int range;
+ int chan;
+ int msb, lsb;
+
+ // disable interrupts and pacing
+ devpriv->control_state &= ~DAS16_INTE & ~DMA_ENABLE & ~PACING_MASK;
+ outb(devpriv->control_state, dev->iobase + DAS16_CONTROL);
+
+ /* set multiplexer */
+ chan = CR_CHAN(insn->chanspec);
+ chan |= CR_CHAN(insn->chanspec) << 4;
+ outb(chan, dev->iobase + DAS16_MUX);
+
+ /* set gain */
+ if (thisboard->ai_pg != das16_pg_none) {
+ range = CR_RANGE(insn->chanspec);
+ outb((das16_gainlists[thisboard->ai_pg])[range],
+ dev->iobase + DAS16_GAIN);
+ }
+
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion */
+ outb_p(0, dev->iobase + DAS16_TRIG);
+
+ for (i = 0; i < DAS16_TIMEOUT; i++) {
+ if (!(inb(dev->iobase + DAS16_STATUS) & BUSY))
+ break;
+ }
+ if (i == DAS16_TIMEOUT) {
+ rt_printk("das16: timeout\n");
+ return -ETIME;
+ }
+ msb = inb(dev->iobase + DAS16_AI_MSB);
+ lsb = inb(dev->iobase + DAS16_AI_LSB);
+ if (thisboard->ai_nbits == 12) {
+ data[n] = ((lsb >> 4) & 0xf) | (msb << 4);
+ } else {
+ data[n] = lsb | (msb << 8);
+ }
+ }
+
+ return n;
+}
+
+static int das16_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int bits;
+
+ bits = inb(dev->iobase + DAS16_DIO) & 0xf;
+ data[1] = bits;
+ data[0] = 0;
+
+ return 2;
+}
+
+static int das16_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int wbits;
+
+ // only set bits that have been masked
+ data[0] &= 0xf;
+ wbits = s->state;
+ // zero bits that have been masked
+ wbits &= ~data[0];
+ // set masked bits
+ wbits |= data[0] & data[1];
+ s->state = wbits;
+ data[1] = wbits;
+
+ outb(s->state, dev->iobase + DAS16_DIO);
+
+ return 2;
+}
+
+static int das16_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int lsb, msb;
+ int chan;
+
+ chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++) {
+ if (thisboard->ao_nbits == 12) {
+ lsb = (data[i] << 4) & 0xff;
+ msb = (data[i] >> 4) & 0xff;
+ } else {
+ lsb = data[i] & 0xff;
+ msb = (data[i] >> 8) & 0xff;
+ }
+ outb(lsb, dev->iobase + DAS16_AO_LSB(chan));
+ outb(msb, dev->iobase + DAS16_AO_MSB(chan));
+ }
+
+ return i;
+}
+
+static irqreturn_t das16_dma_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ int status;
+ struct comedi_device *dev = d;
+
+ status = inb(dev->iobase + DAS16_STATUS);
+
+ if ((status & DAS16_INT) == 0) {
+ DEBUG_PRINT("spurious interrupt\n");
+ return IRQ_NONE;
+ }
+
+ /* clear interrupt */
+ outb(0x00, dev->iobase + DAS16_STATUS);
+ das16_interrupt(dev);
+ return IRQ_HANDLED;
+}
+
+static void das16_timer_interrupt(unsigned long arg)
+{
+ struct comedi_device *dev = (struct comedi_device *) arg;
+
+ das16_interrupt(dev);
+
+ if (devpriv->timer_running)
+ mod_timer(&devpriv->timer, jiffies + timer_period());
+}
+
+/* the pc104-das16jr (at least) has problems if the dma
+ transfer is interrupted in the middle of transferring
+ a 16 bit sample, so this function takes care to get
+ an even transfer count after disabling dma
+ channel.
+*/
+static int disable_dma_on_even(struct comedi_device * dev)
+{
+ int residue;
+ int i;
+ static const int disable_limit = 100;
+ static const int enable_timeout = 100;
+ disable_dma(devpriv->dma_chan);
+ residue = get_dma_residue(devpriv->dma_chan);
+ for (i = 0; i < disable_limit && (residue % 2); ++i) {
+ int j;
+ enable_dma(devpriv->dma_chan);
+ for (j = 0; j < enable_timeout; ++j) {
+ int new_residue;
+ comedi_udelay(2);
+ new_residue = get_dma_residue(devpriv->dma_chan);
+ if (new_residue != residue)
+ break;
+ }
+ disable_dma(devpriv->dma_chan);
+ residue = get_dma_residue(devpriv->dma_chan);
+ }
+ if (i == disable_limit) {
+ comedi_error(dev,
+ "failed to get an even dma transfer, could be trouble.");
+ }
+ return residue;
+}
+
+static void das16_interrupt(struct comedi_device * dev)
+{
+ unsigned long dma_flags, spin_flags;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async;
+ struct comedi_cmd *cmd;
+ int num_bytes, residue;
+ int buffer_index;
+
+ if (dev->attached == 0) {
+ comedi_error(dev, "premature interrupt");
+ return;
+ }
+ // initialize async here to make sure it is not NULL
+ async = s->async;
+ cmd = &async->cmd;
+
+ if (devpriv->dma_chan == 0) {
+ comedi_error(dev, "interrupt with no dma channel?");
+ return;
+ }
+
+ comedi_spin_lock_irqsave(&dev->spinlock, spin_flags);
+ if ((devpriv->control_state & DMA_ENABLE) == 0) {
+ comedi_spin_unlock_irqrestore(&dev->spinlock, spin_flags);
+ DEBUG_PRINT("interrupt while dma disabled?\n");
+ return;
+ }
+
+ dma_flags = claim_dma_lock();
+ clear_dma_ff(devpriv->dma_chan);
+ residue = disable_dma_on_even(dev);
+
+ // figure out how many points to read
+ if (residue > devpriv->dma_transfer_size) {
+ comedi_error(dev, "residue > transfer size!\n");
+ async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ num_bytes = 0;
+ } else
+ num_bytes = devpriv->dma_transfer_size - residue;
+
+ if (cmd->stop_src == TRIG_COUNT && num_bytes >= devpriv->adc_byte_count) {
+ num_bytes = devpriv->adc_byte_count;
+ async->events |= COMEDI_CB_EOA;
+ }
+
+ buffer_index = devpriv->current_buffer;
+ devpriv->current_buffer = (devpriv->current_buffer + 1) % 2;
+ devpriv->adc_byte_count -= num_bytes;
+
+ // figure out how many bytes for next transfer
+ if (cmd->stop_src == TRIG_COUNT && devpriv->timer_mode == 0 &&
+ devpriv->dma_transfer_size > devpriv->adc_byte_count)
+ devpriv->dma_transfer_size = devpriv->adc_byte_count;
+
+ // re-enable dma
+ if ((async->events & COMEDI_CB_EOA) == 0) {
+ set_dma_addr(devpriv->dma_chan,
+ devpriv->dma_buffer_addr[devpriv->current_buffer]);
+ set_dma_count(devpriv->dma_chan, devpriv->dma_transfer_size);
+ enable_dma(devpriv->dma_chan);
+ /* reenable conversions for das1600 mode, (stupid hardware) */
+ if (thisboard->size > 0x400 && devpriv->timer_mode == 0) {
+ outb(0x00, dev->iobase + DAS1600_CONV);
+ }
+ }
+ release_dma_lock(dma_flags);
+
+ comedi_spin_unlock_irqrestore(&dev->spinlock, spin_flags);
+
+ cfc_write_array_to_buffer(s,
+ devpriv->dma_buffer[buffer_index], num_bytes);
+
+ cfc_handle_events(dev, s);
+}
+
+static unsigned int das16_set_pacer(struct comedi_device * dev, unsigned int ns,
+ int rounding_flags)
+{
+ i8253_cascade_ns_to_timer_2div(devpriv->clockbase, &(devpriv->divisor1),
+ &(devpriv->divisor2), &ns, rounding_flags & TRIG_ROUND_MASK);
+
+ /* Write the values of ctr1 and ctr2 into counters 1 and 2 */
+ i8254_load(dev->iobase + DAS16_CNTR0_DATA, 0, 1, devpriv->divisor1, 2);
+ i8254_load(dev->iobase + DAS16_CNTR0_DATA, 0, 2, devpriv->divisor2, 2);
+
+ return ns;
+}
+
+static void reg_dump(struct comedi_device * dev)
+{
+ DEBUG_PRINT("********DAS1600 REGISTER DUMP********\n");
+ DEBUG_PRINT("DAS16_MUX: %x\n", inb(dev->iobase + DAS16_MUX));
+ DEBUG_PRINT("DAS16_DIO: %x\n", inb(dev->iobase + DAS16_DIO));
+ DEBUG_PRINT("DAS16_STATUS: %x\n", inb(dev->iobase + DAS16_STATUS));
+ DEBUG_PRINT("DAS16_CONTROL: %x\n", inb(dev->iobase + DAS16_CONTROL));
+ DEBUG_PRINT("DAS16_PACER: %x\n", inb(dev->iobase + DAS16_PACER));
+ DEBUG_PRINT("DAS16_GAIN: %x\n", inb(dev->iobase + DAS16_GAIN));
+ DEBUG_PRINT("DAS16_CNTR_CONTROL: %x\n",
+ inb(dev->iobase + DAS16_CNTR_CONTROL));
+ DEBUG_PRINT("DAS1600_CONV: %x\n", inb(dev->iobase + DAS1600_CONV));
+ DEBUG_PRINT("DAS1600_BURST: %x\n", inb(dev->iobase + DAS1600_BURST));
+ DEBUG_PRINT("DAS1600_ENABLE: %x\n", inb(dev->iobase + DAS1600_ENABLE));
+ DEBUG_PRINT("DAS1600_STATUS_B: %x\n",
+ inb(dev->iobase + DAS1600_STATUS_B));
+}
+
+static int das16_probe(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int status;
+ int diobits;
+
+ /* status is available on all boards */
+
+ status = inb(dev->iobase + DAS16_STATUS);
+
+ if ((status & UNIPOLAR)) {
+ devpriv->ai_unipolar = 1;
+ } else {
+ devpriv->ai_unipolar = 0;
+ }
+
+ if ((status & DAS16_MUXBIT)) {
+ devpriv->ai_singleended = 1;
+ } else {
+ devpriv->ai_singleended = 0;
+ }
+
+ /* diobits indicates boards */
+
+ diobits = inb(dev->iobase + DAS16_DIO) & 0xf0;
+
+ printk(" id bits are 0x%02x\n", diobits);
+ if (thisboard->id != diobits) {
+ printk(" requested board's id bits are 0x%x (ignore)\n",
+ thisboard->id);
+ }
+
+ return 0;
+}
+
+static int das1600_mode_detect(struct comedi_device * dev)
+{
+ int status = 0;
+
+ status = inb(dev->iobase + DAS1600_STATUS_B);
+
+ if (status & DAS1600_CLK_10MHZ) {
+ devpriv->clockbase = 100;
+ printk(" 10MHz pacer clock\n");
+ } else {
+ devpriv->clockbase = 1000;
+ printk(" 1MHz pacer clock\n");
+ }
+
+ reg_dump(dev);
+
+ return 0;
+}
+
+/*
+ *
+ * Options list:
+ * 0 I/O base
+ * 1 IRQ
+ * 2 DMA
+ * 3 Clock speed (in MHz)
+ */
+
+static int das16_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int ret;
+ unsigned int irq;
+ unsigned long iobase;
+ unsigned int dma_chan;
+ int timer_mode;
+ unsigned long flags;
+ struct comedi_krange *user_ai_range, *user_ao_range;
+
+ iobase = it->options[0];
+#if 0
+ irq = it->options[1];
+ timer_mode = it->options[8];
+#endif
+ /* always use time_mode since using irq can drop samples while
+ * waiting for dma done interrupt (due to hardware limitations) */
+ irq = 0;
+ timer_mode = 1;
+ if (timer_mode)
+ irq = 0;
+
+ printk("comedi%d: das16:", dev->minor);
+
+ // check that clock setting is valid
+ if (it->options[3]) {
+ if (it->options[3] != 0 &&
+ it->options[3] != 1 && it->options[3] != 10) {
+ printk("\n Invalid option. Master clock must be set to 1 or 10 (MHz)\n");
+ return -EINVAL;
+ }
+ }
+
+ if ((ret = alloc_private(dev, sizeof(struct das16_private_struct))) < 0)
+ return ret;
+
+ if (thisboard->size < 0x400) {
+ printk(" 0x%04lx-0x%04lx\n", iobase, iobase + thisboard->size);
+ if (!request_region(iobase, thisboard->size, "das16")) {
+ printk(" I/O port conflict\n");
+ return -EIO;
+ }
+ } else {
+ printk(" 0x%04lx-0x%04lx 0x%04lx-0x%04lx\n",
+ iobase, iobase + 0x0f,
+ iobase + 0x400,
+ iobase + 0x400 + (thisboard->size & 0x3ff));
+ if (!request_region(iobase, 0x10, "das16")) {
+ printk(" I/O port conflict: 0x%04lx-0x%04lx\n",
+ iobase, iobase + 0x0f);
+ return -EIO;
+ }
+ if (!request_region(iobase + 0x400, thisboard->size & 0x3ff,
+ "das16")) {
+ release_region(iobase, 0x10);
+ printk(" I/O port conflict: 0x%04lx-0x%04lx\n",
+ iobase + 0x400,
+ iobase + 0x400 + (thisboard->size & 0x3ff));
+ return -EIO;
+ }
+ }
+
+ dev->iobase = iobase;
+
+ // probe id bits to make sure they are consistent
+ if (das16_probe(dev, it)) {
+ printk(" id bits do not match selected board, aborting\n");
+ return -EINVAL;
+ }
+ dev->board_name = thisboard->name;
+
+ // get master clock speed
+ if (thisboard->size < 0x400) {
+ if (it->options[3])
+ devpriv->clockbase = 1000 / it->options[3];
+ else
+ devpriv->clockbase = 1000; // 1 MHz default
+ } else {
+ das1600_mode_detect(dev);
+ }
+
+ /* now for the irq */
+ if (irq > 1 && irq < 8) {
+ if ((ret = comedi_request_irq(irq, das16_dma_interrupt, 0,
+ "das16", dev)) < 0)
+ return ret;
+ dev->irq = irq;
+ printk(" ( irq = %u )", irq);
+ } else if (irq == 0) {
+ printk(" ( no irq )");
+ } else {
+ printk(" invalid irq\n");
+ return -EINVAL;
+ }
+
+ // initialize dma
+ dma_chan = it->options[2];
+ if (dma_chan == 1 || dma_chan == 3) {
+ // allocate dma buffers
+ int i;
+ for (i = 0; i < 2; i++) {
+ devpriv->dma_buffer[i] = pci_alloc_consistent(NULL,
+ DAS16_DMA_SIZE, &devpriv->dma_buffer_addr[i]);
+ if (devpriv->dma_buffer[i] == NULL)
+ return -ENOMEM;
+ }
+ if (request_dma(dma_chan, "das16")) {
+ printk(" failed to allocate dma channel %i\n",
+ dma_chan);
+ return -EINVAL;
+ }
+ devpriv->dma_chan = dma_chan;
+ flags = claim_dma_lock();
+ disable_dma(devpriv->dma_chan);
+ set_dma_mode(devpriv->dma_chan, DMA_MODE_READ);
+ release_dma_lock(flags);
+ printk(" ( dma = %u)\n", dma_chan);
+ } else if (dma_chan == 0) {
+ printk(" ( no dma )\n");
+ } else {
+ printk(" invalid dma channel\n");
+ return -EINVAL;
+ }
+
+ // get any user-defined input range
+ if (thisboard->ai_pg == das16_pg_none &&
+ (it->options[4] || it->options[5])) {
+ // allocate single-range range table
+ devpriv->user_ai_range_table =
+ kmalloc(sizeof(struct comedi_lrange) + sizeof(struct comedi_krange),
+ GFP_KERNEL);
+ // initialize ai range
+ devpriv->user_ai_range_table->length = 1;
+ user_ai_range = devpriv->user_ai_range_table->range;
+ user_ai_range->min = it->options[4];
+ user_ai_range->max = it->options[5];
+ user_ai_range->flags = UNIT_volt;
+ }
+ // get any user-defined output range
+ if (it->options[6] || it->options[7]) {
+ // allocate single-range range table
+ devpriv->user_ao_range_table =
+ kmalloc(sizeof(struct comedi_lrange) + sizeof(struct comedi_krange),
+ GFP_KERNEL);
+ // initialize ao range
+ devpriv->user_ao_range_table->length = 1;
+ user_ao_range = devpriv->user_ao_range_table->range;
+ user_ao_range->min = it->options[6];
+ user_ao_range->max = it->options[7];
+ user_ao_range->flags = UNIT_volt;
+ }
+
+ if (timer_mode) {
+ init_timer(&(devpriv->timer));
+ devpriv->timer.function = das16_timer_interrupt;
+ devpriv->timer.data = (unsigned long)dev;
+ }
+ devpriv->timer_mode = timer_mode ? 1 : 0;
+
+ if ((ret = alloc_subdevices(dev, 5)) < 0)
+ return ret;
+
+ s = dev->subdevices + 0;
+ dev->read_subdev = s;
+ /* ai */
+ if (thisboard->ai) {
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+ if (devpriv->ai_singleended) {
+ s->n_chan = 16;
+ s->len_chanlist = 16;
+ s->subdev_flags |= SDF_GROUND;
+ } else {
+ s->n_chan = 8;
+ s->len_chanlist = 8;
+ s->subdev_flags |= SDF_DIFF;
+ }
+ s->maxdata = (1 << thisboard->ai_nbits) - 1;
+ if (devpriv->user_ai_range_table) { // user defined ai range
+ s->range_table = devpriv->user_ai_range_table;
+ } else if (devpriv->ai_unipolar) {
+ s->range_table = das16_ai_uni_lranges[thisboard->ai_pg];
+ } else {
+ s->range_table = das16_ai_bip_lranges[thisboard->ai_pg];
+ }
+ s->insn_read = thisboard->ai;
+ s->do_cmdtest = das16_cmd_test;
+ s->do_cmd = das16_cmd_exec;
+ s->cancel = das16_cancel;
+ s->munge = das16_ai_munge;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = dev->subdevices + 1;
+ /* ao */
+ if (thisboard->ao) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = (1 << thisboard->ao_nbits) - 1;
+ if (devpriv->user_ao_range_table) { // user defined ao range
+ s->range_table = devpriv->user_ao_range_table;
+ } else {
+ s->range_table = &range_unknown;
+ }
+ s->insn_write = thisboard->ao;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = dev->subdevices + 2;
+ /* di */
+ if (thisboard->di) {
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = thisboard->di;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = dev->subdevices + 3;
+ /* do */
+ if (thisboard->do_) {
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = thisboard->do_;
+ // initialize digital output lines
+ outb(s->state, dev->iobase + DAS16_DIO);
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = dev->subdevices + 4;
+ /* 8255 */
+ if (thisboard->i8255_offset != 0) {
+ subdev_8255_init(dev, s, NULL, (dev->iobase +
+ thisboard->i8255_offset));
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ das16_reset(dev);
+ /* set the interrupt level */
+ devpriv->control_state = DAS16_IRQ(dev->irq);
+ outb(devpriv->control_state, dev->iobase + DAS16_CONTROL);
+
+ // turn on das1600 mode if available
+ if (thisboard->size > 0x400) {
+ outb(DAS1600_ENABLE_VAL, dev->iobase + DAS1600_ENABLE);
+ outb(0, dev->iobase + DAS1600_CONV);
+ outb(0, dev->iobase + DAS1600_BURST);
+ }
+
+ return 0;
+}
+
+static int das16_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: das16: remove\n", dev->minor);
+
+ das16_reset(dev);
+
+ if (dev->subdevices)
+ subdev_8255_cleanup(dev, dev->subdevices + 4);
+
+ if (devpriv) {
+ int i;
+ for (i = 0; i < 2; i++) {
+ if (devpriv->dma_buffer[i])
+ pci_free_consistent(NULL, DAS16_DMA_SIZE,
+ devpriv->dma_buffer[i],
+ devpriv->dma_buffer_addr[i]);
+ }
+ if (devpriv->dma_chan)
+ free_dma(devpriv->dma_chan);
+ if (devpriv->user_ai_range_table)
+ kfree(devpriv->user_ai_range_table);
+ if (devpriv->user_ao_range_table)
+ kfree(devpriv->user_ao_range_table);
+ }
+
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+
+ if (dev->iobase) {
+ if (thisboard->size < 0x400) {
+ release_region(dev->iobase, thisboard->size);
+ } else {
+ release_region(dev->iobase, 0x10);
+ release_region(dev->iobase + 0x400,
+ thisboard->size & 0x3ff);
+ }
+ }
+
+ return 0;
+}
+
+COMEDI_INITCLEANUP(driver_das16);
+
+// utility function that suggests a dma transfer size in bytes
+static unsigned int das16_suggest_transfer_size(struct comedi_device * dev,
+ struct comedi_cmd cmd)
+{
+ unsigned int size;
+ unsigned int freq;
+
+ /* if we are using timer interrupt, we don't care how long it
+ * will take to complete transfer since it will be interrupted
+ * by timer interrupt */
+ if (devpriv->timer_mode)
+ return DAS16_DMA_SIZE;
+
+ /* otherwise, we are relying on dma terminal count interrupt,
+ * so pick a reasonable size */
+ if (cmd.convert_src == TRIG_TIMER)
+ freq = 1000000000 / cmd.convert_arg;
+ else if (cmd.scan_begin_src == TRIG_TIMER)
+ freq = (1000000000 / cmd.scan_begin_arg) * cmd.chanlist_len;
+ // return some default value
+ else
+ freq = 0xffffffff;
+
+ if (cmd.flags & TRIG_WAKE_EOS) {
+ size = sample_size * cmd.chanlist_len;
+ } else {
+ // make buffer fill in no more than 1/3 second
+ size = (freq / 3) * sample_size;
+ }
+
+ // set a minimum and maximum size allowed
+ if (size > DAS16_DMA_SIZE)
+ size = DAS16_DMA_SIZE - DAS16_DMA_SIZE % sample_size;
+ else if (size < sample_size)
+ size = sample_size;
+
+ if (cmd.stop_src == TRIG_COUNT && size > devpriv->adc_byte_count)
+ size = devpriv->adc_byte_count;
+
+ return size;
+}
+
+static void das16_ai_munge(struct comedi_device * dev, struct comedi_subdevice * s,
+ void *array, unsigned int num_bytes, unsigned int start_chan_index)
+{
+ unsigned int i, num_samples = num_bytes / sizeof(short);
+ short *data = array;
+
+ for (i = 0; i < num_samples; i++) {
+ data[i] = le16_to_cpu(data[i]);
+ if (thisboard->ai_nbits == 12) {
+ data[i] = (data[i] >> 4) & 0xfff;
+ }
+ }
+}
diff --git a/drivers/staging/comedi/drivers/das16m1.c b/drivers/staging/comedi/drivers/das16m1.c
new file mode 100644
index 000000000000..0e423e199ed6
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das16m1.c
@@ -0,0 +1,765 @@
+/*
+ comedi/drivers/das16m1.c
+ CIO-DAS16/M1 driver
+ Author: Frank Mori Hess, based on code from the das16
+ driver.
+ Copyright (C) 2001 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+************************************************************************
+*/
+/*
+Driver: das16m1
+Description: CIO-DAS16/M1
+Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+Devices: [Measurement Computing] CIO-DAS16/M1 (cio-das16/m1)
+Status: works
+
+This driver supports a single board - the CIO-DAS16/M1.
+As far as I know, there are no other boards that have
+the same register layout. Even the CIO-DAS16/M1/16 is
+significantly different.
+
+I was _barely_ able to reach the full 1 MHz capability
+of this board, using a hard real-time interrupt
+(set the TRIG_RT flag in your struct comedi_cmd and use
+rtlinux or RTAI). The board can't do dma, so the bottleneck is
+pulling the data across the ISA bus. I timed the interrupt
+handler, and it took my computer ~470 microseconds to pull 512
+samples from the board. So at 1 Mhz sampling rate,
+expect your CPU to be spending almost all of its
+time in the interrupt handler.
+
+This board has some unusual restrictions for its channel/gain list. If the
+list has 2 or more channels in it, then two conditions must be satisfied:
+(1) - even/odd channels must appear at even/odd indices in the list
+(2) - the list must have an even number of entries.
+
+Options:
+ [0] - base io address
+ [1] - irq (optional, but you probably want it)
+
+irq can be omitted, although the cmd interface will not work without it.
+*/
+
+#include <linux/ioport.h>
+#include "../comedidev.h"
+
+#include "8255.h"
+#include "8253.h"
+#include "comedi_fc.h"
+
+#define DAS16M1_SIZE 16
+#define DAS16M1_SIZE2 8
+
+#define DAS16M1_XTAL 100 //10 MHz master clock
+
+#define FIFO_SIZE 1024 // 1024 sample fifo
+
+/*
+ CIO-DAS16_M1.pdf
+
+ "cio-das16/m1"
+
+ 0 a/d bits 0-3, mux start 12 bit
+ 1 a/d bits 4-11 unused
+ 2 status control
+ 3 di 4 bit do 4 bit
+ 4 unused clear interrupt
+ 5 interrupt, pacer
+ 6 channel/gain queue address
+ 7 channel/gain queue data
+ 89ab 8254
+ cdef 8254
+ 400 8255
+ 404-407 8254
+
+*/
+
+#define DAS16M1_AI 0 // 16-bit wide register
+#define AI_CHAN(x) ((x) & 0xf)
+#define DAS16M1_CS 2
+#define EXT_TRIG_BIT 0x1
+#define OVRUN 0x20
+#define IRQDATA 0x80
+#define DAS16M1_DIO 3
+#define DAS16M1_CLEAR_INTR 4
+#define DAS16M1_INTR_CONTROL 5
+#define EXT_PACER 0x2
+#define INT_PACER 0x3
+#define PACER_MASK 0x3
+#define INTE 0x80
+#define DAS16M1_QUEUE_ADDR 6
+#define DAS16M1_QUEUE_DATA 7
+#define Q_CHAN(x) ((x) & 0x7)
+#define Q_RANGE(x) (((x) & 0xf) << 4)
+#define UNIPOLAR 0x40
+#define DAS16M1_8254_FIRST 0x8
+#define DAS16M1_8254_FIRST_CNTRL 0xb
+#define TOTAL_CLEAR 0x30
+#define DAS16M1_8254_SECOND 0xc
+#define DAS16M1_82C55 0x400
+#define DAS16M1_8254_THIRD 0x404
+
+static const struct comedi_lrange range_das16m1 = { 9,
+ {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ BIP_RANGE(10),
+ }
+};
+
+static int das16m1_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das16m1_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das16m1_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int das16m1_cmd_test(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int das16m1_cmd_exec(struct comedi_device * dev, struct comedi_subdevice * s);
+static int das16m1_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+
+static int das16m1_poll(struct comedi_device * dev, struct comedi_subdevice * s);
+static irqreturn_t das16m1_interrupt(int irq, void *d PT_REGS_ARG);
+static void das16m1_handler(struct comedi_device * dev, unsigned int status);
+
+static unsigned int das16m1_set_pacer(struct comedi_device * dev, unsigned int ns,
+ int round_flag);
+
+static int das16m1_irq_bits(unsigned int irq);
+
+struct das16m1_board {
+ const char *name;
+ unsigned int ai_speed;
+};
+
+static const struct das16m1_board das16m1_boards[] = {
+ {
+ name: "cio-das16/m1", // CIO-DAS16_M1.pdf
+ ai_speed:1000, // 1MHz max speed
+ },
+};
+
+#define das16m1_num_boards ((sizeof(das16m1_boards)) / (sizeof(das16m1_boards[0])))
+
+static int das16m1_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int das16m1_detach(struct comedi_device * dev);
+static struct comedi_driver driver_das16m1 = {
+ driver_name:"das16m1",
+ module:THIS_MODULE,
+ attach:das16m1_attach,
+ detach:das16m1_detach,
+ board_name:&das16m1_boards[0].name,
+ num_names:das16m1_num_boards,
+ offset:sizeof(das16m1_boards[0]),
+};
+
+struct das16m1_private_struct {
+ unsigned int control_state;
+ volatile unsigned int adc_count; // number of samples completed
+ /* initial value in lower half of hardware conversion counter,
+ * needed to keep track of whether new count has been loaded into
+ * counter yet (loaded by first sample conversion) */
+ u16 initial_hw_count;
+ short ai_buffer[FIFO_SIZE];
+ unsigned int do_bits; // saves status of digital output bits
+ unsigned int divisor1; // divides master clock to obtain conversion speed
+ unsigned int divisor2; // divides master clock to obtain conversion speed
+};
+#define devpriv ((struct das16m1_private_struct *)(dev->private))
+#define thisboard ((const struct das16m1_board *)(dev->board_ptr))
+
+COMEDI_INITCLEANUP(driver_das16m1);
+
+static inline short munge_sample(short data)
+{
+ return (data >> 4) & 0xfff;
+}
+
+static int das16m1_cmd_test(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ unsigned int err = 0, tmp, i;
+
+ /* make sure triggers are valid */
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_EXT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_FOLLOW;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+ if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
+ err++;
+ if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ /* internal trigger */
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < thisboard->ai_speed) {
+ cmd->convert_arg = thisboard->ai_speed;
+ err++;
+ }
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ /* any count is allowed */
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ /* calculate counter values that give desired timing */
+ i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL,
+ &(devpriv->divisor1), &(devpriv->divisor2),
+ &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->convert_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ // check chanlist against board's peculiarities
+ if (cmd->chanlist && cmd->chanlist_len > 1) {
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ // even/odd channels must go into even/odd queue addresses
+ if ((i % 2) != (CR_CHAN(cmd->chanlist[i]) % 2)) {
+ comedi_error(dev, "bad chanlist:\n"
+ " even/odd channels must go have even/odd chanlist indices");
+ err++;
+ }
+ }
+ if ((cmd->chanlist_len % 2) != 0) {
+ comedi_error(dev,
+ "chanlist must be of even length or length 1");
+ err++;
+ }
+ }
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int das16m1_cmd_exec(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int byte, i;
+
+ if (dev->irq == 0) {
+ comedi_error(dev, "irq required to execute comedi_cmd");
+ return -1;
+ }
+
+ /* disable interrupts and internal pacer */
+ devpriv->control_state &= ~INTE & ~PACER_MASK;
+ outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
+
+ // set software count
+ devpriv->adc_count = 0;
+ /* Initialize lower half of hardware counter, used to determine how
+ * many samples are in fifo. Value doesn't actually load into counter
+ * until counter's next clock (the next a/d conversion) */
+ i8254_load(dev->iobase + DAS16M1_8254_FIRST, 0, 1, 0, 2);
+ /* remember current reading of counter so we know when counter has
+ * actually been loaded */
+ devpriv->initial_hw_count =
+ i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
+ /* setup channel/gain queue */
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ outb(i, dev->iobase + DAS16M1_QUEUE_ADDR);
+ byte = Q_CHAN(CR_CHAN(cmd->
+ chanlist[i])) | Q_RANGE(CR_RANGE(cmd->
+ chanlist[i]));
+ outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
+ }
+
+ /* set counter mode and counts */
+ cmd->convert_arg =
+ das16m1_set_pacer(dev, cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+
+ // set control & status register
+ byte = 0;
+ /* if we are using external start trigger (also board dislikes having
+ * both start and conversion triggers external simultaneously) */
+ if (cmd->start_src == TRIG_EXT && cmd->convert_src != TRIG_EXT) {
+ byte |= EXT_TRIG_BIT;
+ }
+ outb(byte, dev->iobase + DAS16M1_CS);
+ /* clear interrupt bit */
+ outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
+
+ /* enable interrupts and internal pacer */
+ devpriv->control_state &= ~PACER_MASK;
+ if (cmd->convert_src == TRIG_TIMER) {
+ devpriv->control_state |= INT_PACER;
+ } else {
+ devpriv->control_state |= EXT_PACER;
+ }
+ devpriv->control_state |= INTE;
+ outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
+
+ return 0;
+}
+
+static int das16m1_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ devpriv->control_state &= ~INTE & ~PACER_MASK;
+ outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
+
+ return 0;
+}
+
+static int das16m1_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i, n;
+ int byte;
+ const int timeout = 1000;
+
+ /* disable interrupts and internal pacer */
+ devpriv->control_state &= ~INTE & ~PACER_MASK;
+ outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
+
+ /* setup channel/gain queue */
+ outb(0, dev->iobase + DAS16M1_QUEUE_ADDR);
+ byte = Q_CHAN(CR_CHAN(insn->chanspec)) | Q_RANGE(CR_RANGE(insn->
+ chanspec));
+ outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
+
+ for (n = 0; n < insn->n; n++) {
+ /* clear IRQDATA bit */
+ outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
+ /* trigger conversion */
+ outb(0, dev->iobase);
+
+ for (i = 0; i < timeout; i++) {
+ if (inb(dev->iobase + DAS16M1_CS) & IRQDATA)
+ break;
+ }
+ if (i == timeout) {
+ comedi_error(dev, "timeout");
+ return -ETIME;
+ }
+ data[n] = munge_sample(inw(dev->iobase));
+ }
+
+ return n;
+}
+
+static int das16m1_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int bits;
+
+ bits = inb(dev->iobase + DAS16M1_DIO) & 0xf;
+ data[1] = bits;
+ data[0] = 0;
+
+ return 2;
+}
+
+static int das16m1_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int wbits;
+
+ // only set bits that have been masked
+ data[0] &= 0xf;
+ wbits = devpriv->do_bits;
+ // zero bits that have been masked
+ wbits &= ~data[0];
+ // set masked bits
+ wbits |= data[0] & data[1];
+ devpriv->do_bits = wbits;
+ data[1] = wbits;
+
+ outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO);
+
+ return 2;
+}
+
+static int das16m1_poll(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long flags;
+ unsigned int status;
+
+ // prevent race with interrupt handler
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ status = inb(dev->iobase + DAS16M1_CS);
+ das16m1_handler(dev, status);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return s->async->buf_write_count - s->async->buf_read_count;
+}
+
+static irqreturn_t das16m1_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ int status;
+ struct comedi_device *dev = d;
+
+ if (dev->attached == 0) {
+ comedi_error(dev, "premature interrupt");
+ return IRQ_HANDLED;
+ }
+ // prevent race with comedi_poll()
+ spin_lock(&dev->spinlock);
+
+ status = inb(dev->iobase + DAS16M1_CS);
+
+ if ((status & (IRQDATA | OVRUN)) == 0) {
+ comedi_error(dev, "spurious interrupt");
+ spin_unlock(&dev->spinlock);
+ return IRQ_NONE;
+ }
+
+ das16m1_handler(dev, status);
+
+ /* clear interrupt */
+ outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
+
+ spin_unlock(&dev->spinlock);
+ return IRQ_HANDLED;
+}
+
+static void munge_sample_array(short * array, unsigned int num_elements)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_elements; i++) {
+ array[i] = munge_sample(array[i]);
+ }
+}
+
+static void das16m1_handler(struct comedi_device * dev, unsigned int status)
+{
+ struct comedi_subdevice *s;
+ struct comedi_async *async;
+ struct comedi_cmd *cmd;
+ u16 num_samples;
+ u16 hw_counter;
+
+ s = dev->read_subdev;
+ async = s->async;
+ async->events = 0;
+ cmd = &async->cmd;
+
+ // figure out how many samples are in fifo
+ hw_counter = i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
+ /* make sure hardware counter reading is not bogus due to initial value
+ * not having been loaded yet */
+ if (devpriv->adc_count == 0 && hw_counter == devpriv->initial_hw_count) {
+ num_samples = 0;
+ } else {
+ /* The calculation of num_samples looks odd, but it uses the following facts.
+ * 16 bit hardware counter is initialized with value of zero (which really
+ * means 0x1000). The counter decrements by one on each conversion
+ * (when the counter decrements from zero it goes to 0xffff). num_samples
+ * is a 16 bit variable, so it will roll over in a similar fashion to the
+ * hardware counter. Work it out, and this is what you get. */
+ num_samples = -hw_counter - devpriv->adc_count;
+ }
+ // check if we only need some of the points
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (num_samples > cmd->stop_arg * cmd->chanlist_len)
+ num_samples = cmd->stop_arg * cmd->chanlist_len;
+ }
+ // make sure we dont try to get too many points if fifo has overrun
+ if (num_samples > FIFO_SIZE)
+ num_samples = FIFO_SIZE;
+ insw(dev->iobase, devpriv->ai_buffer, num_samples);
+ munge_sample_array(devpriv->ai_buffer, num_samples);
+ cfc_write_array_to_buffer(s, devpriv->ai_buffer,
+ num_samples * sizeof(short));
+ devpriv->adc_count += num_samples;
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (devpriv->adc_count >= cmd->stop_arg * cmd->chanlist_len) { /* end of acquisition */
+ das16m1_cancel(dev, s);
+ async->events |= COMEDI_CB_EOA;
+ }
+ }
+
+ /* this probably won't catch overruns since the card doesn't generate
+ * overrun interrupts, but we might as well try */
+ if (status & OVRUN) {
+ das16m1_cancel(dev, s);
+ async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_error(dev, "fifo overflow");
+ }
+
+ comedi_event(dev, s);
+
+}
+
+/* This function takes a time in nanoseconds and sets the *
+ * 2 pacer clocks to the closest frequency possible. It also *
+ * returns the actual sampling period. */
+static unsigned int das16m1_set_pacer(struct comedi_device * dev, unsigned int ns,
+ int rounding_flags)
+{
+ i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL, &(devpriv->divisor1),
+ &(devpriv->divisor2), &ns, rounding_flags & TRIG_ROUND_MASK);
+
+ /* Write the values of ctr1 and ctr2 into counters 1 and 2 */
+ i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 1, devpriv->divisor1,
+ 2);
+ i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 2, devpriv->divisor2,
+ 2);
+
+ return ns;
+}
+
+static int das16m1_irq_bits(unsigned int irq)
+{
+ int ret;
+
+ switch (irq) {
+ case 10:
+ ret = 0x0;
+ break;
+ case 11:
+ ret = 0x1;
+ break;
+ case 12:
+ ret = 0x2;
+ break;
+ case 15:
+ ret = 0x3;
+ break;
+ case 2:
+ ret = 0x4;
+ break;
+ case 3:
+ ret = 0x5;
+ break;
+ case 5:
+ ret = 0x6;
+ break;
+ case 7:
+ ret = 0x7;
+ break;
+ default:
+ return -1;
+ break;
+ }
+ return (ret << 4);
+}
+
+/*
+ * Options list:
+ * 0 I/O base
+ * 1 IRQ
+ */
+
+static int das16m1_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int ret;
+ unsigned int irq;
+ unsigned long iobase;
+
+ iobase = it->options[0];
+
+ printk("comedi%d: das16m1:", dev->minor);
+
+ if ((ret = alloc_private(dev,
+ sizeof(struct das16m1_private_struct))) < 0)
+ return ret;
+
+ dev->board_name = thisboard->name;
+
+ printk(" io 0x%lx-0x%lx 0x%lx-0x%lx",
+ iobase, iobase + DAS16M1_SIZE,
+ iobase + DAS16M1_82C55, iobase + DAS16M1_82C55 + DAS16M1_SIZE2);
+ if (!request_region(iobase, DAS16M1_SIZE, driver_das16m1.driver_name)) {
+ printk(" I/O port conflict\n");
+ return -EIO;
+ }
+ if (!request_region(iobase + DAS16M1_82C55, DAS16M1_SIZE2,
+ driver_das16m1.driver_name)) {
+ release_region(iobase, DAS16M1_SIZE);
+ printk(" I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ /* now for the irq */
+ irq = it->options[1];
+ // make sure it is valid
+ if (das16m1_irq_bits(irq) >= 0) {
+ ret = comedi_request_irq(irq, das16m1_interrupt, 0,
+ driver_das16m1.driver_name, dev);
+ if (ret < 0) {
+ printk(", irq unavailable\n");
+ return ret;
+ }
+ dev->irq = irq;
+ printk(", irq %u\n", irq);
+ } else if (irq == 0) {
+ printk(", no irq\n");
+ } else {
+ printk(", invalid irq\n"
+ " valid irqs are 2, 3, 5, 7, 10, 11, 12, or 15\n");
+ return -EINVAL;
+ }
+
+ if ((ret = alloc_subdevices(dev, 4)) < 0)
+ return ret;
+
+ s = dev->subdevices + 0;
+ dev->read_subdev = s;
+ /* ai */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+ s->n_chan = 8;
+ s->subdev_flags = SDF_DIFF;
+ s->len_chanlist = 256;
+ s->maxdata = (1 << 12) - 1;
+ s->range_table = &range_das16m1;
+ s->insn_read = das16m1_ai_rinsn;
+ s->do_cmdtest = das16m1_cmd_test;
+ s->do_cmd = das16m1_cmd_exec;
+ s->cancel = das16m1_cancel;
+ s->poll = das16m1_poll;
+
+ s = dev->subdevices + 1;
+ /* di */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das16m1_di_rbits;
+
+ s = dev->subdevices + 2;
+ /* do */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das16m1_do_wbits;
+
+ s = dev->subdevices + 3;
+ /* 8255 */
+ subdev_8255_init(dev, s, NULL, dev->iobase + DAS16M1_82C55);
+
+ // disable upper half of hardware conversion counter so it doesn't mess with us
+ outb(TOTAL_CLEAR, dev->iobase + DAS16M1_8254_FIRST_CNTRL);
+
+ // initialize digital output lines
+ outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO);
+
+ /* set the interrupt level */
+ if (dev->irq)
+ devpriv->control_state = das16m1_irq_bits(dev->irq);
+ else
+ devpriv->control_state = 0;
+ outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
+
+ return 0;
+}
+
+static int das16m1_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: das16m1: remove\n", dev->minor);
+
+// das16m1_reset(dev);
+
+ if (dev->subdevices)
+ subdev_8255_cleanup(dev, dev->subdevices + 3);
+
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+
+ if (dev->iobase) {
+ release_region(dev->iobase, DAS16M1_SIZE);
+ release_region(dev->iobase + DAS16M1_82C55, DAS16M1_SIZE2);
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/das1800.c b/drivers/staging/comedi/drivers/das1800.c
new file mode 100644
index 000000000000..cd4cd4e6a79b
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das1800.c
@@ -0,0 +1,1758 @@
+/*
+ comedi/drivers/das1800.c
+ Driver for Keitley das1700/das1800 series boards
+ Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+************************************************************************
+*/
+/*
+Driver: das1800
+Description: Keithley Metrabyte DAS1800 (& compatibles)
+Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+Devices: [Keithley Metrabyte] DAS-1701ST (das-1701st),
+ DAS-1701ST-DA (das-1701st-da), DAS-1701/AO (das-1701ao),
+ DAS-1702ST (das-1702st), DAS-1702ST-DA (das-1702st-da),
+ DAS-1702HR (das-1702hr), DAS-1702HR-DA (das-1702hr-da),
+ DAS-1702/AO (das-1702ao), DAS-1801ST (das-1801st),
+ DAS-1801ST-DA (das-1801st-da), DAS-1801HC (das-1801hc),
+ DAS-1801AO (das-1801ao), DAS-1802ST (das-1802st),
+ DAS-1802ST-DA (das-1802st-da), DAS-1802HR (das-1802hr),
+ DAS-1802HR-DA (das-1802hr-da), DAS-1802HC (das-1802hc),
+ DAS-1802AO (das-1802ao)
+Status: works
+
+The waveform analog output on the 'ao' cards is not supported.
+If you need it, send me (Frank Hess) an email.
+
+Configuration options:
+ [0] - I/O port base address
+ [1] - IRQ (optional, required for timed or externally triggered conversions)
+ [2] - DMA0 (optional, requires irq)
+ [3] - DMA1 (optional, requires irq and dma0)
+*/
+/*
+
+This driver supports the following Keithley boards:
+
+das-1701st
+das-1701st-da
+das-1701ao
+das-1702st
+das-1702st-da
+das-1702hr
+das-1702hr-da
+das-1702ao
+das-1801st
+das-1801st-da
+das-1801hc
+das-1801ao
+das-1802st
+das-1802st-da
+das-1802hr
+das-1802hr-da
+das-1802hc
+das-1802ao
+
+Options:
+ [0] - base io address
+ [1] - irq (optional, required for timed or externally triggered conversions)
+ [2] - dma0 (optional, requires irq)
+ [3] - dma1 (optional, requires irq and dma0)
+
+irq can be omitted, although the cmd interface will not work without it.
+
+analog input cmd triggers supported:
+ start_src: TRIG_NOW | TRIG_EXT
+ scan_begin_src: TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT
+ scan_end_src: TRIG_COUNT
+ convert_src: TRIG_TIMER | TRIG_EXT (TRIG_EXT requires scan_begin_src == TRIG_FOLLOW)
+ stop_src: TRIG_COUNT | TRIG_EXT | TRIG_NONE
+
+scan_begin_src triggers TRIG_TIMER and TRIG_EXT use the card's
+'burst mode' which limits the valid conversion time to 64 microseconds
+(convert_arg <= 64000). This limitation does not apply if scan_begin_src
+is TRIG_FOLLOW.
+
+NOTES:
+Only the DAS-1801ST has been tested by me.
+Unipolar and bipolar ranges cannot be mixed in the channel/gain list.
+
+TODO:
+ Make it automatically allocate irq and dma channels if they are not specified
+ Add support for analog out on 'ao' cards
+ read insn for analog out
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+#include <asm/dma.h>
+
+#include "8253.h"
+#include "comedi_fc.h"
+
+// misc. defines
+#define DAS1800_SIZE 16 //uses 16 io addresses
+#define FIFO_SIZE 1024 // 1024 sample fifo
+#define TIMER_BASE 200 // 5 Mhz master clock
+#define UNIPOLAR 0x4 // bit that determines whether input range is uni/bipolar
+#define DMA_BUF_SIZE 0x1ff00 // size in bytes of dma buffers
+
+/* Registers for the das1800 */
+#define DAS1800_FIFO 0x0
+#define DAS1800_QRAM 0x0
+#define DAS1800_DAC 0x0
+#define DAS1800_SELECT 0x2
+#define ADC 0x0
+#define QRAM 0x1
+#define DAC(a) (0x2 + a)
+#define DAS1800_DIGITAL 0x3
+#define DAS1800_CONTROL_A 0x4
+#define FFEN 0x1
+#define CGEN 0x4
+#define CGSL 0x8
+#define TGEN 0x10
+#define TGSL 0x20
+#define ATEN 0x80
+#define DAS1800_CONTROL_B 0x5
+#define DMA_CH5 0x1
+#define DMA_CH6 0x2
+#define DMA_CH7 0x3
+#define DMA_CH5_CH6 0x5
+#define DMA_CH6_CH7 0x6
+#define DMA_CH7_CH5 0x7
+#define DMA_ENABLED 0x3 //mask used to determine if dma is enabled
+#define DMA_DUAL 0x4
+#define IRQ3 0x8
+#define IRQ5 0x10
+#define IRQ7 0x18
+#define IRQ10 0x28
+#define IRQ11 0x30
+#define IRQ15 0x38
+#define FIMD 0x40
+#define DAS1800_CONTROL_C 0X6
+#define IPCLK 0x1
+#define XPCLK 0x3
+#define BMDE 0x4
+#define CMEN 0x8
+#define UQEN 0x10
+#define SD 0x40
+#define UB 0x80
+#define DAS1800_STATUS 0x7
+// bits that prevent interrupt status bits (and CVEN) from being cleared on write
+#define CLEAR_INTR_MASK (CVEN_MASK | 0x1f)
+#define INT 0x1
+#define DMATC 0x2
+#define CT0TC 0x8
+#define OVF 0x10
+#define FHF 0x20
+#define FNE 0x40
+#define CVEN_MASK 0x40 // masks CVEN on write
+#define CVEN 0x80
+#define DAS1800_BURST_LENGTH 0x8
+#define DAS1800_BURST_RATE 0x9
+#define DAS1800_QRAM_ADDRESS 0xa
+#define DAS1800_COUNTER 0xc
+
+#define IOBASE2 0x400 //offset of additional ioports used on 'ao' cards
+
+enum {
+ das1701st, das1701st_da, das1702st, das1702st_da, das1702hr,
+ das1702hr_da,
+ das1701ao, das1702ao, das1801st, das1801st_da, das1802st, das1802st_da,
+ das1802hr, das1802hr_da, das1801hc, das1802hc, das1801ao, das1802ao
+};
+
+static int das1800_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int das1800_detach(struct comedi_device * dev);
+static int das1800_probe(struct comedi_device * dev);
+static int das1800_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static irqreturn_t das1800_interrupt(int irq, void *d PT_REGS_ARG);
+static int das1800_ai_poll(struct comedi_device * dev, struct comedi_subdevice * s);
+static void das1800_ai_handler(struct comedi_device * dev);
+static void das1800_handle_dma(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int status);
+static void das1800_flush_dma(struct comedi_device * dev, struct comedi_subdevice * s);
+static void das1800_flush_dma_channel(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int channel, uint16_t * buffer);
+static void das1800_handle_fifo_half_full(struct comedi_device * dev,
+ struct comedi_subdevice * s);
+static void das1800_handle_fifo_not_empty(struct comedi_device * dev,
+ struct comedi_subdevice * s);
+static int das1800_ai_do_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int das1800_ai_do_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int das1800_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das1800_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das1800_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das1800_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int das1800_set_frequency(struct comedi_device * dev);
+static unsigned int burst_convert_arg(unsigned int convert_arg, int round_mode);
+static unsigned int suggest_transfer_size(struct comedi_cmd * cmd);
+
+// analog input ranges
+static const struct comedi_lrange range_ai_das1801 = {
+ 8,
+ {
+ RANGE(-5, 5),
+ RANGE(-1, 1),
+ RANGE(-0.1, 0.1),
+ RANGE(-0.02, 0.02),
+ RANGE(0, 5),
+ RANGE(0, 1),
+ RANGE(0, 0.1),
+ RANGE(0, 0.02),
+ }
+};
+
+static const struct comedi_lrange range_ai_das1802 = {
+ 8,
+ {
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ RANGE(-2.5, 2.5),
+ RANGE(-1.25, 1.25),
+ RANGE(0, 10),
+ RANGE(0, 5),
+ RANGE(0, 2.5),
+ RANGE(0, 1.25),
+ }
+};
+
+struct das1800_board {
+ const char *name;
+ int ai_speed; /* max conversion period in nanoseconds */
+ int resolution; /* bits of ai resolution */
+ int qram_len; /* length of card's channel / gain queue */
+ int common; /* supports AREF_COMMON flag */
+ int do_n_chan; /* number of digital output channels */
+ int ao_ability; /* 0 == no analog out, 1 == basic analog out, 2 == waveform analog out */
+ int ao_n_chan; /* number of analog out channels */
+ const struct comedi_lrange *range_ai; /* available input ranges */
+};
+
+/* Warning: the maximum conversion speeds listed below are
+ * not always achievable depending on board setup (see
+ * user manual.)
+ */
+static const struct das1800_board das1800_boards[] = {
+ {
+ name: "das-1701st",
+ ai_speed:6250,
+ resolution:12,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:0,
+ ao_n_chan:0,
+ range_ai:&range_ai_das1801,
+ },
+ {
+ name: "das-1701st-da",
+ ai_speed:6250,
+ resolution:12,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:1,
+ ao_n_chan:4,
+ range_ai:&range_ai_das1801,
+ },
+ {
+ name: "das-1702st",
+ ai_speed:6250,
+ resolution:12,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:0,
+ ao_n_chan:0,
+ range_ai:&range_ai_das1802,
+ },
+ {
+ name: "das-1702st-da",
+ ai_speed:6250,
+ resolution:12,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:1,
+ ao_n_chan:4,
+ range_ai:&range_ai_das1802,
+ },
+ {
+ name: "das-1702hr",
+ ai_speed:20000,
+ resolution:16,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:0,
+ ao_n_chan:0,
+ range_ai:&range_ai_das1802,
+ },
+ {
+ name: "das-1702hr-da",
+ ai_speed:20000,
+ resolution:16,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:1,
+ ao_n_chan:2,
+ range_ai:&range_ai_das1802,
+ },
+ {
+ name: "das-1701ao",
+ ai_speed:6250,
+ resolution:12,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:2,
+ ao_n_chan:2,
+ range_ai:&range_ai_das1801,
+ },
+ {
+ name: "das-1702ao",
+ ai_speed:6250,
+ resolution:12,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:2,
+ ao_n_chan:2,
+ range_ai:&range_ai_das1802,
+ },
+ {
+ name: "das-1801st",
+ ai_speed:3000,
+ resolution:12,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:0,
+ ao_n_chan:0,
+ range_ai:&range_ai_das1801,
+ },
+ {
+ name: "das-1801st-da",
+ ai_speed:3000,
+ resolution:12,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:0,
+ ao_n_chan:4,
+ range_ai:&range_ai_das1801,
+ },
+ {
+ name: "das-1802st",
+ ai_speed:3000,
+ resolution:12,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:0,
+ ao_n_chan:0,
+ range_ai:&range_ai_das1802,
+ },
+ {
+ name: "das-1802st-da",
+ ai_speed:3000,
+ resolution:12,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:1,
+ ao_n_chan:4,
+ range_ai:&range_ai_das1802,
+ },
+ {
+ name: "das-1802hr",
+ ai_speed:10000,
+ resolution:16,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:0,
+ ao_n_chan:0,
+ range_ai:&range_ai_das1802,
+ },
+ {
+ name: "das-1802hr-da",
+ ai_speed:10000,
+ resolution:16,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:1,
+ ao_n_chan:2,
+ range_ai:&range_ai_das1802,
+ },
+ {
+ name: "das-1801hc",
+ ai_speed:3000,
+ resolution:12,
+ qram_len:64,
+ common: 0,
+ do_n_chan:8,
+ ao_ability:1,
+ ao_n_chan:2,
+ range_ai:&range_ai_das1801,
+ },
+ {
+ name: "das-1802hc",
+ ai_speed:3000,
+ resolution:12,
+ qram_len:64,
+ common: 0,
+ do_n_chan:8,
+ ao_ability:1,
+ ao_n_chan:2,
+ range_ai:&range_ai_das1802,
+ },
+ {
+ name: "das-1801ao",
+ ai_speed:3000,
+ resolution:12,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:2,
+ ao_n_chan:2,
+ range_ai:&range_ai_das1801,
+ },
+ {
+ name: "das-1802ao",
+ ai_speed:3000,
+ resolution:12,
+ qram_len:256,
+ common: 1,
+ do_n_chan:4,
+ ao_ability:2,
+ ao_n_chan:2,
+ range_ai:&range_ai_das1802,
+ },
+};
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct das1800_board *)dev->board_ptr)
+
+struct das1800_private {
+ volatile unsigned int count; /* number of data points left to be taken */
+ unsigned int divisor1; /* value to load into board's counter 1 for timed conversions */
+ unsigned int divisor2; /* value to load into board's counter 2 for timed conversions */
+ int do_bits; /* digital output bits */
+ int irq_dma_bits; /* bits for control register b */
+ /* dma bits for control register b, stored so that dma can be
+ * turned on and off */
+ int dma_bits;
+ unsigned int dma0; /* dma channels used */
+ unsigned int dma1;
+ volatile unsigned int dma_current; /* dma channel currently in use */
+ uint16_t *ai_buf0; /* pointers to dma buffers */
+ uint16_t *ai_buf1;
+ uint16_t *dma_current_buf; /* pointer to dma buffer currently being used */
+ unsigned int dma_transfer_size; /* size of transfer currently used, in bytes */
+ unsigned long iobase2; /* secondary io address used for analog out on 'ao' boards */
+ short ao_update_bits; /* remembers the last write to the 'update' dac */
+};
+
+#define devpriv ((struct das1800_private *)dev->private)
+
+// analog out range for boards with basic analog out
+static const struct comedi_lrange range_ao_1 = {
+ 1,
+ {
+ RANGE(-10, 10),
+ }
+};
+
+// analog out range for 'ao' boards
+/*
+static const struct comedi_lrange range_ao_2 = {
+ 2,
+ {
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ }
+};
+*/
+
+static struct comedi_driver driver_das1800 = {
+ driver_name:"das1800",
+ module:THIS_MODULE,
+ attach:das1800_attach,
+ detach:das1800_detach,
+ num_names:sizeof(das1800_boards) / sizeof(struct das1800_board),
+ board_name:&das1800_boards[0].name,
+ offset:sizeof(struct das1800_board),
+};
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_INITCLEANUP(driver_das1800);
+
+static int das1800_init_dma(struct comedi_device * dev, unsigned int dma0,
+ unsigned int dma1)
+{
+ unsigned long flags;
+
+ // need an irq to do dma
+ if (dev->irq && dma0) {
+ //encode dma0 and dma1 into 2 digit hexadecimal for switch
+ switch ((dma0 & 0x7) | (dma1 << 4)) {
+ case 0x5: // dma0 == 5
+ devpriv->dma_bits |= DMA_CH5;
+ break;
+ case 0x6: // dma0 == 6
+ devpriv->dma_bits |= DMA_CH6;
+ break;
+ case 0x7: // dma0 == 7
+ devpriv->dma_bits |= DMA_CH7;
+ break;
+ case 0x65: // dma0 == 5, dma1 == 6
+ devpriv->dma_bits |= DMA_CH5_CH6;
+ break;
+ case 0x76: // dma0 == 6, dma1 == 7
+ devpriv->dma_bits |= DMA_CH6_CH7;
+ break;
+ case 0x57: // dma0 == 7, dma1 == 5
+ devpriv->dma_bits |= DMA_CH7_CH5;
+ break;
+ default:
+ printk(" only supports dma channels 5 through 7\n"
+ " Dual dma only allows the following combinations:\n"
+ " dma 5,6 / 6,7 / or 7,5\n");
+ return -EINVAL;
+ break;
+ }
+ if (request_dma(dma0, driver_das1800.driver_name)) {
+ printk(" failed to allocate dma channel %i\n", dma0);
+ return -EINVAL;
+ }
+ devpriv->dma0 = dma0;
+ devpriv->dma_current = dma0;
+ if (dma1) {
+ if (request_dma(dma1, driver_das1800.driver_name)) {
+ printk(" failed to allocate dma channel %i\n",
+ dma1);
+ return -EINVAL;
+ }
+ devpriv->dma1 = dma1;
+ }
+ devpriv->ai_buf0 = kmalloc(DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA);
+ if (devpriv->ai_buf0 == NULL)
+ return -ENOMEM;
+ devpriv->dma_current_buf = devpriv->ai_buf0;
+ if (dma1) {
+ devpriv->ai_buf1 =
+ kmalloc(DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA);
+ if (devpriv->ai_buf1 == NULL)
+ return -ENOMEM;
+ }
+ flags = claim_dma_lock();
+ disable_dma(devpriv->dma0);
+ set_dma_mode(devpriv->dma0, DMA_MODE_READ);
+ if (dma1) {
+ disable_dma(devpriv->dma1);
+ set_dma_mode(devpriv->dma1, DMA_MODE_READ);
+ }
+ release_dma_lock(flags);
+ }
+ return 0;
+}
+
+static int das1800_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase = it->options[0];
+ unsigned int irq = it->options[1];
+ unsigned int dma0 = it->options[2];
+ unsigned int dma1 = it->options[3];
+ unsigned long iobase2;
+ int board;
+ int retval;
+
+ /* allocate and initialize dev->private */
+ if (alloc_private(dev, sizeof(struct das1800_private)) < 0)
+ return -ENOMEM;
+
+ printk("comedi%d: %s: io 0x%lx", dev->minor, driver_das1800.driver_name,
+ iobase);
+ if (irq) {
+ printk(", irq %u", irq);
+ if (dma0) {
+ printk(", dma %u", dma0);
+ if (dma1)
+ printk(" and %u", dma1);
+ }
+ }
+ printk("\n");
+
+ if (iobase == 0) {
+ printk(" io base address required\n");
+ return -EINVAL;
+ }
+
+ /* check if io addresses are available */
+ if (!request_region(iobase, DAS1800_SIZE, driver_das1800.driver_name)) {
+ printk(" I/O port conflict: failed to allocate ports 0x%lx to 0x%lx\n", iobase, iobase + DAS1800_SIZE - 1);
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ board = das1800_probe(dev);
+ if (board < 0) {
+ printk(" unable to determine board type\n");
+ return -ENODEV;
+ }
+
+ dev->board_ptr = das1800_boards + board;
+ dev->board_name = thisboard->name;
+
+ // if it is an 'ao' board with fancy analog out then we need extra io ports
+ if (thisboard->ao_ability == 2) {
+ iobase2 = iobase + IOBASE2;
+ if (!request_region(iobase2, DAS1800_SIZE,
+ driver_das1800.driver_name)) {
+ printk(" I/O port conflict: failed to allocate ports 0x%lx to 0x%lx\n", iobase2, iobase2 + DAS1800_SIZE - 1);
+ return -EIO;
+ }
+ devpriv->iobase2 = iobase2;
+ }
+
+ /* grab our IRQ */
+ if (irq) {
+ if (comedi_request_irq(irq, das1800_interrupt, 0,
+ driver_das1800.driver_name, dev)) {
+ printk(" unable to allocate irq %u\n", irq);
+ return -EINVAL;
+ }
+ }
+ dev->irq = irq;
+
+ // set bits that tell card which irq to use
+ switch (irq) {
+ case 0:
+ break;
+ case 3:
+ devpriv->irq_dma_bits |= 0x8;
+ break;
+ case 5:
+ devpriv->irq_dma_bits |= 0x10;
+ break;
+ case 7:
+ devpriv->irq_dma_bits |= 0x18;
+ break;
+ case 10:
+ devpriv->irq_dma_bits |= 0x28;
+ break;
+ case 11:
+ devpriv->irq_dma_bits |= 0x30;
+ break;
+ case 15:
+ devpriv->irq_dma_bits |= 0x38;
+ break;
+ default:
+ printk(" irq out of range\n");
+ return -EINVAL;
+ break;
+ }
+
+ retval = das1800_init_dma(dev, dma0, dma1);
+ if (retval < 0)
+ return retval;
+
+ if (devpriv->ai_buf0 == NULL) {
+ devpriv->ai_buf0 =
+ kmalloc(FIFO_SIZE * sizeof(uint16_t), GFP_KERNEL);
+ if (devpriv->ai_buf0 == NULL)
+ return -ENOMEM;
+ }
+
+ if (alloc_subdevices(dev, 4) < 0)
+ return -ENOMEM;
+
+ /* analog input subdevice */
+ s = dev->subdevices + 0;
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_GROUND | SDF_CMD_READ;
+ if (thisboard->common)
+ s->subdev_flags |= SDF_COMMON;
+ s->n_chan = thisboard->qram_len;
+ s->len_chanlist = thisboard->qram_len;
+ s->maxdata = (1 << thisboard->resolution) - 1;
+ s->range_table = thisboard->range_ai;
+ s->do_cmd = das1800_ai_do_cmd;
+ s->do_cmdtest = das1800_ai_do_cmdtest;
+ s->insn_read = das1800_ai_rinsn;
+ s->poll = das1800_ai_poll;
+ s->cancel = das1800_cancel;
+
+ /* analog out */
+ s = dev->subdevices + 1;
+ if (thisboard->ao_ability == 1) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = thisboard->ao_n_chan;
+ s->maxdata = (1 << thisboard->resolution) - 1;
+ s->range_table = &range_ao_1;
+ s->insn_write = das1800_ao_winsn;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* di */
+ s = dev->subdevices + 2;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das1800_di_rbits;
+
+ /* do */
+ s = dev->subdevices + 3;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = thisboard->do_n_chan;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das1800_do_wbits;
+
+ das1800_cancel(dev, dev->read_subdev);
+
+ // initialize digital out channels
+ outb(devpriv->do_bits, dev->iobase + DAS1800_DIGITAL);
+
+ // initialize analog out channels
+ if (thisboard->ao_ability == 1) {
+ // select 'update' dac channel for baseAddress + 0x0
+ outb(DAC(thisboard->ao_n_chan - 1),
+ dev->iobase + DAS1800_SELECT);
+ outw(devpriv->ao_update_bits, dev->iobase + DAS1800_DAC);
+ }
+
+ return 0;
+};
+
+static int das1800_detach(struct comedi_device * dev)
+{
+ /* only free stuff if it has been allocated by _attach */
+ if (dev->iobase)
+ release_region(dev->iobase, DAS1800_SIZE);
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+ if (dev->private) {
+ if (devpriv->iobase2)
+ release_region(devpriv->iobase2, DAS1800_SIZE);
+ if (devpriv->dma0)
+ free_dma(devpriv->dma0);
+ if (devpriv->dma1)
+ free_dma(devpriv->dma1);
+ if (devpriv->ai_buf0)
+ kfree(devpriv->ai_buf0);
+ if (devpriv->ai_buf1)
+ kfree(devpriv->ai_buf1);
+ }
+
+ printk("comedi%d: %s: remove\n", dev->minor,
+ driver_das1800.driver_name);
+
+ return 0;
+};
+
+/* probes and checks das-1800 series board type
+ */
+static int das1800_probe(struct comedi_device * dev)
+{
+ int id;
+ int board;
+
+ id = (inb(dev->iobase + DAS1800_DIGITAL) >> 4) & 0xf; /* get id bits */
+ board = ((struct das1800_board *) dev->board_ptr) - das1800_boards;
+
+ switch (id) {
+ case 0x3:
+ if (board == das1801st_da || board == das1802st_da ||
+ board == das1701st_da || board == das1702st_da) {
+ printk(" Board model: %s\n",
+ das1800_boards[board].name);
+ return board;
+ }
+ printk(" Board model (probed, not recommended): das-1800st-da series\n");
+ return das1801st;
+ break;
+ case 0x4:
+ if (board == das1802hr_da || board == das1702hr_da) {
+ printk(" Board model: %s\n",
+ das1800_boards[board].name);
+ return board;
+ }
+ printk(" Board model (probed, not recommended): das-1802hr-da\n");
+ return das1802hr;
+ break;
+ case 0x5:
+ if (board == das1801ao || board == das1802ao ||
+ board == das1701ao || board == das1702ao) {
+ printk(" Board model: %s\n",
+ das1800_boards[board].name);
+ return board;
+ }
+ printk(" Board model (probed, not recommended): das-1800ao series\n");
+ return das1801ao;
+ break;
+ case 0x6:
+ if (board == das1802hr || board == das1702hr) {
+ printk(" Board model: %s\n",
+ das1800_boards[board].name);
+ return board;
+ }
+ printk(" Board model (probed, not recommended): das-1802hr\n");
+ return das1802hr;
+ break;
+ case 0x7:
+ if (board == das1801st || board == das1802st ||
+ board == das1701st || board == das1702st) {
+ printk(" Board model: %s\n",
+ das1800_boards[board].name);
+ return board;
+ }
+ printk(" Board model (probed, not recommended): das-1800st series\n");
+ return das1801st;
+ break;
+ case 0x8:
+ if (board == das1801hc || board == das1802hc) {
+ printk(" Board model: %s\n",
+ das1800_boards[board].name);
+ return board;
+ }
+ printk(" Board model (probed, not recommended): das-1800hc series\n");
+ return das1801hc;
+ break;
+ default:
+ printk(" Board model: probe returned 0x%x (unknown, please report)\n", id);
+ return board;
+ break;
+ }
+ return -1;
+}
+
+static int das1800_ai_poll(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long flags;
+
+ // prevent race with interrupt handler
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ das1800_ai_handler(dev);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return s->async->buf_write_count - s->async->buf_read_count;
+}
+
+static irqreturn_t das1800_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ unsigned int status;
+
+ if (dev->attached == 0) {
+ comedi_error(dev, "premature interrupt");
+ return IRQ_HANDLED;
+ }
+
+ /* Prevent race with das1800_ai_poll() on multi processor systems.
+ * Also protects indirect addressing in das1800_ai_handler */
+ spin_lock(&dev->spinlock);
+ status = inb(dev->iobase + DAS1800_STATUS);
+
+ /* if interrupt was not caused by das-1800 */
+ if (!(status & INT)) {
+ spin_unlock(&dev->spinlock);
+ return IRQ_NONE;
+ }
+ /* clear the interrupt status bit INT */
+ outb(CLEAR_INTR_MASK & ~INT, dev->iobase + DAS1800_STATUS);
+ // handle interrupt
+ das1800_ai_handler(dev);
+
+ spin_unlock(&dev->spinlock);
+ return IRQ_HANDLED;
+}
+
+// the guts of the interrupt handler, that is shared with das1800_ai_poll
+static void das1800_ai_handler(struct comedi_device * dev)
+{
+ struct comedi_subdevice *s = dev->subdevices + 0; /* analog input subdevice */
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int status = inb(dev->iobase + DAS1800_STATUS);
+
+ async->events = 0;
+ // select adc for base address + 0
+ outb(ADC, dev->iobase + DAS1800_SELECT);
+ // dma buffer full
+ if (devpriv->irq_dma_bits & DMA_ENABLED) {
+ // look for data from dma transfer even if dma terminal count hasn't happened yet
+ das1800_handle_dma(dev, s, status);
+ } else if (status & FHF) { // if fifo half full
+ das1800_handle_fifo_half_full(dev, s);
+ } else if (status & FNE) { // if fifo not empty
+ das1800_handle_fifo_not_empty(dev, s);
+ }
+
+ async->events |= COMEDI_CB_BLOCK;
+ /* if the card's fifo has overflowed */
+ if (status & OVF) {
+ // clear OVF interrupt bit
+ outb(CLEAR_INTR_MASK & ~OVF, dev->iobase + DAS1800_STATUS);
+ comedi_error(dev, "DAS1800 FIFO overflow");
+ das1800_cancel(dev, s);
+ async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ return;
+ }
+ // stop taking data if appropriate
+ /* stop_src TRIG_EXT */
+ if (status & CT0TC) {
+ // clear CT0TC interrupt bit
+ outb(CLEAR_INTR_MASK & ~CT0TC, dev->iobase + DAS1800_STATUS);
+ // make sure we get all remaining data from board before quitting
+ if (devpriv->irq_dma_bits & DMA_ENABLED)
+ das1800_flush_dma(dev, s);
+ else
+ das1800_handle_fifo_not_empty(dev, s);
+ das1800_cancel(dev, s); /* disable hardware conversions */
+ async->events |= COMEDI_CB_EOA;
+ } else if (cmd->stop_src == TRIG_COUNT && devpriv->count == 0) { // stop_src TRIG_COUNT
+ das1800_cancel(dev, s); /* disable hardware conversions */
+ async->events |= COMEDI_CB_EOA;
+ }
+
+ comedi_event(dev, s);
+
+ return;
+}
+
+static void das1800_handle_dma(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int status)
+{
+ unsigned long flags;
+ const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL;
+
+ flags = claim_dma_lock();
+ das1800_flush_dma_channel(dev, s, devpriv->dma_current,
+ devpriv->dma_current_buf);
+ // re-enable dma channel
+ set_dma_addr(devpriv->dma_current,
+ virt_to_bus(devpriv->dma_current_buf));
+ set_dma_count(devpriv->dma_current, devpriv->dma_transfer_size);
+ enable_dma(devpriv->dma_current);
+ release_dma_lock(flags);
+
+ if (status & DMATC) {
+ // clear DMATC interrupt bit
+ outb(CLEAR_INTR_MASK & ~DMATC, dev->iobase + DAS1800_STATUS);
+ // switch dma channels for next time, if appropriate
+ if (dual_dma) {
+ // read data from the other channel next time
+ if (devpriv->dma_current == devpriv->dma0) {
+ devpriv->dma_current = devpriv->dma1;
+ devpriv->dma_current_buf = devpriv->ai_buf1;
+ } else {
+ devpriv->dma_current = devpriv->dma0;
+ devpriv->dma_current_buf = devpriv->ai_buf0;
+ }
+ }
+ }
+
+ return;
+}
+
+static inline uint16_t munge_bipolar_sample(const struct comedi_device * dev,
+ uint16_t sample)
+{
+ sample += 1 << (thisboard->resolution - 1);
+ return sample;
+}
+
+static void munge_data(struct comedi_device * dev, uint16_t * array,
+ unsigned int num_elements)
+{
+ unsigned int i;
+ int unipolar;
+
+ /* see if card is using a unipolar or bipolar range so we can munge data correctly */
+ unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB;
+
+ /* convert to unsigned type if we are in a bipolar mode */
+ if (!unipolar) {
+ for (i = 0; i < num_elements; i++) {
+ array[i] = munge_bipolar_sample(dev, array[i]);
+ }
+ }
+}
+
+/* Utility function used by das1800_flush_dma() and das1800_handle_dma().
+ * Assumes dma lock is held */
+static void das1800_flush_dma_channel(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int channel, uint16_t * buffer)
+{
+ unsigned int num_bytes, num_samples;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ disable_dma(channel);
+
+ /* clear flip-flop to make sure 2-byte registers
+ * get set correctly */
+ clear_dma_ff(channel);
+
+ // figure out how many points to read
+ num_bytes = devpriv->dma_transfer_size - get_dma_residue(channel);
+ num_samples = num_bytes / sizeof(short);
+
+ /* if we only need some of the points */
+ if (cmd->stop_src == TRIG_COUNT && devpriv->count < num_samples)
+ num_samples = devpriv->count;
+
+ munge_data(dev, buffer, num_samples);
+ cfc_write_array_to_buffer(s, buffer, num_bytes);
+ if (s->async->cmd.stop_src == TRIG_COUNT)
+ devpriv->count -= num_samples;
+
+ return;
+}
+
+/* flushes remaining data from board when external trigger has stopped aquisition
+ * and we are using dma transfers */
+static void das1800_flush_dma(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long flags;
+ const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL;
+
+ flags = claim_dma_lock();
+ das1800_flush_dma_channel(dev, s, devpriv->dma_current,
+ devpriv->dma_current_buf);
+
+ if (dual_dma) {
+ // switch to other channel and flush it
+ if (devpriv->dma_current == devpriv->dma0) {
+ devpriv->dma_current = devpriv->dma1;
+ devpriv->dma_current_buf = devpriv->ai_buf1;
+ } else {
+ devpriv->dma_current = devpriv->dma0;
+ devpriv->dma_current_buf = devpriv->ai_buf0;
+ }
+ das1800_flush_dma_channel(dev, s, devpriv->dma_current,
+ devpriv->dma_current_buf);
+ }
+
+ release_dma_lock(flags);
+
+ // get any remaining samples in fifo
+ das1800_handle_fifo_not_empty(dev, s);
+
+ return;
+}
+
+static void das1800_handle_fifo_half_full(struct comedi_device * dev,
+ struct comedi_subdevice * s)
+{
+ int numPoints = 0; /* number of points to read */
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ numPoints = FIFO_SIZE / 2;
+ /* if we only need some of the points */
+ if (cmd->stop_src == TRIG_COUNT && devpriv->count < numPoints)
+ numPoints = devpriv->count;
+ insw(dev->iobase + DAS1800_FIFO, devpriv->ai_buf0, numPoints);
+ munge_data(dev, devpriv->ai_buf0, numPoints);
+ cfc_write_array_to_buffer(s, devpriv->ai_buf0,
+ numPoints * sizeof(devpriv->ai_buf0[0]));
+ if (cmd->stop_src == TRIG_COUNT)
+ devpriv->count -= numPoints;
+ return;
+}
+
+static void das1800_handle_fifo_not_empty(struct comedi_device * dev,
+ struct comedi_subdevice * s)
+{
+ short dpnt;
+ int unipolar;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB;
+
+ while (inb(dev->iobase + DAS1800_STATUS) & FNE) {
+ if (cmd->stop_src == TRIG_COUNT && devpriv->count == 0)
+ break;
+ dpnt = inw(dev->iobase + DAS1800_FIFO);
+ /* convert to unsigned type if we are in a bipolar mode */
+ if (!unipolar) ;
+ dpnt = munge_bipolar_sample(dev, dpnt);
+ cfc_write_to_buffer(s, dpnt);
+ if (cmd->stop_src == TRIG_COUNT)
+ devpriv->count--;
+ }
+
+ return;
+}
+
+static int das1800_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ outb(0x0, dev->iobase + DAS1800_STATUS); /* disable conversions */
+ outb(0x0, dev->iobase + DAS1800_CONTROL_B); /* disable interrupts and dma */
+ outb(0x0, dev->iobase + DAS1800_CONTROL_A); /* disable and clear fifo and stop triggering */
+ if (devpriv->dma0)
+ disable_dma(devpriv->dma0);
+ if (devpriv->dma1)
+ disable_dma(devpriv->dma1);
+ return 0;
+}
+
+/* test analog input cmd */
+static int das1800_ai_do_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+ unsigned int tmp_arg;
+ int i;
+ int unipolar;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_EXT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_EXT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ // uniqueness check
+ if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
+ err++;
+ if (cmd->scan_begin_src != TRIG_FOLLOW &&
+ cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_EXT)
+ err++;
+ if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT &&
+ cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_EXT)
+ err++;
+ //compatibility check
+ if (cmd->scan_begin_src != TRIG_FOLLOW &&
+ cmd->convert_src != TRIG_TIMER)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < thisboard->ai_speed) {
+ cmd->convert_arg = thisboard->ai_speed;
+ err++;
+ }
+ }
+ if (!cmd->chanlist_len) {
+ cmd->chanlist_len = 1;
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ if (!cmd->stop_arg) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ break;
+ case TRIG_NONE:
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ // if we are not in burst mode
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ tmp_arg = cmd->convert_arg;
+ /* calculate counter values that give desired timing */
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE,
+ &(devpriv->divisor1), &(devpriv->divisor2),
+ &(cmd->convert_arg),
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp_arg != cmd->convert_arg)
+ err++;
+ }
+ // if we are in burst mode
+ else {
+ // check that convert_arg is compatible
+ tmp_arg = cmd->convert_arg;
+ cmd->convert_arg =
+ burst_convert_arg(cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp_arg != cmd->convert_arg)
+ err++;
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ // if scans are timed faster than conversion rate allows
+ if (cmd->convert_arg * cmd->chanlist_len >
+ cmd->scan_begin_arg) {
+ cmd->scan_begin_arg =
+ cmd->convert_arg *
+ cmd->chanlist_len;
+ err++;
+ }
+ tmp_arg = cmd->scan_begin_arg;
+ /* calculate counter values that give desired timing */
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE,
+ &(devpriv->divisor1),
+ &(devpriv->divisor2),
+ &(cmd->scan_begin_arg),
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp_arg != cmd->scan_begin_arg)
+ err++;
+ }
+ }
+ }
+
+ if (err)
+ return 4;
+
+ // make sure user is not trying to mix unipolar and bipolar ranges
+ if (cmd->chanlist) {
+ unipolar = CR_RANGE(cmd->chanlist[0]) & UNIPOLAR;
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ if (unipolar != (CR_RANGE(cmd->chanlist[i]) & UNIPOLAR)) {
+ comedi_error(dev,
+ "unipolar and bipolar ranges cannot be mixed in the chanlist");
+ err++;
+ break;
+ }
+ }
+ }
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+/* analog input cmd interface */
+
+// first, some utility functions used in the main ai_do_cmd()
+
+// returns appropriate bits for control register a, depending on command
+static int control_a_bits(struct comedi_cmd cmd)
+{
+ int control_a;
+
+ control_a = FFEN; //enable fifo
+ if (cmd.stop_src == TRIG_EXT) {
+ control_a |= ATEN;
+ }
+ switch (cmd.start_src) {
+ case TRIG_EXT:
+ control_a |= TGEN | CGSL;
+ break;
+ case TRIG_NOW:
+ control_a |= CGEN;
+ break;
+ default:
+ break;
+ }
+
+ return control_a;
+}
+
+// returns appropriate bits for control register c, depending on command
+static int control_c_bits(struct comedi_cmd cmd)
+{
+ int control_c;
+ int aref;
+
+ /* set clock source to internal or external, select analog reference,
+ * select unipolar / bipolar
+ */
+ aref = CR_AREF(cmd.chanlist[0]);
+ control_c = UQEN; //enable upper qram addresses
+ if (aref != AREF_DIFF)
+ control_c |= SD;
+ if (aref == AREF_COMMON)
+ control_c |= CMEN;
+ /* if a unipolar range was selected */
+ if (CR_RANGE(cmd.chanlist[0]) & UNIPOLAR)
+ control_c |= UB;
+ switch (cmd.scan_begin_src) {
+ case TRIG_FOLLOW: // not in burst mode
+ switch (cmd.convert_src) {
+ case TRIG_TIMER:
+ /* trig on cascaded counters */
+ control_c |= IPCLK;
+ break;
+ case TRIG_EXT:
+ /* trig on falling edge of external trigger */
+ control_c |= XPCLK;
+ break;
+ default:
+ break;
+ }
+ break;
+ case TRIG_TIMER:
+ // burst mode with internal pacer clock
+ control_c |= BMDE | IPCLK;
+ break;
+ case TRIG_EXT:
+ // burst mode with external trigger
+ control_c |= BMDE | XPCLK;
+ break;
+ default:
+ break;
+ }
+
+ return control_c;
+}
+
+// sets up counters
+static int setup_counters(struct comedi_device * dev, struct comedi_cmd cmd)
+{
+ // setup cascaded counters for conversion/scan frequency
+ switch (cmd.scan_begin_src) {
+ case TRIG_FOLLOW: // not in burst mode
+ if (cmd.convert_src == TRIG_TIMER) {
+ /* set conversion frequency */
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE,
+ &(devpriv->divisor1), &(devpriv->divisor2),
+ &(cmd.convert_arg),
+ cmd.flags & TRIG_ROUND_MASK);
+ if (das1800_set_frequency(dev) < 0) {
+ return -1;
+ }
+ }
+ break;
+ case TRIG_TIMER: // in burst mode
+ /* set scan frequency */
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
+ &(devpriv->divisor2), &(cmd.scan_begin_arg),
+ cmd.flags & TRIG_ROUND_MASK);
+ if (das1800_set_frequency(dev) < 0) {
+ return -1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // setup counter 0 for 'about triggering'
+ if (cmd.stop_src == TRIG_EXT) {
+ // load counter 0 in mode 0
+ i8254_load(dev->iobase + DAS1800_COUNTER, 0, 0, 1, 0);
+ }
+
+ return 0;
+}
+
+// sets up dma
+static void setup_dma(struct comedi_device * dev, struct comedi_cmd cmd)
+{
+ unsigned long lock_flags;
+ const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL;
+
+ if ((devpriv->irq_dma_bits & DMA_ENABLED) == 0)
+ return;
+
+ /* determine a reasonable dma transfer size */
+ devpriv->dma_transfer_size = suggest_transfer_size(&cmd);
+ lock_flags = claim_dma_lock();
+ disable_dma(devpriv->dma0);
+ /* clear flip-flop to make sure 2-byte registers for
+ * count and address get set correctly */
+ clear_dma_ff(devpriv->dma0);
+ set_dma_addr(devpriv->dma0, virt_to_bus(devpriv->ai_buf0));
+ // set appropriate size of transfer
+ set_dma_count(devpriv->dma0, devpriv->dma_transfer_size);
+ devpriv->dma_current = devpriv->dma0;
+ devpriv->dma_current_buf = devpriv->ai_buf0;
+ enable_dma(devpriv->dma0);
+ // set up dual dma if appropriate
+ if (dual_dma) {
+ disable_dma(devpriv->dma1);
+ /* clear flip-flop to make sure 2-byte registers for
+ * count and address get set correctly */
+ clear_dma_ff(devpriv->dma1);
+ set_dma_addr(devpriv->dma1, virt_to_bus(devpriv->ai_buf1));
+ // set appropriate size of transfer
+ set_dma_count(devpriv->dma1, devpriv->dma_transfer_size);
+ enable_dma(devpriv->dma1);
+ }
+ release_dma_lock(lock_flags);
+
+ return;
+}
+
+// programs channel/gain list into card
+static void program_chanlist(struct comedi_device * dev, struct comedi_cmd cmd)
+{
+ int i, n, chan_range;
+ unsigned long irq_flags;
+ const int range_mask = 0x3; //masks unipolar/bipolar bit off range
+ const int range_bitshift = 8;
+
+ n = cmd.chanlist_len;
+ // spinlock protects indirect addressing
+ comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
+ outb(QRAM, dev->iobase + DAS1800_SELECT); /* select QRAM for baseAddress + 0x0 */
+ outb(n - 1, dev->iobase + DAS1800_QRAM_ADDRESS); /*set QRAM address start */
+ /* make channel / gain list */
+ for (i = 0; i < n; i++) {
+ chan_range =
+ CR_CHAN(cmd.chanlist[i]) | ((CR_RANGE(cmd.
+ chanlist[i]) & range_mask) <<
+ range_bitshift);
+ outw(chan_range, dev->iobase + DAS1800_QRAM);
+ }
+ outb(n - 1, dev->iobase + DAS1800_QRAM_ADDRESS); /*finish write to QRAM */
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ return;
+}
+
+// analog input do_cmd
+static int das1800_ai_do_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int ret;
+ int control_a, control_c;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd cmd = async->cmd;
+
+ if (!dev->irq) {
+ comedi_error(dev,
+ "no irq assigned for das-1800, cannot do hardware conversions");
+ return -1;
+ }
+
+ /* disable dma on TRIG_WAKE_EOS, or TRIG_RT
+ * (because dma in handler is unsafe at hard real-time priority) */
+ if (cmd.flags & (TRIG_WAKE_EOS | TRIG_RT)) {
+ devpriv->irq_dma_bits &= ~DMA_ENABLED;
+ } else {
+ devpriv->irq_dma_bits |= devpriv->dma_bits;
+ }
+ // interrupt on end of conversion for TRIG_WAKE_EOS
+ if (cmd.flags & TRIG_WAKE_EOS) {
+ // interrupt fifo not empty
+ devpriv->irq_dma_bits &= ~FIMD;
+ } else {
+ // interrupt fifo half full
+ devpriv->irq_dma_bits |= FIMD;
+ }
+ // determine how many conversions we need
+ if (cmd.stop_src == TRIG_COUNT) {
+ devpriv->count = cmd.stop_arg * cmd.chanlist_len;
+ }
+
+ das1800_cancel(dev, s);
+
+ // determine proper bits for control registers
+ control_a = control_a_bits(cmd);
+ control_c = control_c_bits(cmd);
+
+ /* setup card and start */
+ program_chanlist(dev, cmd);
+ ret = setup_counters(dev, cmd);
+ if (ret < 0) {
+ comedi_error(dev, "Error setting up counters");
+ return ret;
+ }
+ setup_dma(dev, cmd);
+ outb(control_c, dev->iobase + DAS1800_CONTROL_C);
+ // set conversion rate and length for burst mode
+ if (control_c & BMDE) {
+ // program conversion period with number of microseconds minus 1
+ outb(cmd.convert_arg / 1000 - 1,
+ dev->iobase + DAS1800_BURST_RATE);
+ outb(cmd.chanlist_len - 1, dev->iobase + DAS1800_BURST_LENGTH);
+ }
+ outb(devpriv->irq_dma_bits, dev->iobase + DAS1800_CONTROL_B); // enable irq/dma
+ outb(control_a, dev->iobase + DAS1800_CONTROL_A); /* enable fifo and triggering */
+ outb(CVEN, dev->iobase + DAS1800_STATUS); /* enable conversions */
+
+ return 0;
+}
+
+/* read analog input */
+static int das1800_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i, n;
+ int chan, range, aref, chan_range;
+ int timeout = 1000;
+ short dpnt;
+ int conv_flags = 0;
+ unsigned long irq_flags;
+
+ /* set up analog reference and unipolar / bipolar mode */
+ aref = CR_AREF(insn->chanspec);
+ conv_flags |= UQEN;
+ if (aref != AREF_DIFF)
+ conv_flags |= SD;
+ if (aref == AREF_COMMON)
+ conv_flags |= CMEN;
+ /* if a unipolar range was selected */
+ if (CR_RANGE(insn->chanspec) & UNIPOLAR)
+ conv_flags |= UB;
+
+ outb(conv_flags, dev->iobase + DAS1800_CONTROL_C); /* software conversion enabled */
+ outb(CVEN, dev->iobase + DAS1800_STATUS); /* enable conversions */
+ outb(0x0, dev->iobase + DAS1800_CONTROL_A); /* reset fifo */
+ outb(FFEN, dev->iobase + DAS1800_CONTROL_A);
+
+ chan = CR_CHAN(insn->chanspec);
+ /* mask of unipolar/bipolar bit from range */
+ range = CR_RANGE(insn->chanspec) & 0x3;
+ chan_range = chan | (range << 8);
+ comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
+ outb(QRAM, dev->iobase + DAS1800_SELECT); /* select QRAM for baseAddress + 0x0 */
+ outb(0x0, dev->iobase + DAS1800_QRAM_ADDRESS); /* set QRAM address start */
+ outw(chan_range, dev->iobase + DAS1800_QRAM);
+ outb(0x0, dev->iobase + DAS1800_QRAM_ADDRESS); /*finish write to QRAM */
+ outb(ADC, dev->iobase + DAS1800_SELECT); /* select ADC for baseAddress + 0x0 */
+
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion */
+ outb(0, dev->iobase + DAS1800_FIFO);
+ for (i = 0; i < timeout; i++) {
+ if (inb(dev->iobase + DAS1800_STATUS) & FNE)
+ break;
+ }
+ if (i == timeout) {
+ comedi_error(dev, "timeout");
+ return -ETIME;
+ }
+ dpnt = inw(dev->iobase + DAS1800_FIFO);
+ /* shift data to offset binary for bipolar ranges */
+ if ((conv_flags & UB) == 0)
+ dpnt += 1 << (thisboard->resolution - 1);
+ data[n] = dpnt;
+ }
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ return n;
+}
+
+/* writes to an analog output channel */
+static int das1800_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+// int range = CR_RANGE(insn->chanspec);
+ int update_chan = thisboard->ao_n_chan - 1;
+ short output;
+ unsigned long irq_flags;
+
+ // card expects two's complement data
+ output = data[0] - (1 << (thisboard->resolution - 1));
+ // if the write is to the 'update' channel, we need to remember its value
+ if (chan == update_chan)
+ devpriv->ao_update_bits = output;
+ // write to channel
+ comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
+ outb(DAC(chan), dev->iobase + DAS1800_SELECT); /* select dac channel for baseAddress + 0x0 */
+ outw(output, dev->iobase + DAS1800_DAC);
+ // now we need to write to 'update' channel to update all dac channels
+ if (chan != update_chan) {
+ outb(DAC(update_chan), dev->iobase + DAS1800_SELECT); /* select 'update' channel for baseAddress + 0x0 */
+ outw(devpriv->ao_update_bits, dev->iobase + DAS1800_DAC);
+ }
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ return 1;
+}
+
+/* reads from digital input channels */
+static int das1800_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ data[1] = inb(dev->iobase + DAS1800_DIGITAL) & 0xf;
+ data[0] = 0;
+
+ return 2;
+}
+
+/* writes to digital output channels */
+static int das1800_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int wbits;
+
+ // only set bits that have been masked
+ data[0] &= (1 << s->n_chan) - 1;
+ wbits = devpriv->do_bits;
+ wbits &= ~data[0];
+ wbits |= data[0] & data[1];
+ devpriv->do_bits = wbits;
+
+ outb(devpriv->do_bits, dev->iobase + DAS1800_DIGITAL);
+
+ data[1] = devpriv->do_bits;
+
+ return 2;
+}
+
+/* loads counters with divisor1, divisor2 from private structure */
+static int das1800_set_frequency(struct comedi_device * dev)
+{
+ int err = 0;
+
+ // counter 1, mode 2
+ if (i8254_load(dev->iobase + DAS1800_COUNTER, 0, 1, devpriv->divisor1,
+ 2))
+ err++;
+ // counter 2, mode 2
+ if (i8254_load(dev->iobase + DAS1800_COUNTER, 0, 2, devpriv->divisor2,
+ 2))
+ err++;
+ if (err)
+ return -1;
+
+ return 0;
+}
+
+/* converts requested conversion timing to timing compatible with
+ * hardware, used only when card is in 'burst mode'
+ */
+static unsigned int burst_convert_arg(unsigned int convert_arg, int round_mode)
+{
+ unsigned int micro_sec;
+
+ // in burst mode, the maximum conversion time is 64 microseconds
+ if (convert_arg > 64000)
+ convert_arg = 64000;
+
+ // the conversion time must be an integral number of microseconds
+ switch (round_mode) {
+ case TRIG_ROUND_NEAREST:
+ default:
+ micro_sec = (convert_arg + 500) / 1000;
+ break;
+ case TRIG_ROUND_DOWN:
+ micro_sec = convert_arg / 1000;
+ break;
+ case TRIG_ROUND_UP:
+ micro_sec = (convert_arg - 1) / 1000 + 1;
+ break;
+ }
+
+ // return number of nanoseconds
+ return micro_sec * 1000;
+}
+
+// utility function that suggests a dma transfer size based on the conversion period 'ns'
+static unsigned int suggest_transfer_size(struct comedi_cmd * cmd)
+{
+ unsigned int size = DMA_BUF_SIZE;
+ static const int sample_size = 2; // size in bytes of one sample from board
+ unsigned int fill_time = 300000000; // target time in nanoseconds for filling dma buffer
+ unsigned int max_size; // maximum size we will allow for a transfer
+
+ // make dma buffer fill in 0.3 seconds for timed modes
+ switch (cmd->scan_begin_src) {
+ case TRIG_FOLLOW: // not in burst mode
+ if (cmd->convert_src == TRIG_TIMER)
+ size = (fill_time / cmd->convert_arg) * sample_size;
+ break;
+ case TRIG_TIMER:
+ size = (fill_time / (cmd->scan_begin_arg * cmd->chanlist_len)) *
+ sample_size;
+ break;
+ default:
+ size = DMA_BUF_SIZE;
+ break;
+ }
+
+ // set a minimum and maximum size allowed
+ max_size = DMA_BUF_SIZE;
+ // if we are taking limited number of conversions, limit transfer size to that
+ if (cmd->stop_src == TRIG_COUNT &&
+ cmd->stop_arg * cmd->chanlist_len * sample_size < max_size)
+ max_size = cmd->stop_arg * cmd->chanlist_len * sample_size;
+
+ if (size > max_size)
+ size = max_size;
+ if (size < sample_size)
+ size = sample_size;
+
+ return size;
+}
diff --git a/drivers/staging/comedi/drivers/das6402.c b/drivers/staging/comedi/drivers/das6402.c
new file mode 100644
index 000000000000..2a8ca0525104
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das6402.c
@@ -0,0 +1,354 @@
+/*
+ Some comments on the code..
+
+ - it shouldn't be necessary to use outb_p().
+
+ - ignoreirq creates a race condition. It needs to be fixed.
+
+ */
+
+/*
+ comedi/drivers/das6402.c
+ An experimental driver for Computerboards' DAS6402 I/O card
+
+ Copyright (C) 1999 Oystein Svendsen <svendsen@pvv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+/*
+Driver: das6402
+Description: Keithley Metrabyte DAS6402 (& compatibles)
+Author: Oystein Svendsen <svendsen@pvv.org>
+Status: bitrotten
+Devices: [Keithley Metrabyte] DAS6402 (das6402)
+
+This driver has suffered bitrot.
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+#define DAS6402_SIZE 16
+
+#define N_WORDS 3000*64
+
+#define STOP 0
+#define START 1
+
+#define SCANL 0x3f00
+#define BYTE unsigned char
+#define WORD unsigned short
+
+/*----- register 8 ----*/
+#define CLRINT 0x01
+#define CLRXTR 0x02
+#define CLRXIN 0x04
+#define EXTEND 0x10
+#define ARMED 0x20 /* enable conting of post sample conv */
+#define POSTMODE 0x40
+#define MHZ 0x80 /* 10 MHz clock */
+/*---------------------*/
+
+/*----- register 9 ----*/
+#define IRQ (0x04 << 4) /* these two are */
+#define IRQV 10 /* dependent on each other */
+
+#define CONVSRC 0x03 /* trig src is Intarnal pacer */
+#define BURSTEN 0x04 /* enable burst */
+#define XINTE 0x08 /* use external int. trig */
+#define INTE 0x80 /* enable analog interrupts */
+/*---------------------*/
+
+/*----- register 10 ---*/
+#define TGEN 0x01 /* Use pin DI1 for externl trigging? */
+#define TGSEL 0x02 /* Use edge triggering */
+#define TGPOL 0x04 /* active edge is falling */
+#define PRETRIG 0x08 /* pretrig */
+/*---------------------*/
+
+/*----- register 11 ---*/
+#define EOB 0x0c
+#define FIFOHFULL 0x08
+#define GAIN 0x01
+#define FIFONEPTY 0x04
+#define MODE 0x10
+#define SEM 0x20
+#define BIP 0x40
+/*---------------------*/
+
+#define M0 0x00
+#define M2 0x04
+
+#define C0 0x00
+#define C1 0x40
+#define C2 0x80
+#define RWLH 0x30
+
+static int das6402_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int das6402_detach(struct comedi_device * dev);
+static struct comedi_driver driver_das6402 = {
+ driver_name:"das6402",
+ module:THIS_MODULE,
+ attach:das6402_attach,
+ detach:das6402_detach,
+};
+
+COMEDI_INITCLEANUP(driver_das6402);
+
+struct das6402_private {
+ int ai_bytes_to_read;
+
+ int das6402_ignoreirq;
+};
+#define devpriv ((struct das6402_private *)dev->private)
+
+static void das6402_ai_fifo_dregs(struct comedi_device * dev, struct comedi_subdevice * s);
+
+static void das6402_setcounter(struct 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 irqreturn_t intr_handler(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices;
+
+ if (!dev->attached || devpriv->das6402_ignoreirq) {
+ printk("das6402: BUG: spurious interrupt\n");
+ return IRQ_HANDLED;
+ }
+#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->async->buf_write_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
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ }
+
+ outb(0x01, dev->iobase + 8); /* clear only the interrupt flip-flop */
+
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+}
+
+#if 0
+static void das6402_ai_fifo_read(struct comedi_device * dev, short * data, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ data[i] = inw(dev->iobase);
+}
+#endif
+
+static void das6402_ai_fifo_dregs(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ while (1) {
+ if (!(inb(dev->iobase + 8) & 0x01))
+ return;
+ comedi_buf_put(s->async, inw(dev->iobase));
+ }
+}
+
+static int das6402_ai_cancel(struct comedi_device * dev, struct 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;
+}
+
+#ifdef unused
+static int das6402_ai_mode2(struct comedi_device * dev, struct 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(short);
+
+ /* um... ignoreirq is a nasty race condition */
+ devpriv->das6402_ignoreirq = 0;
+
+ outw_p(SCANL, dev->iobase + 2);
+
+ return 0;
+}
+#endif
+
+static int board_init(struct 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(struct comedi_device * dev)
+{
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+ if (dev->iobase)
+ release_region(dev->iobase, DAS6402_SIZE);
+
+ return 0;
+}
+
+static int das6402_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ unsigned int irq;
+ unsigned long iobase;
+ int ret;
+ struct comedi_subdevice *s;
+
+ dev->board_name = "das6402";
+
+ iobase = it->options[0];
+ if (iobase == 0)
+ iobase = 0x300;
+
+ printk("comedi%d: das6402: 0x%04lx", dev->minor, iobase);
+
+ if (!request_region(iobase, DAS6402_SIZE, "das6402")) {
+ printk(" I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ /* should do a probe here */
+
+ irq = it->options[0];
+ printk(" ( irq = %u )", irq);
+ ret = comedi_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(struct das6402_private))) < 0)
+ return ret;
+
+ if ((ret = alloc_subdevices(dev, 1)) < 0)
+ return ret;
+
+ /* ai subdevice */
+ s = dev->subdevices + 0;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ 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_table = &range_unknown;
+
+ board_init(dev);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/das800.c b/drivers/staging/comedi/drivers/das800.c
new file mode 100644
index 000000000000..7a6656bf809c
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das800.c
@@ -0,0 +1,894 @@
+/*
+ comedi/drivers/das800.c
+ Driver for Keitley das800 series boards and compatibles
+ Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+************************************************************************
+*/
+/*
+Driver: das800
+Description: Keithley Metrabyte DAS800 (& compatibles)
+Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
+ DAS-802 (das-802),
+ [Measurement Computing] CIO-DAS800 (cio-das800),
+ CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
+ CIO-DAS802/16 (cio-das802/16)
+Status: works, cio-das802/16 untested - email me if you have tested it
+
+Configuration options:
+ [0] - I/O port base address
+ [1] - IRQ (optional, required for timed or externally triggered conversions)
+
+Notes:
+ IRQ can be omitted, although the cmd interface will not work without it.
+
+ All entries in the channel/gain list must use the same gain and be
+ consecutive channels counting upwards in channel number (these are
+ hardware limitations.)
+
+ I've never tested the gain setting stuff since I only have a
+ DAS-800 board with fixed gain.
+
+ The cio-das802/16 does not have a fifo-empty status bit! Therefore
+ only fifo-half-full transfers are possible with this card.
+*/
+/*
+
+cmd triggers supported:
+ start_src: TRIG_NOW | TRIG_EXT
+ scan_begin_src: TRIG_FOLLOW
+ scan_end_src: TRIG_COUNT
+ convert_src: TRIG_TIMER | TRIG_EXT
+ stop_src: TRIG_NONE | TRIG_COUNT
+
+
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+
+#include "8253.h"
+#include "comedi_fc.h"
+
+#define DAS800_SIZE 8
+#define TIMER_BASE 1000
+#define N_CHAN_AI 8 // number of analog input channels
+
+/* Registers for the das800 */
+
+#define DAS800_LSB 0
+#define FIFO_EMPTY 0x1
+#define FIFO_OVF 0x2
+#define DAS800_MSB 1
+#define DAS800_CONTROL1 2
+#define CONTROL1_INTE 0x8
+#define DAS800_CONV_CONTROL 2
+#define ITE 0x1
+#define CASC 0x2
+#define DTEN 0x4
+#define IEOC 0x8
+#define EACS 0x10
+#define CONV_HCEN 0x80
+#define DAS800_SCAN_LIMITS 2
+#define DAS800_STATUS 2
+#define IRQ 0x8
+#define BUSY 0x80
+#define DAS800_GAIN 3
+#define CIO_FFOV 0x8 // fifo overflow for cio-das802/16
+#define CIO_ENHF 0x90 // interrupt fifo half full for cio-das802/16
+#define CONTROL1 0x80
+#define CONV_CONTROL 0xa0
+#define SCAN_LIMITS 0xc0
+#define ID 0xe0
+#define DAS800_8254 4
+#define DAS800_STATUS2 7
+#define STATUS2_HCEN 0x80
+#define STATUS2_INTE 0X20
+#define DAS800_ID 7
+
+struct das800_board {
+ const char *name;
+ int ai_speed;
+ const struct comedi_lrange *ai_range;
+ int resolution;
+};
+
+//analog input ranges
+static const struct comedi_lrange range_das800_ai = {
+ 1,
+ {
+ RANGE(-5, 5),
+ }
+};
+
+static const struct comedi_lrange range_das801_ai = {
+ 9,
+ {
+ RANGE(-5, 5),
+ RANGE(-10, 10),
+ RANGE(0, 10),
+ RANGE(-0.5, 0.5),
+ RANGE(0, 1),
+ RANGE(-0.05, 0.05),
+ RANGE(0, 0.1),
+ RANGE(-0.01, 0.01),
+ RANGE(0, 0.02),
+ }
+};
+
+static const struct comedi_lrange range_cio_das801_ai = {
+ 9,
+ {
+ RANGE(-5, 5),
+ RANGE(-10, 10),
+ RANGE(0, 10),
+ RANGE(-0.5, 0.5),
+ RANGE(0, 1),
+ RANGE(-0.05, 0.05),
+ RANGE(0, 0.1),
+ RANGE(-0.005, 0.005),
+ RANGE(0, 0.01),
+ }
+};
+
+static const struct comedi_lrange range_das802_ai = {
+ 9,
+ {
+ RANGE(-5, 5),
+ RANGE(-10, 10),
+ RANGE(0, 10),
+ RANGE(-2.5, 2.5),
+ RANGE(0, 5),
+ RANGE(-1.25, 1.25),
+ RANGE(0, 2.5),
+ RANGE(-0.625, 0.625),
+ RANGE(0, 1.25),
+ }
+};
+
+static const struct comedi_lrange range_das80216_ai = {
+ 8,
+ {
+ RANGE(-10, 10),
+ RANGE(0, 10),
+ RANGE(-5, 5),
+ RANGE(0, 5),
+ RANGE(-2.5, 2.5),
+ RANGE(0, 2.5),
+ RANGE(-1.25, 1.25),
+ RANGE(0, 1.25),
+ }
+};
+
+enum { das800, ciodas800, das801, ciodas801, das802, ciodas802, ciodas80216 };
+
+static const struct das800_board das800_boards[] = {
+ {
+ name: "das-800",
+ ai_speed:25000,
+ ai_range:&range_das800_ai,
+ resolution:12,
+ },
+ {
+ name: "cio-das800",
+ ai_speed:20000,
+ ai_range:&range_das800_ai,
+ resolution:12,
+ },
+ {
+ name: "das-801",
+ ai_speed:25000,
+ ai_range:&range_das801_ai,
+ resolution:12,
+ },
+ {
+ name: "cio-das801",
+ ai_speed:20000,
+ ai_range:&range_cio_das801_ai,
+ resolution:12,
+ },
+ {
+ name: "das-802",
+ ai_speed:25000,
+ ai_range:&range_das802_ai,
+ resolution:12,
+ },
+ {
+ name: "cio-das802",
+ ai_speed:20000,
+ ai_range:&range_das802_ai,
+ resolution:12,
+ },
+ {
+ name: "cio-das802/16",
+ ai_speed:10000,
+ ai_range:&range_das80216_ai,
+ resolution:16,
+ },
+};
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct das800_board *)dev->board_ptr)
+
+struct das800_private {
+ volatile unsigned int count; /* number of data points left to be taken */
+ volatile int forever; /* flag indicating whether we should take data forever */
+ unsigned int divisor1; /* value to load into board's counter 1 for timed conversions */
+ unsigned int divisor2; /* value to load into board's counter 2 for timed conversions */
+ volatile int do_bits; /* digital output bits */
+};
+
+#define devpriv ((struct das800_private *)dev->private)
+
+static int das800_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int das800_detach(struct comedi_device * dev);
+static int das800_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+
+static struct comedi_driver driver_das800 = {
+ driver_name:"das800",
+ module:THIS_MODULE,
+ attach:das800_attach,
+ detach:das800_detach,
+ num_names:sizeof(das800_boards) / sizeof(struct das800_board),
+ board_name:&das800_boards[0].name,
+ offset:sizeof(struct das800_board),
+};
+
+static irqreturn_t das800_interrupt(int irq, void *d PT_REGS_ARG);
+static void enable_das800(struct comedi_device * dev);
+static void disable_das800(struct comedi_device * dev);
+static int das800_ai_do_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int das800_ai_do_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int das800_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das800_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das800_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int das800_probe(struct comedi_device * dev);
+static int das800_set_frequency(struct comedi_device * dev);
+
+/* checks and probes das-800 series board type */
+static int das800_probe(struct comedi_device * dev)
+{
+ int id_bits;
+ unsigned long irq_flags;
+ int board;
+
+ // 'comedi spin lock irqsave' disables even rt interrupts, we use them to protect indirect addressing
+ comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
+ outb(ID, dev->iobase + DAS800_GAIN); /* select base address + 7 to be ID register */
+ id_bits = inb(dev->iobase + DAS800_ID) & 0x3; /* get id bits */
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ board = thisboard - das800_boards;
+
+ switch (id_bits) {
+ case 0x0:
+ if (board == das800) {
+ printk(" Board model: DAS-800\n");
+ return board;
+ }
+ if (board == ciodas800) {
+ printk(" Board model: CIO-DAS800\n");
+ return board;
+ }
+ printk(" Board model (probed): DAS-800\n");
+ return das800;
+ break;
+ case 0x2:
+ if (board == das801) {
+ printk(" Board model: DAS-801\n");
+ return board;
+ }
+ if (board == ciodas801) {
+ printk(" Board model: CIO-DAS801\n");
+ return board;
+ }
+ printk(" Board model (probed): DAS-801\n");
+ return das801;
+ break;
+ case 0x3:
+ if (board == das802) {
+ printk(" Board model: DAS-802\n");
+ return board;
+ }
+ if (board == ciodas802) {
+ printk(" Board model: CIO-DAS802\n");
+ return board;
+ }
+ if (board == ciodas80216) {
+ printk(" Board model: CIO-DAS802/16\n");
+ return board;
+ }
+ printk(" Board model (probed): DAS-802\n");
+ return das802;
+ break;
+ default:
+ printk(" Board model: probe returned 0x%x (unknown)\n",
+ id_bits);
+ return board;
+ break;
+ }
+ return -1;
+}
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_INITCLEANUP(driver_das800);
+
+/* interrupt service routine */
+static irqreturn_t das800_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ short i; /* loop index */
+ short dataPoint = 0;
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev; /* analog input subdevice */
+ struct comedi_async *async;
+ int status;
+ unsigned long irq_flags;
+ static const int max_loops = 128; // half-fifo size for cio-das802/16
+ // flags
+ int fifo_empty = 0;
+ int fifo_overflow = 0;
+
+ status = inb(dev->iobase + DAS800_STATUS);
+ /* if interrupt was not generated by board or driver not attached, quit */
+ if (!(status & IRQ))
+ return IRQ_NONE;
+ if (!(dev->attached))
+ return IRQ_HANDLED;
+
+ /* wait until here to initialize async, since we will get null dereference
+ * if interrupt occurs before driver is fully attached!
+ */
+ async = s->async;
+
+ // if hardware conversions are not enabled, then quit
+ comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
+ outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select base address + 7 to be STATUS2 register */
+ status = inb(dev->iobase + DAS800_STATUS2) & STATUS2_HCEN;
+ /* don't release spinlock yet since we want to make sure noone else disables hardware conversions */
+ if (status == 0) {
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ return IRQ_HANDLED;
+ }
+
+ /* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */
+ for (i = 0; i < max_loops; i++) {
+ /* read 16 bits from dev->iobase and dev->iobase + 1 */
+ dataPoint = inb(dev->iobase + DAS800_LSB);
+ dataPoint += inb(dev->iobase + DAS800_MSB) << 8;
+ if (thisboard->resolution == 12) {
+ fifo_empty = dataPoint & FIFO_EMPTY;
+ fifo_overflow = dataPoint & FIFO_OVF;
+ if (fifo_overflow)
+ break;
+ } else {
+ fifo_empty = 0; // cio-das802/16 has no fifo empty status bit
+ }
+ if (fifo_empty) {
+ break;
+ }
+ /* strip off extraneous bits for 12 bit cards */
+ if (thisboard->resolution == 12)
+ dataPoint = (dataPoint >> 4) & 0xfff;
+ /* if there are more data points to collect */
+ if (devpriv->count > 0 || devpriv->forever == 1) {
+ /* write data point to buffer */
+ cfc_write_to_buffer(s, dataPoint);
+ if (devpriv->count > 0)
+ devpriv->count--;
+ }
+ }
+ async->events |= COMEDI_CB_BLOCK;
+ /* check for fifo overflow */
+ if (thisboard->resolution == 12) {
+ fifo_overflow = dataPoint & FIFO_OVF;
+ // else cio-das802/16
+ } else {
+ fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
+ }
+ if (fifo_overflow) {
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ comedi_error(dev, "DAS800 FIFO overflow");
+ das800_cancel(dev, dev->subdevices + 0);
+ async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ async->events = 0;
+ return IRQ_HANDLED;
+ }
+ if (devpriv->count > 0 || devpriv->forever == 1) {
+ /* Re-enable card's interrupt.
+ * We already have spinlock, so indirect addressing is safe */
+ outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
+ outb(CONTROL1_INTE | devpriv->do_bits,
+ dev->iobase + DAS800_CONTROL1);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ /* otherwise, stop taking data */
+ } else {
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ disable_das800(dev); /* diable hardware triggered conversions */
+ async->events |= COMEDI_CB_EOA;
+ }
+ comedi_event(dev, s);
+ async->events = 0;
+ return IRQ_HANDLED;
+}
+
+static int das800_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase = it->options[0];
+ unsigned int irq = it->options[1];
+ unsigned long irq_flags;
+ int board;
+
+ printk("comedi%d: das800: io 0x%lx", dev->minor, iobase);
+ if (irq) {
+ printk(", irq %u", irq);
+ }
+ printk("\n");
+
+ /* allocate and initialize dev->private */
+ if (alloc_private(dev, sizeof(struct das800_private)) < 0)
+ return -ENOMEM;
+
+ if (iobase == 0) {
+ printk("io base address required for das800\n");
+ return -EINVAL;
+ }
+
+ /* check if io addresses are available */
+ if (!request_region(iobase, DAS800_SIZE, "das800")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ board = das800_probe(dev);
+ if (board < 0) {
+ printk("unable to determine board type\n");
+ return -ENODEV;
+ }
+ dev->board_ptr = das800_boards + board;
+
+ /* grab our IRQ */
+ if (irq == 1 || irq > 7) {
+ printk("irq out of range\n");
+ return -EINVAL;
+ }
+ if (irq) {
+ if (comedi_request_irq(irq, das800_interrupt, 0, "das800", dev)) {
+ printk("unable to allocate irq %u\n", irq);
+ return -EINVAL;
+ }
+ }
+ dev->irq = irq;
+
+ dev->board_name = thisboard->name;
+
+ if (alloc_subdevices(dev, 3) < 0)
+ return -ENOMEM;
+
+ /* analog input subdevice */
+ s = dev->subdevices + 0;
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
+ s->n_chan = 8;
+ s->len_chanlist = 8;
+ s->maxdata = (1 << thisboard->resolution) - 1;
+ s->range_table = thisboard->ai_range;
+ s->do_cmd = das800_ai_do_cmd;
+ s->do_cmdtest = das800_ai_do_cmdtest;
+ s->insn_read = das800_ai_rinsn;
+ s->cancel = das800_cancel;
+
+ /* di */
+ s = dev->subdevices + 1;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 3;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das800_di_rbits;
+
+ /* do */
+ s = dev->subdevices + 2;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das800_do_wbits;
+
+ disable_das800(dev);
+
+ /* initialize digital out channels */
+ comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
+ outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
+ outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ return 0;
+};
+
+static int das800_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: das800: remove\n", dev->minor);
+
+ /* only free stuff if it has been allocated by _attach */
+ if (dev->iobase)
+ release_region(dev->iobase, DAS800_SIZE);
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+ return 0;
+};
+
+static int das800_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ devpriv->forever = 0;
+ devpriv->count = 0;
+ disable_das800(dev);
+ return 0;
+}
+
+/* enable_das800 makes the card start taking hardware triggered conversions */
+static void enable_das800(struct comedi_device * dev)
+{
+ unsigned long irq_flags;
+ comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
+ // enable fifo-half full interrupts for cio-das802/16
+ if (thisboard->resolution == 16)
+ outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
+ outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
+ outb(CONV_HCEN, dev->iobase + DAS800_CONV_CONTROL); /* enable hardware triggering */
+ outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
+ outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1); /* enable card's interrupt */
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+}
+
+/* disable_das800 stops hardware triggered conversions */
+static void disable_das800(struct comedi_device * dev)
+{
+ unsigned long irq_flags;
+ comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
+ outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
+ outb(0x0, dev->iobase + DAS800_CONV_CONTROL); /* disable hardware triggering of conversions */
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+}
+
+static int das800_ai_do_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+ int gain, startChan;
+ int i;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_EXT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_FOLLOW;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
+ err++;
+ if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < thisboard->ai_speed) {
+ cmd->convert_arg = thisboard->ai_speed;
+ err++;
+ }
+ }
+ if (!cmd->chanlist_len) {
+ cmd->chanlist_len = 1;
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (!cmd->stop_arg) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ } else { /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ /* calculate counter values that give desired timing */
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
+ &(devpriv->divisor2), &(cmd->convert_arg),
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->convert_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ // check channel/gain list against card's limitations
+ if (cmd->chanlist) {
+ gain = CR_RANGE(cmd->chanlist[0]);
+ startChan = CR_CHAN(cmd->chanlist[0]);
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ if (CR_CHAN(cmd->chanlist[i]) !=
+ (startChan + i) % N_CHAN_AI) {
+ comedi_error(dev,
+ "entries in chanlist must be consecutive channels, counting upwards\n");
+ err++;
+ }
+ if (CR_RANGE(cmd->chanlist[i]) != gain) {
+ comedi_error(dev,
+ "entries in chanlist must all have the same gain\n");
+ err++;
+ }
+ }
+ }
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int das800_ai_do_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int startChan, endChan, scan, gain;
+ int conv_bits;
+ unsigned long irq_flags;
+ struct comedi_async *async = s->async;
+
+ if (!dev->irq) {
+ comedi_error(dev,
+ "no irq assigned for das-800, cannot do hardware conversions");
+ return -1;
+ }
+
+ disable_das800(dev);
+
+ /* set channel scan limits */
+ startChan = CR_CHAN(async->cmd.chanlist[0]);
+ endChan = (startChan + async->cmd.chanlist_len - 1) % 8;
+ scan = (endChan << 3) | startChan;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
+ outb(SCAN_LIMITS, dev->iobase + DAS800_GAIN); /* select base address + 2 to be scan limits register */
+ outb(scan, dev->iobase + DAS800_SCAN_LIMITS); /* set scan limits */
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ /* set gain */
+ gain = CR_RANGE(async->cmd.chanlist[0]);
+ if (thisboard->resolution == 12 && gain > 0)
+ gain += 0x7;
+ gain &= 0xf;
+ outb(gain, dev->iobase + DAS800_GAIN);
+
+ switch (async->cmd.stop_src) {
+ case TRIG_COUNT:
+ devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
+ devpriv->forever = 0;
+ break;
+ case TRIG_NONE:
+ devpriv->forever = 1;
+ devpriv->count = 0;
+ break;
+ default:
+ break;
+ }
+
+ /* enable auto channel scan, send interrupts on end of conversion
+ * and set clock source to internal or external
+ */
+ conv_bits = 0;
+ conv_bits |= EACS | IEOC;
+ if (async->cmd.start_src == TRIG_EXT)
+ conv_bits |= DTEN;
+ switch (async->cmd.convert_src) {
+ case TRIG_TIMER:
+ conv_bits |= CASC | ITE;
+ /* set conversion frequency */
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
+ &(devpriv->divisor2), &(async->cmd.convert_arg),
+ async->cmd.flags & TRIG_ROUND_MASK);
+ if (das800_set_frequency(dev) < 0) {
+ comedi_error(dev, "Error setting up counters");
+ return -1;
+ }
+ break;
+ case TRIG_EXT:
+ break;
+ default:
+ break;
+ }
+
+ comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
+ outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
+ outb(conv_bits, dev->iobase + DAS800_CONV_CONTROL);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ async->events = 0;
+ enable_das800(dev);
+ return 0;
+}
+
+static int das800_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i, n;
+ int chan;
+ int range;
+ int lsb, msb;
+ int timeout = 1000;
+ unsigned long irq_flags;
+
+ disable_das800(dev); /* disable hardware conversions (enables software conversions) */
+
+ /* set multiplexer */
+ chan = CR_CHAN(insn->chanspec);
+
+ comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
+ outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
+ outb(chan | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ /* set gain / range */
+ range = CR_RANGE(insn->chanspec);
+ if (thisboard->resolution == 12 && range)
+ range += 0x7;
+ range &= 0xf;
+ outb(range, dev->iobase + DAS800_GAIN);
+
+ comedi_udelay(5);
+
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion */
+ outb_p(0, dev->iobase + DAS800_MSB);
+
+ for (i = 0; i < timeout; i++) {
+ if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
+ break;
+ }
+ if (i == timeout) {
+ comedi_error(dev, "timeout");
+ return -ETIME;
+ }
+ lsb = inb(dev->iobase + DAS800_LSB);
+ msb = inb(dev->iobase + DAS800_MSB);
+ if (thisboard->resolution == 12) {
+ data[n] = (lsb >> 4) & 0xff;
+ data[n] |= (msb << 4);
+ } else {
+ data[n] = (msb << 8) | lsb;
+ }
+ }
+
+ return n;
+}
+
+static int das800_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int bits;
+
+ bits = inb(dev->iobase + DAS800_STATUS) >> 4;
+ bits &= 0x7;
+ data[1] = bits;
+ data[0] = 0;
+
+ return 2;
+}
+
+static int das800_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int wbits;
+ unsigned long irq_flags;
+
+ // only set bits that have been masked
+ data[0] &= 0xf;
+ wbits = devpriv->do_bits >> 4;
+ wbits &= ~data[0];
+ wbits |= data[0] & data[1];
+ devpriv->do_bits = wbits << 4;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
+ outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
+ outb(devpriv->do_bits | CONTROL1_INTE, dev->iobase + DAS800_CONTROL1);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ data[1] = wbits;
+
+ return 2;
+}
+
+/* loads counters with divisor1, divisor2 from private structure */
+static int das800_set_frequency(struct comedi_device * dev)
+{
+ int err = 0;
+
+ if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
+ err++;
+ if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
+ err++;
+ if (err)
+ return -1;
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/dmm32at.c b/drivers/staging/comedi/drivers/dmm32at.c
new file mode 100644
index 000000000000..829083651edc
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dmm32at.c
@@ -0,0 +1,1081 @@
+/*
+ comedi/drivers/dmm32at.c
+ Diamond Systems mm32at code for a Comedi driver
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: dmm32at
+Description: Diamond Systems mm32at driver.
+Devices:
+Author: Perry J. Piplani <perry.j.piplani@nasa.gov>
+Updated: Fri Jun 4 09:13:24 CDT 2004
+Status: experimental
+
+This driver is for the Diamond Systems MM-32-AT board
+http://www.diamondsystems.com/products/diamondmm32at It is being used
+on serveral projects inside NASA, without problems so far. For analog
+input commands, TRIG_EXT is not yet supported at all..
+
+Configuration Options:
+ comedi_config /dev/comedi0 dmm32at baseaddr,irq
+*/
+
+/*
+ * The previous block comment is used to automatically generate
+ * documentation in Comedi and Comedilib. The fields:
+ *
+ * Driver: the name of the driver
+ * Description: a short phrase describing the driver. Don't list boards.
+ * Devices: a full list of the boards that attempt to be supported by
+ * the driver. Format is "(manufacturer) board name [comedi name]",
+ * where comedi_name is the name that is used to configure the board.
+ * See the comment near board_name: in the struct comedi_driver structure
+ * below. If (manufacturer) or [comedi name] is missing, the previous
+ * value is used.
+ * Author: you
+ * Updated: date when the _documentation_ was last updated. Use 'date -R'
+ * to get a value for this.
+ * Status: a one-word description of the status. Valid values are:
+ * works - driver works correctly on most boards supported, and
+ * passes comedi_test.
+ * unknown - unknown. Usually put there by ds.
+ * experimental - may not work in any particular release. Author
+ * probably wants assistance testing it.
+ * bitrotten - driver has not been update in a long time, probably
+ * doesn't work, and probably is missing support for significant
+ * Comedi interface features.
+ * untested - author probably wrote it "blind", and is believed to
+ * work, but no confirmation.
+ *
+ * These headers should be followed by a blank line, and any comments
+ * you wish to say about the driver. The comment area is the place
+ * to put any known bugs, limitations, unsupported features, supported
+ * command triggers, whether or not commands are supported on particular
+ * subdevices, etc.
+ *
+ * Somewhere in the comment should be information about configuration
+ * options that are used with comedi_config.
+ */
+
+#include "../comedidev.h"
+#include <linux/ioport.h>
+
+/* Board register addresses */
+
+#define DMM32AT_MEMSIZE 0x10
+
+#define DMM32AT_CONV 0x00
+#define DMM32AT_AILSB 0x00
+#define DMM32AT_AUXDOUT 0x01
+#define DMM32AT_AIMSB 0x01
+#define DMM32AT_AILOW 0x02
+#define DMM32AT_AIHIGH 0x03
+
+#define DMM32AT_DACLSB 0x04
+#define DMM32AT_DACSTAT 0x04
+#define DMM32AT_DACMSB 0x05
+
+#define DMM32AT_FIFOCNTRL 0x07
+#define DMM32AT_FIFOSTAT 0x07
+
+#define DMM32AT_CNTRL 0x08
+#define DMM32AT_AISTAT 0x08
+
+#define DMM32AT_INTCLOCK 0x09
+
+#define DMM32AT_CNTRDIO 0x0a
+
+#define DMM32AT_AICONF 0x0b
+#define DMM32AT_AIRBACK 0x0b
+
+#define DMM32AT_CLK1 0x0d
+#define DMM32AT_CLK2 0x0e
+#define DMM32AT_CLKCT 0x0f
+
+#define DMM32AT_DIOA 0x0c
+#define DMM32AT_DIOB 0x0d
+#define DMM32AT_DIOC 0x0e
+#define DMM32AT_DIOCONF 0x0f
+
+#define dmm_inb(cdev,reg) inb((cdev->iobase)+reg)
+#define dmm_outb(cdev,reg,valu) outb(valu,(cdev->iobase)+reg)
+
+/* Board register values. */
+
+/* DMM32AT_DACSTAT 0x04 */
+#define DMM32AT_DACBUSY 0x80
+
+/* DMM32AT_FIFOCNTRL 0x07 */
+#define DMM32AT_FIFORESET 0x02
+#define DMM32AT_SCANENABLE 0x04
+
+/* DMM32AT_CNTRL 0x08 */
+#define DMM32AT_RESET 0x20
+#define DMM32AT_INTRESET 0x08
+#define DMM32AT_CLKACC 0x00
+#define DMM32AT_DIOACC 0x01
+
+/* DMM32AT_AISTAT 0x08 */
+#define DMM32AT_STATUS 0x80
+
+/* DMM32AT_INTCLOCK 0x09 */
+#define DMM32AT_ADINT 0x80
+#define DMM32AT_CLKSEL 0x03
+
+/* DMM32AT_CNTRDIO 0x0a */
+#define DMM32AT_FREQ12 0x80
+
+/* DMM32AT_AICONF 0x0b */
+#define DMM32AT_RANGE_U10 0x0c
+#define DMM32AT_RANGE_U5 0x0d
+#define DMM32AT_RANGE_B10 0x08
+#define DMM32AT_RANGE_B5 0x00
+#define DMM32AT_SCINT_20 0x00
+#define DMM32AT_SCINT_15 0x10
+#define DMM32AT_SCINT_10 0x20
+#define DMM32AT_SCINT_5 0x30
+
+/* DMM32AT_CLKCT 0x0f */
+#define DMM32AT_CLKCT1 0x56 /* mode3 counter 1 - write low byte only */
+#define DMM32AT_CLKCT2 0xb6 /* mode3 counter 2 - write high and low byte */
+
+/* DMM32AT_DIOCONF 0x0f */
+#define DMM32AT_DIENABLE 0x80
+#define DMM32AT_DIRA 0x10
+#define DMM32AT_DIRB 0x02
+#define DMM32AT_DIRCL 0x01
+#define DMM32AT_DIRCH 0x08
+
+/* board AI ranges in comedi structure */
+static const struct comedi_lrange dmm32at_airanges = {
+ 4,
+ {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ }
+};
+
+/* register values for above ranges */
+static const unsigned char dmm32at_rangebits[] = {
+ DMM32AT_RANGE_U10,
+ DMM32AT_RANGE_U5,
+ DMM32AT_RANGE_B10,
+ DMM32AT_RANGE_B5,
+};
+
+/* only one of these ranges is valid, as set by a jumper on the
+ * board. The application should only use the range set by the jumper
+ */
+static const struct comedi_lrange dmm32at_aoranges = {
+ 4,
+ {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ }
+};
+
+/*
+ * Board descriptions for two imaginary boards. Describing the
+ * boards in this way is optional, and completely driver-dependent.
+ * Some drivers use arrays such as this, other do not.
+ */
+struct dmm32at_board {
+ const char *name;
+ int ai_chans;
+ int ai_bits;
+ const struct comedi_lrange *ai_ranges;
+ int ao_chans;
+ int ao_bits;
+ const struct comedi_lrange *ao_ranges;
+ int have_dio;
+ int dio_chans;
+};
+static const struct dmm32at_board dmm32at_boards[] = {
+ {
+ name: "dmm32at",
+ ai_chans:32,
+ ai_bits: 16,
+ ai_ranges:&dmm32at_airanges,
+ ao_chans:4,
+ ao_bits: 12,
+ ao_ranges:&dmm32at_aoranges,
+ have_dio:1,
+ dio_chans:24,
+ },
+};
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct dmm32at_board *)dev->board_ptr)
+
+/* 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 struct comedi_device struct.
+ */
+struct dmm32at_private {
+
+ int data;
+ int ai_inuse;
+ unsigned int ai_scans_left;
+
+ /* Used for AO readback */
+ unsigned int ao_readback[4];
+ unsigned char dio_config;
+
+};
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((struct dmm32at_private *)dev->private)
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int dmm32at_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int dmm32at_detach(struct comedi_device * dev);
+static struct comedi_driver driver_dmm32at = {
+ driver_name:"dmm32at",
+ module:THIS_MODULE,
+ attach:dmm32at_attach,
+ detach:dmm32at_detach,
+/* It is not necessary to implement the following members if you are
+ * writing a driver for a ISA PnP or PCI card */
+/* Most drivers will support multiple types of boards by
+ * having an array of board structures. These were defined
+ * in dmm32at_boards[] above. Note that the element 'name'
+ * was first in the structure -- Comedi uses this fact to
+ * extract the name of the board without knowing any details
+ * about the structure except for its length.
+ * When a device is attached (by comedi_config), the name
+ * of the device is given to Comedi, and Comedi tries to
+ * match it by going through the list of board names. If
+ * there is a match, the address of the pointer is put
+ * into dev->board_ptr and driver->attach() is called.
+ *
+ * Note that these are not necessary if you can determine
+ * the type of board in software. ISA PnP, PCI, and PCMCIA
+ * devices are such boards.
+ */
+ board_name:&dmm32at_boards[0].name,
+ offset:sizeof(struct dmm32at_board),
+ num_names:sizeof(dmm32at_boards) / sizeof(struct dmm32at_board),
+};
+
+/* prototypes for driver functions below */
+static int dmm32at_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dmm32at_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dmm32at_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dmm32at_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dmm32at_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dmm32at_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int dmm32at_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int dmm32at_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static int dmm32at_ns_to_timer(unsigned int *ns, int round);
+static irqreturn_t dmm32at_isr(int irq, void *d PT_REGS_ARG);
+void dmm32at_setaitimer(struct comedi_device * dev, unsigned int nansec);
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int dmm32at_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int ret;
+ struct comedi_subdevice *s;
+ unsigned char aihi, ailo, fifostat, aistat, intstat, airback;
+ unsigned long iobase;
+ unsigned int irq;
+
+ iobase = it->options[0];
+ irq = it->options[1];
+
+ printk("comedi%d: dmm32at: attaching\n", dev->minor);
+ printk("dmm32at: probing at address 0x%04lx, irq %u\n", iobase, irq);
+
+ /* register address space */
+ if (!request_region(iobase, DMM32AT_MEMSIZE, thisboard->name)) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ /* the following just makes sure the board is there and gets
+ it to a known state */
+
+ /* reset the board */
+ dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_RESET);
+
+ /* allow a millisecond to reset */
+ udelay(1000);
+
+ /* zero scan and fifo control */
+ dmm_outb(dev, DMM32AT_FIFOCNTRL, 0x0);
+
+ /* zero interrupt and clock control */
+ dmm_outb(dev, DMM32AT_INTCLOCK, 0x0);
+
+ /* write a test channel range, the high 3 bits should drop */
+ dmm_outb(dev, DMM32AT_AILOW, 0x80);
+ dmm_outb(dev, DMM32AT_AIHIGH, 0xff);
+
+ /* set the range at 10v unipolar */
+ dmm_outb(dev, DMM32AT_AICONF, DMM32AT_RANGE_U10);
+
+ /* should take 10 us to settle, here's a hundred */
+ udelay(100);
+
+ /* read back the values */
+ ailo = dmm_inb(dev, DMM32AT_AILOW);
+ aihi = dmm_inb(dev, DMM32AT_AIHIGH);
+ fifostat = dmm_inb(dev, DMM32AT_FIFOSTAT);
+ aistat = dmm_inb(dev, DMM32AT_AISTAT);
+ intstat = dmm_inb(dev, DMM32AT_INTCLOCK);
+ airback = dmm_inb(dev, DMM32AT_AIRBACK);
+
+ printk("dmm32at: lo=0x%02x hi=0x%02x fifostat=0x%02x\n",
+ ailo, aihi, fifostat);
+ printk("dmm32at: aistat=0x%02x intstat=0x%02x airback=0x%02x\n",
+ aistat, intstat, airback);
+
+ if ((ailo != 0x00) || (aihi != 0x1f) || (fifostat != 0x80) ||
+ (aistat != 0x60 || (intstat != 0x00) || airback != 0x0c)) {
+ printk("dmmat32: board detection failed\n");
+ return -EIO;
+ }
+
+ /* board is there, register interrupt */
+ if (irq) {
+ ret = comedi_request_irq(irq, dmm32at_isr, 0, thisboard->name,
+ dev);
+ if (ret < 0) {
+ printk("irq conflict\n");
+ return ret;
+ }
+ dev->irq = irq;
+ }
+
+/*
+ * If you can probe the device to determine what device in a series
+ * it is, this is the place to do it. Otherwise, dev->board_ptr
+ * should already be initialized.
+ */
+ //dev->board_ptr = dmm32at_probe(dev);
+
+/*
+ * Initialize dev->board_name. Note that we can use the "thisboard"
+ * macro now, since we just initialized it in the last line.
+ */
+ dev->board_name = thisboard->name;
+
+/*
+ * Allocate the private structure area. alloc_private() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_private(dev, sizeof(struct dmm32at_private)) < 0)
+ return -ENOMEM;
+
+/*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_subdevices(dev, 3) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ dev->read_subdev = s;
+ /* analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ /* we support single-ended (ground) and differential */
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
+ s->n_chan = thisboard->ai_chans;
+ s->maxdata = (1 << thisboard->ai_bits) - 1;
+ s->range_table = thisboard->ai_ranges;
+ s->len_chanlist = 32; /* This is the maximum chanlist length that
+ the board can handle */
+ s->insn_read = dmm32at_ai_rinsn;
+ s->do_cmd = dmm32at_ai_cmd;
+ s->do_cmdtest = dmm32at_ai_cmdtest;
+ s->cancel = dmm32at_ai_cancel;
+
+ s = dev->subdevices + 1;
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = thisboard->ao_chans;
+ s->maxdata = (1 << thisboard->ao_bits) - 1;
+ s->range_table = thisboard->ao_ranges;
+ s->insn_write = dmm32at_ao_winsn;
+ s->insn_read = dmm32at_ao_rinsn;
+
+ s = dev->subdevices + 2;
+ /* digital i/o subdevice */
+ if (thisboard->have_dio) {
+
+ /* get access to the DIO regs */
+ dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_DIOACC);
+ /* set the DIO's to the defualt input setting */
+ devpriv->dio_config = DMM32AT_DIRA | DMM32AT_DIRB |
+ DMM32AT_DIRCL | DMM32AT_DIRCH | DMM32AT_DIENABLE;
+ dmm_outb(dev, DMM32AT_DIOCONF, devpriv->dio_config);
+
+ /* set up the subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = thisboard->dio_chans;
+ s->maxdata = 1;
+ s->state = 0;
+ s->range_table = &range_digital;
+ s->insn_bits = dmm32at_dio_insn_bits;
+ s->insn_config = dmm32at_dio_insn_config;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* success */
+ printk("comedi%d: dmm32at: attached\n", dev->minor);
+
+ return 1;
+
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int dmm32at_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: dmm32at: remove\n", dev->minor);
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+ if (dev->iobase)
+ release_region(dev->iobase, DMM32AT_MEMSIZE);
+
+ return 0;
+}
+
+/*
+ * "instructions" read/write data in "one-shot" or "software-triggered"
+ * mode.
+ */
+
+static int dmm32at_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, i;
+ unsigned int d;
+ unsigned char status;
+ unsigned short msb, lsb;
+ unsigned char chan;
+ int range;
+
+ /* get the channel and range number */
+
+ chan = CR_CHAN(insn->chanspec) & (s->n_chan - 1);
+ range = CR_RANGE(insn->chanspec);
+
+ //printk("channel=0x%02x, range=%d\n",chan,range);
+
+ /* zero scan and fifo control and reset fifo */
+ dmm_outb(dev, DMM32AT_FIFOCNTRL, DMM32AT_FIFORESET);
+
+ /* write the ai channel range regs */
+ dmm_outb(dev, DMM32AT_AILOW, chan);
+ dmm_outb(dev, DMM32AT_AIHIGH, chan);
+ /* set the range bits */
+ dmm_outb(dev, DMM32AT_AICONF, dmm32at_rangebits[range]);
+
+ /* wait for circuit to settle */
+ for (i = 0; i < 40000; i++) {
+ status = dmm_inb(dev, DMM32AT_AIRBACK);
+ if ((status & DMM32AT_STATUS) == 0)
+ break;
+ }
+ if (i == 40000) {
+ printk("timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion */
+ dmm_outb(dev, DMM32AT_CONV, 0xff);
+ /* wait for conversion to end */
+ for (i = 0; i < 40000; i++) {
+ status = dmm_inb(dev, DMM32AT_AISTAT);
+ if ((status & DMM32AT_STATUS) == 0)
+ break;
+ }
+ if (i == 40000) {
+ printk("timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* read data */
+ lsb = dmm_inb(dev, DMM32AT_AILSB);
+ msb = dmm_inb(dev, DMM32AT_AIMSB);
+
+ /* invert sign bit to make range unsigned, this is an
+ idiosyncracy of the diamond board, it return
+ conversions as a signed value, i.e. -32768 to
+ 32767, flipping the bit and interpreting it as
+ signed gives you a range of 0 to 65535 which is
+ used by comedi */
+ d = ((msb ^ 0x0080) << 8) + lsb;
+
+ data[n] = d;
+ }
+
+ /* return the number of samples read/written */
+ return n;
+}
+
+static int dmm32at_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+ int start_chan, gain, i;
+
+ //printk("dmmat32 in command test\n");
+
+ /* cmdtest tests a particular command to see if it is valid.
+ * Using the cmdtest ioctl, a user can create a valid cmd
+ * and then have it executes by the cmd ioctl.
+ *
+ * cmdtest returns 1,2,3,4 or 0, depending on which tests
+ * the command passes. */
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER /*| TRIG_EXT */ ;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER /*| TRIG_EXT */ ;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ /* note that mutual compatiblity is not an issue here */
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_EXT)
+ err++;
+ if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+#define MAX_SCAN_SPEED 1000000 /* in nanoseconds */
+#define MIN_SCAN_SPEED 1000000000 /* in nanoseconds */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->scan_begin_arg < MAX_SCAN_SPEED) {
+ cmd->scan_begin_arg = MAX_SCAN_SPEED;
+ err++;
+ }
+ if (cmd->scan_begin_arg > MIN_SCAN_SPEED) {
+ cmd->scan_begin_arg = MIN_SCAN_SPEED;
+ err++;
+ }
+ } else {
+ /* external trigger */
+ /* should be level/edge, hi/lo specification here */
+ /* should specify multiple external triggers */
+ if (cmd->scan_begin_arg > 9) {
+ cmd->scan_begin_arg = 9;
+ err++;
+ }
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg >= 17500)
+ cmd->convert_arg = 20000;
+ else if (cmd->convert_arg >= 12500)
+ cmd->convert_arg = 15000;
+ else if (cmd->convert_arg >= 7500)
+ cmd->convert_arg = 10000;
+ else
+ cmd->convert_arg = 5000;
+
+ } else {
+ /* external trigger */
+ /* see above */
+ if (cmd->convert_arg > 9) {
+ cmd->convert_arg = 9;
+ err++;
+ }
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (cmd->stop_arg > 0xfffffff0) {
+ cmd->stop_arg = 0xfffffff0;
+ err++;
+ }
+ if (cmd->stop_arg == 0) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ dmm32at_ns_to_timer(&cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ dmm32at_ns_to_timer(&cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->convert_arg)
+ err++;
+ if (cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->scan_begin_arg <
+ cmd->convert_arg * cmd->scan_end_arg) {
+ cmd->scan_begin_arg =
+ cmd->convert_arg * cmd->scan_end_arg;
+ err++;
+ }
+ }
+
+ if (err)
+ return 4;
+
+ /* step 5 check the channel list, the channel list for this
+ board must be consecutive and gains must be the same */
+
+ if (cmd->chanlist) {
+ gain = CR_RANGE(cmd->chanlist[0]);
+ start_chan = CR_CHAN(cmd->chanlist[0]);
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ if (CR_CHAN(cmd->chanlist[i]) !=
+ (start_chan + i) % s->n_chan) {
+ comedi_error(dev,
+ "entries in chanlist must be consecutive channels, counting upwards\n");
+ err++;
+ }
+ if (CR_RANGE(cmd->chanlist[i]) != gain) {
+ comedi_error(dev,
+ "entries in chanlist must all have the same gain\n");
+ err++;
+ }
+ }
+ }
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int dmm32at_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int i, range;
+ unsigned char chanlo, chanhi, status;
+
+ if (!cmd->chanlist)
+ return -EINVAL;
+
+ /* get the channel list and range */
+ chanlo = CR_CHAN(cmd->chanlist[0]) & (s->n_chan - 1);
+ chanhi = chanlo + cmd->chanlist_len - 1;
+ if (chanhi >= s->n_chan)
+ return -EINVAL;
+ range = CR_RANGE(cmd->chanlist[0]);
+
+ /* reset fifo */
+ dmm_outb(dev, DMM32AT_FIFOCNTRL, DMM32AT_FIFORESET);
+
+ /* set scan enable */
+ dmm_outb(dev, DMM32AT_FIFOCNTRL, DMM32AT_SCANENABLE);
+
+ /* write the ai channel range regs */
+ dmm_outb(dev, DMM32AT_AILOW, chanlo);
+ dmm_outb(dev, DMM32AT_AIHIGH, chanhi);
+
+ /* set the range bits */
+ dmm_outb(dev, DMM32AT_AICONF, dmm32at_rangebits[range]);
+
+ /* reset the interrupt just in case */
+ dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_INTRESET);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ devpriv->ai_scans_left = cmd->stop_arg;
+ else { /* TRIG_NONE */
+ devpriv->ai_scans_left = 0xffffffff; /* indicates TRIG_NONE to isr */
+ }
+
+ /* wait for circuit to settle */
+ for (i = 0; i < 40000; i++) {
+ status = dmm_inb(dev, DMM32AT_AIRBACK);
+ if ((status & DMM32AT_STATUS) == 0)
+ break;
+ }
+ if (i == 40000) {
+ printk("timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ if (devpriv->ai_scans_left > 1) {
+ /* start the clock and enable the interrupts */
+ dmm32at_setaitimer(dev, cmd->scan_begin_arg);
+ } else {
+ /* start the interrups and initiate a single scan */
+ dmm_outb(dev, DMM32AT_INTCLOCK, DMM32AT_ADINT);
+ dmm_outb(dev, DMM32AT_CONV, 0xff);
+ }
+
+/* printk("dmmat32 in command\n"); */
+
+/* for(i=0;i<cmd->chanlist_len;i++) */
+/* comedi_buf_put(s->async,i*100); */
+
+/* s->async->events |= COMEDI_CB_EOA; */
+/* comedi_event(dev, s); */
+
+ return 0;
+
+}
+
+static int dmm32at_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ devpriv->ai_scans_left = 1;
+ return 0;
+}
+
+static irqreturn_t dmm32at_isr(int irq, void *d PT_REGS_ARG)
+{
+ unsigned char intstat;
+ unsigned int samp;
+ unsigned short msb, lsb;
+ int i;
+ struct comedi_device *dev = d;
+
+ if (!dev->attached) {
+ comedi_error(dev, "spurious interrupt");
+ return IRQ_HANDLED;
+ }
+
+ intstat = dmm_inb(dev, DMM32AT_INTCLOCK);
+
+ if (intstat & DMM32AT_ADINT) {
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ /* read data */
+ lsb = dmm_inb(dev, DMM32AT_AILSB);
+ msb = dmm_inb(dev, DMM32AT_AIMSB);
+
+ /* invert sign bit to make range unsigned */
+ samp = ((msb ^ 0x0080) << 8) + lsb;
+ comedi_buf_put(s->async, samp);
+ }
+
+ if (devpriv->ai_scans_left != 0xffffffff) { /* TRIG_COUNT */
+ devpriv->ai_scans_left--;
+ if (devpriv->ai_scans_left == 0) {
+ /* disable further interrupts and clocks */
+ dmm_outb(dev, DMM32AT_INTCLOCK, 0x0);
+ /* set the buffer to be flushed with an EOF */
+ s->async->events |= COMEDI_CB_EOA;
+ }
+
+ }
+ /* flush the buffer */
+ comedi_event(dev, s);
+ }
+
+ /* reset the interrupt */
+ dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_INTRESET);
+ return IRQ_HANDLED;
+}
+
+/* This function doesn't require a particular form, this is just
+ * what happens to be used in some of the drivers. It should
+ * convert ns nanoseconds to a counter value suitable for programming
+ * the device. Also, it should adjust ns so that it cooresponds to
+ * the actual time that the device will use. */
+static int dmm32at_ns_to_timer(unsigned int *ns, int round)
+{
+ /* trivial timer */
+ /* if your timing is done through two cascaded timers, the
+ * i8253_cascade_ns_to_timer() function in 8253.h can be
+ * very helpful. There are also i8254_load() and i8254_mm_load()
+ * which can be used to load values into the ubiquitous 8254 counters
+ */
+
+ return *ns;
+}
+
+static int dmm32at_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+ unsigned char hi, lo, status;
+
+ /* Writing a list of values to an AO channel is probably not
+ * very useful, but that's how the interface is defined. */
+ for (i = 0; i < insn->n; i++) {
+
+ devpriv->ao_readback[chan] = data[i];
+
+ /* get the low byte */
+ lo = data[i] & 0x00ff;
+ /* high byte also contains channel number */
+ hi = (data[i] >> 8) + chan * (1 << 6);
+ //printk("writing 0x%02x 0x%02x\n",hi,lo);
+ /* write the low and high values to the board */
+ dmm_outb(dev, DMM32AT_DACLSB, lo);
+ dmm_outb(dev, DMM32AT_DACMSB, hi);
+
+ /* wait for circuit to settle */
+ for (i = 0; i < 40000; i++) {
+ status = dmm_inb(dev, DMM32AT_DACSTAT);
+ if ((status & DMM32AT_DACBUSY) == 0)
+ break;
+ }
+ if (i == 40000) {
+ printk("timeout\n");
+ return -ETIMEDOUT;
+ }
+ /* dummy read to update trigger the output */
+ status = dmm_inb(dev, DMM32AT_DACMSB);
+
+ }
+
+ /* return the number of samples read/written */
+ return i;
+}
+
+/* AO subdevices should have a read insn as well as a write insn.
+ * Usually this means copying a value stored in devpriv. */
+static int dmm32at_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = devpriv->ao_readback[chan];
+
+ return i;
+}
+
+/* DIO devices are slightly special. Although it is possible to
+ * implement the insn_read/insn_write interface, it is much more
+ * useful to applications if you implement the insn_bits interface.
+ * This allows packed reading/writing of the DIO channels. The
+ * comedi core can convert between insn_bits and insn_read/write */
+static int dmm32at_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned char diobits;
+
+ if (insn->n != 2)
+ return -EINVAL;
+
+ /* The insn data is a mask in data[0] and the new data
+ * in data[1], each channel cooresponding to a bit. */
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+ /* Write out the new digital output lines */
+ //outw(s->state,dev->iobase + DMM32AT_DIO);
+ }
+
+ /* get access to the DIO regs */
+ dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_DIOACC);
+
+ /* if either part of dio is set for output */
+ if (((devpriv->dio_config & DMM32AT_DIRCL) == 0) ||
+ ((devpriv->dio_config & DMM32AT_DIRCH) == 0)) {
+ diobits = (s->state & 0x00ff0000) >> 16;
+ dmm_outb(dev, DMM32AT_DIOC, diobits);
+ }
+ if ((devpriv->dio_config & DMM32AT_DIRB) == 0) {
+ diobits = (s->state & 0x0000ff00) >> 8;
+ dmm_outb(dev, DMM32AT_DIOB, diobits);
+ }
+ if ((devpriv->dio_config & DMM32AT_DIRA) == 0) {
+ diobits = (s->state & 0x000000ff);
+ dmm_outb(dev, DMM32AT_DIOA, diobits);
+ }
+
+ /* now read the state back in */
+ s->state = dmm_inb(dev, DMM32AT_DIOC);
+ s->state <<= 8;
+ s->state |= dmm_inb(dev, DMM32AT_DIOB);
+ s->state <<= 8;
+ s->state |= dmm_inb(dev, DMM32AT_DIOA);
+ data[1] = s->state;
+
+ /* on return, data[1] contains the value of the digital
+ * input and output lines. */
+ //data[1]=inw(dev->iobase + DMM32AT_DIO);
+ /* or we could just return the software copy of the output values if
+ * it was a purely digital output subdevice */
+ //data[1]=s->state;
+
+ return 2;
+}
+
+static int dmm32at_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned char chanbit;
+ int chan = CR_CHAN(insn->chanspec);
+
+ if (insn->n != 1)
+ return -EINVAL;
+
+ if (chan < 8)
+ chanbit = DMM32AT_DIRA;
+ else if (chan < 16)
+ chanbit = DMM32AT_DIRB;
+ else if (chan < 20)
+ chanbit = DMM32AT_DIRCL;
+ else
+ chanbit = DMM32AT_DIRCH;
+
+ /* The input or output configuration of each digital line is
+ * configured by a special insn_config instruction. chanspec
+ * contains the channel to be changed, and data[0] contains the
+ * value COMEDI_INPUT or COMEDI_OUTPUT. */
+
+ /* if output clear the bit, otherwise set it */
+ if (data[0] == COMEDI_OUTPUT) {
+ devpriv->dio_config &= ~chanbit;
+ } else {
+ devpriv->dio_config |= chanbit;
+ }
+ /* get access to the DIO regs */
+ dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_DIOACC);
+ /* set the DIO's to the new configuration setting */
+ dmm_outb(dev, DMM32AT_DIOCONF, devpriv->dio_config);
+
+ return 1;
+}
+
+void dmm32at_setaitimer(struct comedi_device * dev, unsigned int nansec)
+{
+ unsigned char lo1, lo2, hi2;
+ unsigned short both2;
+
+ /* based on 10mhz clock */
+ lo1 = 200;
+ both2 = nansec / 20000;
+ hi2 = (both2 & 0xff00) >> 8;
+ lo2 = both2 & 0x00ff;
+
+ /* set the counter frequency to 10mhz */
+ dmm_outb(dev, DMM32AT_CNTRDIO, 0);
+
+ /* get access to the clock regs */
+ dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_CLKACC);
+
+ /* write the counter 1 control word and low byte to counter */
+ dmm_outb(dev, DMM32AT_CLKCT, DMM32AT_CLKCT1);
+ dmm_outb(dev, DMM32AT_CLK1, lo1);
+
+ /* write the counter 2 control word and low byte then to counter */
+ dmm_outb(dev, DMM32AT_CLKCT, DMM32AT_CLKCT2);
+ dmm_outb(dev, DMM32AT_CLK2, lo2);
+ dmm_outb(dev, DMM32AT_CLK2, hi2);
+
+ /* enable the ai conversion interrupt and the clock to start scans */
+ dmm_outb(dev, DMM32AT_INTCLOCK, DMM32AT_ADINT | DMM32AT_CLKSEL);
+
+}
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_INITCLEANUP(driver_dmm32at);
diff --git a/drivers/staging/comedi/drivers/dt2801.c b/drivers/staging/comedi/drivers/dt2801.c
new file mode 100644
index 000000000000..5e0eed835d1a
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt2801.c
@@ -0,0 +1,697 @@
+/*
+ * comedi/drivers/dt2801.c
+ * Device Driver for DataTranslation DT2801
+ *
+ */
+/*
+Driver: dt2801
+Description: Data Translation DT2801 series and DT01-EZ
+Author: ds
+Status: works
+Devices: [Data Translation] DT2801 (dt2801), DT2801-A, DT2801/5716A,
+ DT2805, DT2805/5716A, DT2808, DT2818, DT2809, DT01-EZ
+
+This driver can autoprobe the type of board.
+
+Configuration options:
+ [0] - I/O port base address
+ [1] - unused
+ [2] - A/D reference 0=differential, 1=single-ended
+ [3] - A/D range
+ 0 = [-10,10]
+ 1 = [0,10]
+ [4] - D/A 0 range
+ 0 = [-10,10]
+ 1 = [-5,5]
+ 2 = [-2.5,2.5]
+ 3 = [0,10]
+ 4 = [0,5]
+ [5] - D/A 1 range (same choices)
+*/
+
+#include "../comedidev.h"
+#include <linux/delay.h>
+#include <linux/ioport.h>
+
+#define DT2801_TIMEOUT 1000
+
+/* Hardware Configuration */
+/* ====================== */
+
+#define DT2801_MAX_DMA_SIZE (64 * 1024)
+
+/* Ports */
+#define DT2801_IOSIZE 2
+
+/* define'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(struct comedi_device * dev, struct comedi_devconfig * it);
+static int dt2801_detach(struct comedi_device * dev);
+static struct comedi_driver driver_dt2801 = {
+ driver_name:"dt2801",
+ module:THIS_MODULE,
+ attach:dt2801_attach,
+ detach:dt2801_detach,
+};
+
+COMEDI_INITCLEANUP(driver_dt2801);
+
+#if 0
+// ignore 'defined but not used' warning
+static const struct comedi_lrange range_dt2801_ai_pgh_bipolar = { 4, {
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ RANGE(-2.5, 2.5),
+ RANGE(-1.25, 1.25),
+ }
+};
+#endif
+static const struct comedi_lrange range_dt2801_ai_pgl_bipolar = { 4, {
+ RANGE(-10, 10),
+ RANGE(-1, 1),
+ RANGE(-0.1, 0.1),
+ RANGE(-0.02, 0.02),
+ }
+};
+
+#if 0
+// ignore 'defined but not used' warning
+static const struct comedi_lrange range_dt2801_ai_pgh_unipolar = { 4, {
+ RANGE(0, 10),
+ RANGE(0, 5),
+ RANGE(0, 2.5),
+ RANGE(0, 1.25),
+ }
+};
+#endif
+static const struct comedi_lrange range_dt2801_ai_pgl_unipolar = { 4, {
+ RANGE(0, 10),
+ RANGE(0, 1),
+ RANGE(0, 0.1),
+ RANGE(0, 0.02),
+ }
+};
+
+struct dt2801_board {
+
+ const char *name;
+ int boardcode;
+ int ad_diff;
+ int ad_chan;
+ int adbits;
+ int adrangetype;
+ int dabits;
+};
+
+
+/* Typeid's for the different boards of the DT2801-series
+ (taken from the test-software, that comes with the board)
+ */
+static const struct dt2801_board boardtypes[] = {
+ {
+ name: "dt2801",
+ boardcode:0x09,
+ ad_diff: 2,
+ ad_chan: 16,
+ adbits: 12,
+ adrangetype:0,
+ dabits: 12},
+ {
+ name: "dt2801-a",
+ boardcode:0x52,
+ ad_diff: 2,
+ ad_chan: 16,
+ adbits: 12,
+ adrangetype:0,
+ dabits: 12},
+ {
+ name: "dt2801/5716a",
+ boardcode:0x82,
+ ad_diff: 1,
+ ad_chan: 16,
+ adbits: 16,
+ adrangetype:1,
+ dabits: 12},
+ {
+ name: "dt2805",
+ boardcode:0x12,
+ ad_diff: 1,
+ ad_chan: 16,
+ adbits: 12,
+ adrangetype:0,
+ dabits: 12},
+ {
+ name: "dt2805/5716a",
+ boardcode:0x92,
+ ad_diff: 1,
+ ad_chan: 16,
+ adbits: 16,
+ adrangetype:1,
+ dabits: 12},
+ {
+ name: "dt2808",
+ boardcode:0x20,
+ ad_diff: 0,
+ ad_chan: 16,
+ adbits: 12,
+ adrangetype:2,
+ dabits: 8},
+ {
+ name: "dt2818",
+ boardcode:0xa2,
+ ad_diff: 0,
+ ad_chan: 4,
+ adbits: 12,
+ adrangetype:0,
+ dabits: 12},
+ {
+ name: "dt2809",
+ boardcode:0xb0,
+ ad_diff: 0,
+ ad_chan: 8,
+ adbits: 12,
+ adrangetype:1,
+ dabits: 12},
+};
+
+#define n_boardtypes ((sizeof(boardtypes))/(sizeof(boardtypes[0])))
+#define boardtype (*(const struct dt2801_board *)dev->board_ptr)
+
+struct dt2801_private {
+
+ const struct comedi_lrange *dac_range_types[2];
+ unsigned int ao_readback[2];
+};
+
+#define devpriv ((struct dt2801_private *)dev->private)
+
+static int dt2801_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dt2801_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dt2801_ao_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dt2801_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dt2801_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+/* 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(struct 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 | DT_S_READY)) {
+ return stat;
+ }
+ if (stat & DT_S_DATA_OUT_READY) {
+ *data = inb_p(dev->iobase + DT2801_DATA);
+ return 0;
+ }
+ } while (--timeout > 0);
+
+ return -ETIME;
+}
+
+static int dt2801_readdata2(struct comedi_device * dev, int *data)
+{
+ int lb, hb;
+ int ret;
+
+ ret = dt2801_readdata(dev, &lb);
+ if (ret)
+ return ret;
+ ret = dt2801_readdata(dev, &hb);
+ if (ret)
+ return ret;
+
+ *data = (hb << 8) + lb;
+ return 0;
+}
+
+static int dt2801_writedata(struct 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) {
+ return stat;
+ }
+ if (!(stat & DT_S_DATA_IN_FULL)) {
+ outb_p(data & 0xff, dev->iobase + DT2801_DATA);
+ return 0;
+ }
+#if 0
+ if (stat & DT_S_READY) {
+ printk("dt2801: ready flag set (bad!) in dt2801_writedata()\n");
+ return -EIO;
+ }
+#endif
+ } while (--timeout > 0);
+
+ return -ETIME;
+}
+
+static int dt2801_writedata2(struct 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(struct comedi_device * dev)
+{
+ int timeout = DT2801_TIMEOUT;
+ int stat;
+
+ stat = inb_p(dev->iobase + DT2801_STATUS);
+ if (stat & DT_S_READY) {
+ return 0;
+ }
+ do {
+ stat = inb_p(dev->iobase + DT2801_STATUS);
+
+ if (stat & DT_S_COMPOSITE_ERROR) {
+ return stat;
+ }
+ if (stat & DT_S_READY) {
+ return 0;
+ }
+ } while (--timeout > 0);
+
+ return -ETIME;
+}
+
+static int dt2801_writecmd(struct 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(), ignoring\n");
+ }
+ 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(struct comedi_device * dev)
+{
+ int board_code = 0;
+ unsigned int stat;
+ int timeout;
+
+ DPRINTK("dt2801: resetting board...\n");
+ DPRINTK("fingerprint: 0x%02x 0x%02x\n", inb_p(dev->iobase),
+ inb_p(dev->iobase + 1));
+
+ /* pull random data from data port */
+ inb_p(dev->iobase + DT2801_DATA);
+ inb_p(dev->iobase + DT2801_DATA);
+ inb_p(dev->iobase + DT2801_DATA);
+ inb_p(dev->iobase + DT2801_DATA);
+
+ DPRINTK("dt2801: stop\n");
+ //dt2801_writecmd(dev,DT_C_STOP);
+ outb_p(DT_C_STOP, dev->iobase + DT2801_CMD);
+
+ //dt2801_wait_for_ready(dev);
+ comedi_udelay(100);
+ timeout = 10000;
+ do {
+ stat = inb_p(dev->iobase + DT2801_STATUS);
+ if (stat & DT_S_READY)
+ break;
+ } while (timeout--);
+ if (!timeout) {
+ printk("dt2801: timeout 1 status=0x%02x\n", stat);
+ }
+ //printk("dt2801: reading dummy\n");
+ //dt2801_readdata(dev,&board_code);
+
+ DPRINTK("dt2801: reset\n");
+ outb_p(DT_C_RESET, dev->iobase + DT2801_CMD);
+ //dt2801_writecmd(dev,DT_C_RESET);
+
+ comedi_udelay(100);
+ timeout = 10000;
+ do {
+ stat = inb_p(dev->iobase + DT2801_STATUS);
+ if (stat & DT_S_READY)
+ break;
+ } while (timeout--);
+ if (!timeout) {
+ printk("dt2801: timeout 2 status=0x%02x\n", stat);
+ }
+
+ DPRINTK("dt2801: reading code\n");
+ dt2801_readdata(dev, &board_code);
+
+ DPRINTK("dt2801: ok. code=0x%02x\n", board_code);
+
+ return board_code;
+}
+
+static int probe_number_of_ai_chans(struct comedi_device * dev)
+{
+ int n_chans;
+ int stat;
+ int data;
+
+ for (n_chans = 0; n_chans < 16; n_chans++) {
+ stat = dt2801_writecmd(dev, DT_C_READ_ADIM);
+ dt2801_writedata(dev, 0);
+ dt2801_writedata(dev, n_chans);
+ stat = dt2801_readdata2(dev, &data);
+
+ if (stat)
+ break;
+ }
+
+ dt2801_reset(dev);
+ dt2801_reset(dev);
+
+ return n_chans;
+}
+
+static const struct comedi_lrange *dac_range_table[] = {
+ &range_bipolar10,
+ &range_bipolar5,
+ &range_bipolar2_5,
+ &range_unipolar10,
+ &range_unipolar5
+};
+
+static const struct comedi_lrange *dac_range_lkup(int opt)
+{
+ if (opt < 0 || opt > 5)
+ return &range_unknown;
+ return dac_range_table[opt];
+}
+
+static const struct comedi_lrange *ai_range_lkup(int type, int opt)
+{
+ switch (type) {
+ case 0:
+ return (opt) ?
+ &range_dt2801_ai_pgl_unipolar :
+ &range_dt2801_ai_pgl_bipolar;
+ case 1:
+ return (opt) ? &range_unipolar10 : &range_bipolar10;
+ case 2:
+ return &range_unipolar5;
+ }
+ return &range_unknown;
+}
+
+/*
+ options:
+ [0] - i/o base
+ [1] - unused
+ [2] - a/d 0=differential, 1=single-ended
+ [3] - a/d range 0=[-10,10], 1=[0,10]
+ [4] - dac0 range 0=[-10,10], 1=[-5,5], 2=[-2.5,2.5] 3=[0,10], 4=[0,5]
+ [5] - dac1 range 0=[-10,10], 1=[-5,5], 2=[-2.5,2.5] 3=[0,10], 4=[0,5]
+*/
+static int dt2801_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+ int board_code, type;
+ int ret = 0;
+ int n_ai_chans;
+
+ iobase = it->options[0];
+ if (!request_region(iobase, DT2801_IOSIZE, "dt2801")) {
+ comedi_error(dev, "I/O port conflict");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ /* do some checking */
+
+ board_code = dt2801_reset(dev);
+
+ /* heh. if it didn't work, try it again. */
+ if (!board_code)
+ board_code = dt2801_reset(dev);
+
+ for (type = 0; type < n_boardtypes; type++) {
+ if (boardtypes[type].boardcode == board_code)
+ goto havetype;
+ }
+ printk("dt2801: unrecognized board code=0x%02x, contact author\n",
+ board_code);
+ type = 0;
+
+ havetype:
+ dev->board_ptr = boardtypes + type;
+ printk("dt2801: %s at port 0x%lx", boardtype.name, iobase);
+
+ n_ai_chans = probe_number_of_ai_chans(dev);
+ printk(" (ai channels = %d)", n_ai_chans);
+
+ if ((ret = alloc_subdevices(dev, 4)) < 0)
+ goto out;
+
+ if ((ret = alloc_private(dev, sizeof(struct dt2801_private))) < 0)
+ goto out;
+
+ dev->board_name = boardtype.name;
+
+ s = dev->subdevices + 0;
+ /* ai subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+#if 1
+ s->n_chan = n_ai_chans;
+#else
+ if (it->options[2])
+ s->n_chan = boardtype.ad_chan;
+ else
+ s->n_chan = boardtype.ad_chan / 2;
+#endif
+ s->maxdata = (1 << boardtype.adbits) - 1;
+ s->range_table = ai_range_lkup(boardtype.adrangetype, it->options[3]);
+ s->insn_read = dt2801_ai_insn_read;
+
+ s++;
+ /* ao subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = (1 << boardtype.dabits) - 1;
+ s->range_table_list = devpriv->dac_range_types;
+ devpriv->dac_range_types[0] = dac_range_lkup(it->options[4]);
+ devpriv->dac_range_types[1] = dac_range_lkup(it->options[5]);
+ s->insn_read = dt2801_ao_insn_read;
+ s->insn_write = dt2801_ao_insn_write;
+
+ s++;
+ /* 1st digital subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = dt2801_dio_insn_bits;
+ s->insn_config = dt2801_dio_insn_config;
+
+ s++;
+ /* 2nd digital subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = dt2801_dio_insn_bits;
+ s->insn_config = dt2801_dio_insn_config;
+
+ ret = 0;
+ out:
+ printk("\n");
+
+ return ret;
+}
+
+static int dt2801_detach(struct comedi_device * dev)
+{
+ if (dev->iobase)
+ release_region(dev->iobase, DT2801_IOSIZE);
+
+ return 0;
+}
+
+static int dt2801_error(struct comedi_device * dev, int stat)
+{
+ if (stat < 0) {
+ if (stat == -ETIME) {
+ printk("dt2801: timeout\n");
+ } else {
+ printk("dt2801: error %d\n", stat);
+ }
+ return stat;
+ }
+ printk("dt2801: error status 0x%02x, resetting...\n", stat);
+
+ dt2801_reset(dev);
+ dt2801_reset(dev);
+
+ return -EIO;
+}
+
+static int dt2801_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int d;
+ int stat;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ stat = dt2801_writecmd(dev, DT_C_READ_ADIM);
+ dt2801_writedata(dev, CR_RANGE(insn->chanspec));
+ dt2801_writedata(dev, CR_CHAN(insn->chanspec));
+ stat = dt2801_readdata2(dev, &d);
+
+ if (stat != 0)
+ return dt2801_error(dev, stat);
+
+ data[i] = d;
+ }
+
+ return i;
+}
+
+static int dt2801_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = devpriv->ao_readback[CR_CHAN(insn->chanspec)];
+
+ return 1;
+}
+
+static int dt2801_ao_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ dt2801_writecmd(dev, DT_C_WRITE_DAIM);
+ dt2801_writedata(dev, CR_CHAN(insn->chanspec));
+ dt2801_writedata2(dev, data[0]);
+
+ devpriv->ao_readback[CR_CHAN(insn->chanspec)] = data[0];
+
+ return 1;
+}
+
+static int dt2801_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int which = 0;
+
+ if (s == dev->subdevices + 4)
+ which = 1;
+
+ if (insn->n != 2)
+ return -EINVAL;
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ dt2801_writecmd(dev, DT_C_WRITE_DIG);
+ dt2801_writedata(dev, which);
+ dt2801_writedata(dev, s->state);
+ }
+ dt2801_writecmd(dev, DT_C_READ_DIG);
+ dt2801_writedata(dev, which);
+ dt2801_readdata(dev, data + 1);
+
+ return 2;
+}
+
+static int dt2801_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int which = 0;
+
+ if (s == dev->subdevices + 4)
+ which = 1;
+
+ /* configure */
+ if (data[0]) {
+ 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);
+
+ return 1;
+}
diff --git a/drivers/staging/comedi/drivers/dt2811.c b/drivers/staging/comedi/drivers/dt2811.c
new file mode 100644
index 000000000000..795932ec67d3
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt2811.c
@@ -0,0 +1,604 @@
+/*
+ comedi/drivers/dt2811.c
+ Hardware driver for Data Translation DT2811
+
+ COMEDI - Linux Control and Measurement Device Interface
+ History:
+ Base Version - David A. Schleef <ds@schleef.org>
+ 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.
+ */
+/*
+Driver: dt2811
+Description: Data Translation DT2811
+Author: ds
+Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh)
+Status: works
+
+Configuration options:
+ [0] - I/O port base address
+ [1] - IRQ, although this is currently unused
+ [2] - A/D reference
+ 0 = signle-ended
+ 1 = differential
+ 2 = pseudo-differential (common reference)
+ [3] - A/D range
+ 0 = [-5,5]
+ 1 = [-2.5,2.5]
+ 2 = [0,5]
+ [4] - D/A 0 range (same choices)
+ [4] - D/A 1 range (same choices)
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+static const char *driver_name = "dt2811";
+
+static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = { 4, {
+ RANGE(0, 5),
+ RANGE(0, 2.5),
+ RANGE(0, 1.25),
+ RANGE(0, 0.625)
+ }
+};
+static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = { 4, {
+ RANGE(-2.5, 2.5),
+ RANGE(-1.25, 1.25),
+ RANGE(-0.625, 0.625),
+ RANGE(-0.3125, 0.3125)
+ }
+};
+static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = { 4, {
+ RANGE(-5, 5),
+ RANGE(-2.5, 2.5),
+ RANGE(-1.25, 1.25),
+ RANGE(-0.625, 0.625)
+ }
+};
+static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = { 4, {
+ RANGE(0, 5),
+ RANGE(0, 0.5),
+ RANGE(0, 0.05),
+ RANGE(0, 0.01)
+ }
+};
+static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = { 4, {
+ RANGE(-2.5, 2.5),
+ RANGE(-0.25, 0.25),
+ RANGE(-0.025, 0.025),
+ RANGE(-0.005, 0.005)
+ }
+};
+static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = { 4, {
+ RANGE(-5, 5),
+ RANGE(-0.5, 0.5),
+ RANGE(-0.05, 0.05),
+ RANGE(-0.01, 0.01)
+ }
+};
+
+/*
+
+ 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 TIMEOUT 10000
+
+#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
+
+struct dt2811_board {
+
+ const char *name;
+ const struct comedi_lrange *bip_5;
+ const struct comedi_lrange *bip_2_5;
+ const struct comedi_lrange *unip_5;
+};
+
+static const struct dt2811_board boardtypes[] = {
+ {"dt2811-pgh",
+ &range_dt2811_pgh_ai_5_bipolar,
+ &range_dt2811_pgh_ai_2_5_bipolar,
+ &range_dt2811_pgh_ai_5_unipolar,
+ },
+ {"dt2811-pgl",
+ &range_dt2811_pgl_ai_5_bipolar,
+ &range_dt2811_pgl_ai_2_5_bipolar,
+ &range_dt2811_pgl_ai_5_unipolar,
+ },
+};
+
+#define this_board ((const struct dt2811_board *)dev->board_ptr)
+
+static int dt2811_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int dt2811_detach(struct comedi_device * dev);
+static struct comedi_driver driver_dt2811 = {
+ driver_name:"dt2811",
+ module:THIS_MODULE,
+ attach:dt2811_attach,
+ detach:dt2811_detach,
+ board_name:&boardtypes[0].name,
+ num_names:sizeof(boardtypes) / sizeof(struct dt2811_board),
+ offset:sizeof(struct dt2811_board),
+};
+
+COMEDI_INITCLEANUP(driver_dt2811);
+
+static int dt2811_ai_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dt2811_ao_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dt2811_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dt2811_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int dt2811_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+enum { card_2811_pgh, card_2811_pgl };
+
+struct dt2811_private {
+ int ntrig;
+ int curadchan;
+ enum {
+ adc_singleended, adc_diff, adc_pseudo_diff
+ } adc_mux;
+ enum {
+ dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
+ } dac_range[2];
+ const struct comedi_lrange *range_type_list[2];
+ unsigned int ao_readback[2];
+};
+
+#define devpriv ((struct dt2811_private *)dev->private)
+
+static const struct comedi_lrange *dac_range_types[] = {
+ &range_bipolar5,
+ &range_bipolar2_5,
+ &range_unipolar5
+};
+
+#define DT2811_TIMEOUT 5
+
+#if 0
+static irqreturn_t dt2811_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ int lo, hi;
+ int data;
+ struct comedi_device *dev = d;
+
+ if (!dev->attached) {
+ comedi_error(dev, "spurious interrupt");
+ return IRQ_HANDLED;
+ }
+
+ lo = inb(dev->iobase + DT2811_ADDATLO);
+ hi = inb(dev->iobase + DT2811_ADDATHI);
+
+ data = lo + (hi << 8);
+
+ if (!(--devpriv->ntrig)) {
+ /* how to turn off acquisition */
+ s->async->events |= COMEDI_SB_EOA;
+ }
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+}
+#endif
+
+/*
+ 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_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ //int i, irq;
+ //unsigned long irqs;
+ //long flags;
+ int ret;
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+
+ iobase = it->options[0];
+
+ printk("comedi%d: dt2811: base=0x%04lx\n", dev->minor, iobase);
+
+ if (!request_region(iobase, DT2811_SIZE, driver_name)) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+
+ dev->iobase = iobase;
+ dev->board_name = this_board->name;
+
+#if 0
+ outb(0, dev->iobase + DT2811_ADCSR);
+ comedi_udelay(100);
+ i = inb(dev->iobase + DT2811_ADDATLO);
+ i = inb(dev->iobase + DT2811_ADDATHI);
+#endif
+
+#if 0
+ 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);
+
+ comedi_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);
+ ret = comedi_request_irq(irq, dt2811_interrupt, 0,
+ driver_name, dev);
+ if (ret < 0)
+ return -EIO;
+ dev->irq = irq;
+ } else if (irq == 0) {
+ printk("(no irq)\n");
+ } else {
+ printk("( multiple irq's -- this is bad! )\n");
+ }
+ }
+#endif
+
+ if ((ret = alloc_subdevices(dev, 4)) < 0)
+ return ret;
+ if ((ret = alloc_private(dev, sizeof(struct 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[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 | SDF_GROUND;
+ s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
+ s->insn_read = dt2811_ai_insn;
+ s->maxdata = 0xfff;
+ switch (it->options[3]) {
+ case 0:
+ default:
+ s->range_table = this_board->bip_5;
+ break;
+ case 1:
+ s->range_table = this_board->bip_2_5;
+ break;
+ case 2:
+ s->range_table = this_board->unip_5;
+ break;
+ }
+
+ s = dev->subdevices + 1;
+ /* ao subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->insn_write = dt2811_ao_insn;
+ s->insn_read = dt2811_ao_insn_read;
+ s->maxdata = 0xfff;
+ s->range_table_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->insn_bits = dt2811_di_insn_bits;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+
+ s = dev->subdevices + 3;
+ /* do subdevice */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 8;
+ s->insn_bits = dt2811_do_insn_bits;
+ s->maxdata = 1;
+ s->state = 0;
+ s->range_table = &range_digital;
+
+ return 0;
+}
+
+static int dt2811_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: dt2811: remove\n", dev->minor);
+
+ if (dev->irq) {
+ comedi_free_irq(dev->irq, dev);
+ }
+ if (dev->iobase) {
+ release_region(dev->iobase, DT2811_SIZE);
+ }
+
+ return 0;
+}
+
+static int dt2811_ai_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+ int timeout = DT2811_TIMEOUT;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ outb(chan, dev->iobase + DT2811_ADGCR);
+
+ while (timeout
+ && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
+ timeout--;
+ if (!timeout)
+ return -ETIME;
+
+ data[i] = inb(dev->iobase + DT2811_ADDATLO);
+ data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
+ data[i] &= 0xfff;
+ }
+
+ return i;
+}
+
+#if 0
+/* Wow. This is code from the Comedi stone age. But it hasn't been
+ * replaced, so I'll let it stay. */
+int dt2811_adtrig(kdev_t minor, comedi_adtrig * adtrig)
+{
+ struct 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_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan;
+
+ chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++) {
+ outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
+ outb((data[i] >> 8) & 0xff,
+ dev->iobase + DT2811_DADAT0HI + 2 * chan);
+ devpriv->ao_readback[chan] = data[i];
+ }
+
+ return i;
+}
+
+static int dt2811_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan;
+
+ chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++) {
+ data[i] = devpriv->ao_readback[chan];
+ }
+
+ return i;
+}
+
+static int dt2811_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ data[1] = inb(dev->iobase + DT2811_DIO);
+
+ return 2;
+}
+
+static int dt2811_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+ outb(s->state, dev->iobase + DT2811_DIO);
+
+ data[1] = s->state;
+
+ return 2;
+}
diff --git a/drivers/staging/comedi/drivers/dt2814.c b/drivers/staging/comedi/drivers/dt2814.c
new file mode 100644
index 000000000000..8320139160a4
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt2814.c
@@ -0,0 +1,383 @@
+/*
+ comedi/drivers/dt2814.c
+ Hardware driver for Data Translation DT2814
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: dt2814
+Description: Data Translation DT2814
+Author: ds
+Status: complete
+Devices: [Data Translation] DT2814 (dt2814)
+
+Configuration options:
+ [0] - I/O port base address
+ [1] - IRQ
+
+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.
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+
+#define DT2814_SIZE 2
+
+#define DT2814_CSR 0
+#define DT2814_DATA 1
+
+/*
+ * flags
+ */
+
+#define DT2814_FINISH 0x80
+#define DT2814_ERR 0x40
+#define DT2814_BUSY 0x20
+#define DT2814_ENB 0x10
+#define DT2814_CHANMASK 0x0f
+
+static int dt2814_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int dt2814_detach(struct comedi_device * dev);
+static struct comedi_driver driver_dt2814 = {
+ driver_name:"dt2814",
+ module:THIS_MODULE,
+ attach:dt2814_attach,
+ detach:dt2814_detach,
+};
+
+COMEDI_INITCLEANUP(driver_dt2814);
+
+static irqreturn_t dt2814_interrupt(int irq, void *dev PT_REGS_ARG);
+
+struct dt2814_private {
+
+ int ntrig;
+ int curadchan;
+};
+
+#define devpriv ((struct dt2814_private *)dev->private)
+
+#define DT2814_TIMEOUT 10
+#define DT2814_MAX_SPEED 100000 /* Arbitrary 10 khz limit */
+
+static int dt2814_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, i, hi, lo;
+ int chan;
+ int status = 0;
+
+ for (n = 0; n < insn->n; n++) {
+ chan = CR_CHAN(insn->chanspec);
+
+ outb(chan, dev->iobase + DT2814_CSR);
+ for (i = 0; i < DT2814_TIMEOUT; i++) {
+ status = inb(dev->iobase + DT2814_CSR);
+ printk("dt2814: status: %02x\n", status);
+ comedi_udelay(10);
+ if (status & DT2814_FINISH)
+ break;
+ }
+ if (i >= DT2814_TIMEOUT) {
+ printk("dt2814: status: %02x\n", status);
+ return -ETIMEDOUT;
+ }
+
+ hi = inb(dev->iobase + DT2814_DATA);
+ lo = inb(dev->iobase + DT2814_DATA);
+
+ data[n] = (hi << 4) | (lo >> 4);
+ }
+
+ return n;
+}
+
+static int dt2814_ns_to_timer(unsigned int *ns, unsigned int flags)
+{
+ int i;
+ unsigned int f;
+
+ /* XXX ignores flags */
+
+ f = 10000; /* ns */
+ for (i = 0; i < 8; i++) {
+ if ((2 * (*ns)) < (f * 11))
+ break;
+ f *= 10;
+ }
+
+ *ns = f;
+
+ return i;
+}
+
+static int dt2814_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ /* note that mutual compatiblity is not an issue here */
+ if (cmd->stop_src != TRIG_TIMER && cmd->stop_src != TRIG_EXT)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ if (cmd->scan_begin_arg > 1000000000) {
+ cmd->scan_begin_arg = 1000000000;
+ err++;
+ }
+ if (cmd->scan_begin_arg < DT2814_MAX_SPEED) {
+ cmd->scan_begin_arg = DT2814_MAX_SPEED;
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (cmd->stop_arg < 2) {
+ cmd->stop_arg = 2;
+ err++;
+ }
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ tmp = cmd->scan_begin_arg;
+ dt2814_ns_to_timer(&cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int dt2814_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int chan;
+ int trigvar;
+
+ trigvar =
+ dt2814_ns_to_timer(&cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+
+ chan = CR_CHAN(cmd->chanlist[0]);
+
+ devpriv->ntrig = cmd->stop_arg;
+ outb(chan | DT2814_ENB | (trigvar << 5), dev->iobase + DT2814_CSR);
+
+ return 0;
+
+}
+
+static int dt2814_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int i, irq;
+ int ret;
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+
+ iobase = it->options[0];
+ printk("comedi%d: dt2814: 0x%04lx ", dev->minor, iobase);
+ if (!request_region(iobase, DT2814_SIZE, "dt2814")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+ dev->board_name = "dt2814";
+
+ outb(0, dev->iobase + DT2814_CSR);
+ comedi_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);
+
+ comedi_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) {
+ if (comedi_request_irq(irq, dt2814_interrupt, 0, "dt2814", dev)) {
+ printk("(irq %d unavailable)\n", irq);
+ } else {
+ printk("( irq = %d )\n", irq);
+ dev->irq = irq;
+ }
+ } else if (irq == 0) {
+ printk("(no irq)\n");
+ } else {
+#if 0
+ printk("(probe returned multiple irqs--bad)\n");
+#else
+ printk("(irq probe not implemented)\n");
+#endif
+ }
+
+ if ((ret = alloc_subdevices(dev, 1)) < 0)
+ return ret;
+ if ((ret = alloc_private(dev, sizeof(struct dt2814_private))) < 0)
+ return ret;
+
+ s = dev->subdevices + 0;
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
+ s->n_chan = 16; /* XXX */
+ s->len_chanlist = 1;
+ s->insn_read = dt2814_ai_insn_read;
+ s->do_cmd = dt2814_ai_cmd;
+ s->do_cmdtest = dt2814_ai_cmdtest;
+ s->maxdata = 0xfff;
+ s->range_table = &range_unknown; /* XXX */
+
+ return 0;
+}
+
+static int dt2814_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: dt2814: remove\n", dev->minor);
+
+ if (dev->irq) {
+ comedi_free_irq(dev->irq, dev);
+ }
+ if (dev->iobase) {
+ release_region(dev->iobase, DT2814_SIZE);
+ }
+
+ return 0;
+}
+
+static irqreturn_t dt2814_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ int lo, hi;
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s;
+ int data;
+
+ if (!dev->attached) {
+ comedi_error(dev, "spurious interrupt");
+ return IRQ_HANDLED;
+ }
+
+ s = dev->subdevices + 0;
+
+ hi = inb(dev->iobase + DT2814_DATA);
+ lo = inb(dev->iobase + DT2814_DATA);
+
+ data = (hi << 4) | (lo >> 4);
+
+ if (!(--devpriv->ntrig)) {
+ int i;
+
+ outb(0, dev->iobase + DT2814_CSR);
+ /* note: turning off timed mode triggers another
+ sample. */
+
+ for (i = 0; i < DT2814_TIMEOUT; i++) {
+ if (inb(dev->iobase + DT2814_CSR) & DT2814_FINISH)
+ break;
+ }
+ inb(dev->iobase + DT2814_DATA);
+ inb(dev->iobase + DT2814_DATA);
+
+ s->async->events |= COMEDI_CB_EOA;
+ }
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+}
diff --git a/drivers/staging/comedi/drivers/dt2815.c b/drivers/staging/comedi/drivers/dt2815.c
new file mode 100644
index 000000000000..fccec8a77d50
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt2815.c
@@ -0,0 +1,264 @@
+/*
+ comedi/drivers/dt2815.c
+ Hardware driver for Data Translation DT2815
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+/*
+Driver: dt2815
+Description: Data Translation DT2815
+Author: ds
+Status: mostly complete, untested
+Devices: [Data Translation] DT2815 (dt2815)
+
+I'm not sure anyone has ever tested this board. If you have information
+contrary, please update.
+
+Configuration options:
+ [0] - I/O port base base address
+ [1] - IRQ (unused)
+ [2] - Voltage unipolar/bipolar configuration
+ 0 == unipolar 5V (0V -- +5V)
+ 1 == bipolar 5V (-5V -- +5V)
+ [3] - Current offset configuration
+ 0 == disabled (0mA -- +32mAV)
+ 1 == enabled (+4mA -- +20mAV)
+ [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)
+ [5] - Analog output 0 range configuration
+ 0 == voltage
+ 1 == current
+ [6] - Analog output 1 range configuration (same options)
+ [7] - Analog output 2 range configuration (same options)
+ [8] - Analog output 3 range configuration (same options)
+ [9] - Analog output 4 range configuration (same options)
+ [10] - Analog output 5 range configuration (same options)
+ [11] - Analog output 6 range configuration (same options)
+ [12] - Analog output 7 range configuration (same options)
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+
+static const struct comedi_lrange range_dt2815_ao_32_current = { 1, {
+ RANGE_mA(0, 32)
+ }
+};
+static const struct comedi_lrange range_dt2815_ao_20_current = { 1, {
+ RANGE_mA(4, 20)
+ }
+};
+
+#define DT2815_SIZE 2
+
+#define DT2815_DATA 0
+#define DT2815_STATUS 1
+
+static int dt2815_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int dt2815_detach(struct comedi_device * dev);
+static struct comedi_driver driver_dt2815 = {
+ driver_name:"dt2815",
+ module:THIS_MODULE,
+ attach:dt2815_attach,
+ detach:dt2815_detach,
+};
+
+COMEDI_INITCLEANUP(driver_dt2815);
+
+static void dt2815_free_resources(struct comedi_device * dev);
+
+struct dt2815_private {
+
+ const struct comedi_lrange *range_type_list[8];
+ unsigned int ao_readback[8];
+};
+
+
+#define devpriv ((struct dt2815_private *)dev->private)
+
+static int dt2815_wait_for_status(struct comedi_device * dev, int status)
+{
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ if (inb(dev->iobase + DT2815_STATUS) == status)
+ break;
+ }
+ return status;
+}
+
+static int dt2815_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++) {
+ data[i] = devpriv->ao_readback[chan];
+ }
+
+ return i;
+}
+
+static int dt2815_ao_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+ unsigned int status;
+ unsigned int lo, hi;
+
+ for (i = 0; i < insn->n; i++) {
+ lo = ((data[i] & 0x0f) << 4) | (chan << 1) | 0x01;
+ hi = (data[i] & 0xff0) >> 4;
+
+ status = dt2815_wait_for_status(dev, 0x00);
+ if (status != 0) {
+ rt_printk
+ ("dt2815: failed to write low byte on %d reason %x\n",
+ chan, status);
+ return -EBUSY;
+ }
+
+ outb(lo, dev->iobase + DT2815_DATA);
+
+ status = dt2815_wait_for_status(dev, 0x10);
+ if (status != 0x10) {
+ rt_printk
+ ("dt2815: failed to write high byte on %d reason %x\n",
+ chan, status);
+ return -EBUSY;
+ }
+ devpriv->ao_readback[chan] = data[i];
+ }
+ 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(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int i;
+ const struct comedi_lrange *current_range_type, *voltage_range_type;
+ unsigned long iobase;
+
+ iobase = it->options[0];
+ printk("comedi%d: dt2815: 0x%04lx ", dev->minor, iobase);
+ if (!request_region(iobase, DT2815_SIZE, "dt2815")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+
+ dev->iobase = iobase;
+ dev->board_name = "dt2815";
+
+ if (alloc_subdevices(dev, 1) < 0)
+ return -ENOMEM;
+ if (alloc_private(dev, sizeof(struct dt2815_private)) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices;
+ /* ao subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->maxdata = 0xfff;
+ s->n_chan = 8;
+ s->insn_write = dt2815_ao_insn;
+ s->insn_read = dt2815_ao_insn_read;
+ s->range_table_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_bipolar5 : &range_unipolar5;
+ 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;
+
+ comedi_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(struct comedi_device * dev)
+{
+ if (dev->iobase)
+ release_region(dev->iobase, DT2815_SIZE);
+}
+
+static int dt2815_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: dt2815: remove\n", dev->minor);
+
+ dt2815_free_resources(dev);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/dt2817.c b/drivers/staging/comedi/drivers/dt2817.c
new file mode 100644
index 000000000000..2dc396a44e6f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt2817.c
@@ -0,0 +1,178 @@
+/*
+ comedi/drivers/dt2817.c
+ Hardware driver for Data Translation DT2817
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: dt2817
+Description: Data Translation DT2817
+Author: ds
+Status: complete
+Devices: [Data Translation] DT2817 (dt2817)
+
+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.
+
+Configuration options:
+ [0] - I/O port base base address
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+#define DT2817_SIZE 5
+
+#define DT2817_CR 0
+#define DT2817_DATA 1
+
+static int dt2817_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int dt2817_detach(struct comedi_device * dev);
+static struct comedi_driver driver_dt2817 = {
+ driver_name:"dt2817",
+ module:THIS_MODULE,
+ attach:dt2817_attach,
+ detach:dt2817_detach,
+};
+
+COMEDI_INITCLEANUP(driver_dt2817);
+
+static int dt2817_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int mask;
+ int chan;
+ int oe = 0;
+
+ if (insn->n != 1)
+ return -EINVAL;
+
+ chan = CR_CHAN(insn->chanspec);
+ if (chan < 8) {
+ mask = 0xff;
+ } else if (chan < 16) {
+ mask = 0xff00;
+ } else if (chan < 24) {
+ mask = 0xff0000;
+ } else
+ mask = 0xff000000;
+ if (data[0])
+ 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);
+
+ return 1;
+}
+
+static int dt2817_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int changed;
+
+ /* It's questionable whether it is more important in
+ * a driver like this to be deterministic or fast.
+ * We choose fast. */
+
+ if (data[0]) {
+ changed = s->state;
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ changed ^= s->state;
+ changed &= s->io_bits;
+ if (changed & 0x000000ff)
+ outb(s->state & 0xff, dev->iobase + DT2817_DATA + 0);
+ if (changed & 0x0000ff00)
+ outb((s->state >> 8) & 0xff,
+ dev->iobase + DT2817_DATA + 1);
+ if (changed & 0x00ff0000)
+ outb((s->state >> 16) & 0xff,
+ dev->iobase + DT2817_DATA + 2);
+ if (changed & 0xff000000)
+ outb((s->state >> 24) & 0xff,
+ dev->iobase + DT2817_DATA + 3);
+ }
+ data[1] = inb(dev->iobase + DT2817_DATA + 0);
+ data[1] |= (inb(dev->iobase + DT2817_DATA + 1) << 8);
+ data[1] |= (inb(dev->iobase + DT2817_DATA + 2) << 16);
+ data[1] |= (inb(dev->iobase + DT2817_DATA + 3) << 24);
+
+ return 2;
+}
+
+static int dt2817_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int ret;
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+
+ iobase = it->options[0];
+ printk("comedi%d: dt2817: 0x%04lx ", dev->minor, iobase);
+ if (!request_region(iobase, DT2817_SIZE, "dt2817")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+ dev->board_name = "dt2817";
+
+ if ((ret = alloc_subdevices(dev, 1)) < 0)
+ return ret;
+
+ s = dev->subdevices + 0;
+
+ s->n_chan = 32;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+ s->insn_bits = dt2817_dio_insn_bits;
+ s->insn_config = dt2817_dio_insn_config;
+
+ s->state = 0;
+ outb(0, dev->iobase + DT2817_CR);
+
+ printk("\n");
+
+ return 0;
+}
+
+static int dt2817_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: dt2817: remove\n", dev->minor);
+
+ if (dev->iobase)
+ release_region(dev->iobase, DT2817_SIZE);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/dt282x.c b/drivers/staging/comedi/drivers/dt282x.c
new file mode 100644
index 000000000000..4882c3e679cc
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt282x.c
@@ -0,0 +1,1471 @@
+/*
+ comedi/drivers/dt282x.c
+ Hardware driver for Data Translation DT2821 series
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+/*
+Driver: dt282x
+Description: Data Translation DT2821 series (including DT-EZ)
+Author: ds
+Devices: [Data Translation] DT2821 (dt2821),
+ DT2821-F-16SE (dt2821-f), DT2821-F-8DI (dt2821-f),
+ DT2821-G-16SE (dt2821-f), DT2821-G-8DI (dt2821-g),
+ DT2823 (dt2823),
+ DT2824-PGH (dt2824-pgh), DT2824-PGL (dt2824-pgl), DT2825 (dt2825),
+ DT2827 (dt2827), DT2828 (dt2828), DT21-EZ (dt21-ez), DT23-EZ (dt23-ez),
+ DT24-EZ (dt24-ez), DT24-EZ-PGL (dt24-ez-pgl)
+Status: complete
+Updated: Wed, 22 Aug 2001 17:11:34 -0700
+
+Configuration options:
+ [0] - I/O port base address
+ [1] - IRQ
+ [2] - DMA 1
+ [3] - DMA 2
+ [4] - AI jumpered for 0=single ended, 1=differential
+ [5] - AI jumpered for 0=straight binary, 1=2's complement
+ [6] - AO 0 jumpered for 0=straight binary, 1=2's complement
+ [7] - AO 1 jumpered for 0=straight binary, 1=2's complement
+ [8] - AI jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5]
+ [9] - AO 0 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5],
+ 4=[-2.5,2.5]
+ [10]- A0 1 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5],
+ 4=[-2.5,2.5]
+
+Notes:
+ - AO commands might be broken.
+ - If you try to run a command on both the AI and AO subdevices
+ simultaneously, bad things will happen. The driver needs to
+ be fixed to check for this situation and return an error.
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <asm/dma.h>
+#include "comedi_fc.h"
+
+#define DEBUG
+
+#define DT2821_TIMEOUT 100 /* 500 us */
+#define DT2821_SIZE 0x10
+
+/*
+ * Registers in the DT282x
+ */
+
+#define DT2821_ADCSR 0x00 /* A/D Control/Status */
+#define DT2821_CHANCSR 0x02 /* Channel Control/Status */
+#define DT2821_ADDAT 0x04 /* A/D data */
+#define DT2821_DACSR 0x06 /* D/A Control/Status */
+#define DT2821_DADAT 0x08 /* D/A data */
+#define DT2821_DIODAT 0x0a /* digital data */
+#define DT2821_SUPCSR 0x0c /* Supervisor Control/Status */
+#define DT2821_TMRCTR 0x0e /* Timer/Counter */
+
+/*
+ * At power up, some registers are in a well-known state. The
+ * masks and values are as follows:
+ */
+
+#define DT2821_ADCSR_MASK 0xfff0
+#define DT2821_ADCSR_VAL 0x7c00
+
+#define DT2821_CHANCSR_MASK 0xf0f0
+#define DT2821_CHANCSR_VAL 0x70f0
+
+#define DT2821_DACSR_MASK 0x7c93
+#define DT2821_DACSR_VAL 0x7c90
+
+#define DT2821_SUPCSR_MASK 0xf8ff
+#define DT2821_SUPCSR_VAL 0x0000
+
+#define DT2821_TMRCTR_MASK 0xff00
+#define DT2821_TMRCTR_VAL 0xf000
+
+/*
+ * Bit fields of each register
+ */
+
+/* ADCSR */
+
+#define DT2821_ADERR 0x8000 /* (R) 1 for A/D error */
+#define DT2821_ADCLK 0x0200 /* (R/W) A/D clock enable */
+ /* 0x7c00 read as 1's */
+#define DT2821_MUXBUSY 0x0100 /* (R) multiplexer busy */
+#define DT2821_ADDONE 0x0080 /* (R) A/D done */
+#define DT2821_IADDONE 0x0040 /* (R/W) interrupt on A/D done */
+ /* 0x0030 gain select */
+ /* 0x000f channel select */
+
+/* CHANCSR */
+
+#define DT2821_LLE 0x8000 /* (R/W) Load List Enable */
+ /* 0x7000 read as 1's */
+ /* 0x0f00 (R) present address */
+ /* 0x00f0 read as 1's */
+ /* 0x000f (R) number of entries - 1 */
+
+/* DACSR */
+
+#define DT2821_DAERR 0x8000 /* (R) D/A error */
+#define DT2821_YSEL 0x0200 /* (R/W) DAC 1 select */
+#define DT2821_SSEL 0x0100 /* (R/W) single channel select */
+#define DT2821_DACRDY 0x0080 /* (R) DAC ready */
+#define DT2821_IDARDY 0x0040 /* (R/W) interrupt on DAC ready */
+#define DT2821_DACLK 0x0020 /* (R/W) D/A clock enable */
+#define DT2821_HBOE 0x0002 /* (R/W) DIO high byte output enable */
+#define DT2821_LBOE 0x0001 /* (R/W) DIO low byte output enable */
+
+/* SUPCSR */
+
+#define DT2821_DMAD 0x8000 /* (R) DMA done */
+#define DT2821_ERRINTEN 0x4000 /* (R/W) interrupt on error */
+#define DT2821_CLRDMADNE 0x2000 /* (W) clear DMA done */
+#define DT2821_DDMA 0x1000 /* (R/W) dual DMA */
+#define DT2821_DS1 0x0800 /* (R/W) DMA select 1 */
+#define DT2821_DS0 0x0400 /* (R/W) DMA select 0 */
+#define DT2821_BUFFB 0x0200 /* (R/W) buffer B selected */
+#define DT2821_SCDN 0x0100 /* (R) scan done */
+#define DT2821_DACON 0x0080 /* (W) DAC single conversion */
+#define DT2821_ADCINIT 0x0040 /* (W) A/D initialize */
+#define DT2821_DACINIT 0x0020 /* (W) D/A initialize */
+#define DT2821_PRLD 0x0010 /* (W) preload multiplexer */
+#define DT2821_STRIG 0x0008 /* (W) software trigger */
+#define DT2821_XTRIG 0x0004 /* (R/W) external trigger enable */
+#define DT2821_XCLK 0x0002 /* (R/W) external clock enable */
+#define DT2821_BDINIT 0x0001 /* (W) initialize board */
+
+static const struct comedi_lrange range_dt282x_ai_lo_bipolar = { 4, {
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ RANGE(-2.5, 2.5),
+ RANGE(-1.25, 1.25)
+ }
+};
+static const struct comedi_lrange range_dt282x_ai_lo_unipolar = { 4, {
+ RANGE(0, 10),
+ RANGE(0, 5),
+ RANGE(0, 2.5),
+ RANGE(0, 1.25)
+ }
+};
+static const struct comedi_lrange range_dt282x_ai_5_bipolar = { 4, {
+ RANGE(-5, 5),
+ RANGE(-2.5, 2.5),
+ RANGE(-1.25, 1.25),
+ RANGE(-0.625, 0.625),
+ }
+};
+static const struct comedi_lrange range_dt282x_ai_5_unipolar = { 4, {
+ RANGE(0, 5),
+ RANGE(0, 2.5),
+ RANGE(0, 1.25),
+ RANGE(0, 0.625),
+ }
+};
+static const struct comedi_lrange range_dt282x_ai_hi_bipolar = { 4, {
+ RANGE(-10, 10),
+ RANGE(-1, 1),
+ RANGE(-0.1, 0.1),
+ RANGE(-0.02, 0.02)
+ }
+};
+static const struct comedi_lrange range_dt282x_ai_hi_unipolar = { 4, {
+ RANGE(0, 10),
+ RANGE(0, 1),
+ RANGE(0, 0.1),
+ RANGE(0, 0.02)
+ }
+};
+
+struct dt282x_board {
+ const char *name;
+ int adbits;
+ int adchan_se;
+ int adchan_di;
+ int ai_speed;
+ int ispgl;
+ int dachan;
+ int dabits;
+};
+
+static const struct dt282x_board boardtypes[] = {
+ {name:"dt2821",
+ adbits: 12,
+ adchan_se:16,
+ adchan_di:8,
+ ai_speed:20000,
+ ispgl: 0,
+ dachan: 2,
+ dabits: 12,
+ },
+ {name:"dt2821-f",
+ adbits: 12,
+ adchan_se:16,
+ adchan_di:8,
+ ai_speed:6500,
+ ispgl: 0,
+ dachan: 2,
+ dabits: 12,
+ },
+ {name:"dt2821-g",
+ adbits: 12,
+ adchan_se:16,
+ adchan_di:8,
+ ai_speed:4000,
+ ispgl: 0,
+ dachan: 2,
+ dabits: 12,
+ },
+ {name:"dt2823",
+ adbits: 16,
+ adchan_se:0,
+ adchan_di:4,
+ ai_speed:10000,
+ ispgl: 0,
+ dachan: 2,
+ dabits: 16,
+ },
+ {name:"dt2824-pgh",
+ adbits: 12,
+ adchan_se:16,
+ adchan_di:8,
+ ai_speed:20000,
+ ispgl: 0,
+ dachan: 0,
+ dabits: 0,
+ },
+ {name:"dt2824-pgl",
+ adbits: 12,
+ adchan_se:16,
+ adchan_di:8,
+ ai_speed:20000,
+ ispgl: 1,
+ dachan: 0,
+ dabits: 0,
+ },
+ {name:"dt2825",
+ adbits: 12,
+ adchan_se:16,
+ adchan_di:8,
+ ai_speed:20000,
+ ispgl: 1,
+ dachan: 2,
+ dabits: 12,
+ },
+ {name:"dt2827",
+ adbits: 16,
+ adchan_se:0,
+ adchan_di:4,
+ ai_speed:10000,
+ ispgl: 0,
+ dachan: 2,
+ dabits: 12,
+ },
+ {name:"dt2828",
+ adbits: 12,
+ adchan_se:4,
+ adchan_di:0,
+ ai_speed:10000,
+ ispgl: 0,
+ dachan: 2,
+ dabits: 12,
+ },
+ {name:"dt2829",
+ adbits: 16,
+ adchan_se:8,
+ adchan_di:0,
+ ai_speed:33250,
+ ispgl: 0,
+ dachan: 2,
+ dabits: 16,
+ },
+ {name:"dt21-ez",
+ adbits: 12,
+ adchan_se:16,
+ adchan_di:8,
+ ai_speed:10000,
+ ispgl: 0,
+ dachan: 2,
+ dabits: 12,
+ },
+ {name:"dt23-ez",
+ adbits: 16,
+ adchan_se:16,
+ adchan_di:8,
+ ai_speed:10000,
+ ispgl: 0,
+ dachan: 0,
+ dabits: 0,
+ },
+ {name:"dt24-ez",
+ adbits: 12,
+ adchan_se:16,
+ adchan_di:8,
+ ai_speed:10000,
+ ispgl: 0,
+ dachan: 0,
+ dabits: 0,
+ },
+ {name:"dt24-ez-pgl",
+ adbits: 12,
+ adchan_se:16,
+ adchan_di:8,
+ ai_speed:10000,
+ ispgl: 1,
+ dachan: 0,
+ dabits: 0,
+ },
+};
+
+#define n_boardtypes sizeof(boardtypes)/sizeof(struct dt282x_board)
+#define this_board ((const struct dt282x_board *)dev->board_ptr)
+
+struct dt282x_private {
+ int ad_2scomp; /* we have 2's comp jumper set */
+ int da0_2scomp; /* same, for DAC0 */
+ int da1_2scomp; /* same, for DAC1 */
+
+ const struct comedi_lrange *darangelist[2];
+
+ short ao[2];
+
+ volatile int dacsr; /* software copies of registers */
+ volatile int adcsr;
+ volatile int supcsr;
+
+ volatile int ntrig;
+ volatile int nread;
+
+ struct {
+ int chan;
+ short *buf; /* DMA buffer */
+ volatile int size; /* size of current transfer */
+ } dma[2];
+ int dma_maxsize; /* max size of DMA transfer (in bytes) */
+ int usedma; /* driver uses DMA */
+ volatile int current_dma_index;
+ int dma_dir;
+};
+
+#define devpriv ((struct dt282x_private *)dev->private)
+#define boardtype (*(const struct dt282x_board *)dev->board_ptr)
+
+/*
+ * Some useless abstractions
+ */
+#define chan_to_DAC(a) ((a)&1)
+#define update_dacsr(a) outw(devpriv->dacsr|(a),dev->iobase+DT2821_DACSR)
+#define update_adcsr(a) outw(devpriv->adcsr|(a),dev->iobase+DT2821_ADCSR)
+#define mux_busy() (inw(dev->iobase+DT2821_ADCSR)&DT2821_MUXBUSY)
+#define ad_done() (inw(dev->iobase+DT2821_ADCSR)&DT2821_ADDONE)
+#define update_supcsr(a) outw(devpriv->supcsr|(a),dev->iobase+DT2821_SUPCSR)
+
+/*
+ * danger! macro abuse... a is the expression to wait on, and b is
+ * the statement(s) to execute if it doesn't happen.
+ */
+#define wait_for(a,b) \
+ do{ \
+ int _i; \
+ for(_i=0;_i<DT2821_TIMEOUT;_i++){ \
+ if(a){_i=0;break;} \
+ comedi_udelay(5); \
+ } \
+ if(_i){b} \
+ }while(0)
+
+static int dt282x_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int dt282x_detach(struct comedi_device * dev);
+static struct comedi_driver driver_dt282x = {
+ driver_name:"dt282x",
+ module:THIS_MODULE,
+ attach:dt282x_attach,
+ detach:dt282x_detach,
+ board_name:&boardtypes[0].name,
+ num_names:n_boardtypes,
+ offset:sizeof(struct dt282x_board),
+};
+
+COMEDI_INITCLEANUP(driver_dt282x);
+
+static void free_resources(struct comedi_device * dev);
+static int prep_ai_dma(struct comedi_device * dev, int chan, int size);
+static int prep_ao_dma(struct comedi_device * dev, int chan, int size);
+static int dt282x_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static int dt282x_ao_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static int dt282x_ns_to_timer(int *nanosec, int round_mode);
+static void dt282x_disable_dma(struct comedi_device * dev);
+
+static int dt282x_grab_dma(struct comedi_device * dev, int dma1, int dma2);
+
+static void dt282x_munge(struct comedi_device * dev, short * buf,
+ unsigned int nbytes)
+{
+ unsigned int i;
+ unsigned short mask = (1 << boardtype.adbits) - 1;
+ unsigned short sign = 1 << (boardtype.adbits - 1);
+ int n;
+
+ if (devpriv->ad_2scomp) {
+ sign = 1 << (boardtype.adbits - 1);
+ } else {
+ sign = 0;
+ }
+
+ if (nbytes % 2)
+ comedi_error(dev, "bug! odd number of bytes from dma xfer");
+ n = nbytes / 2;
+ for (i = 0; i < n; i++) {
+ buf[i] = (buf[i] & mask) ^ sign;
+ }
+}
+
+static void dt282x_ao_dma_interrupt(struct comedi_device * dev)
+{
+ void *ptr;
+ int size;
+ int i;
+ struct comedi_subdevice *s = dev->subdevices + 1;
+
+ update_supcsr(DT2821_CLRDMADNE);
+
+ if (!s->async->prealloc_buf) {
+ printk("async->data disappeared. dang!\n");
+ return;
+ }
+
+ i = devpriv->current_dma_index;
+ ptr = devpriv->dma[i].buf;
+
+ disable_dma(devpriv->dma[i].chan);
+
+ devpriv->current_dma_index = 1 - i;
+
+ size = cfc_read_array_from_buffer(s, ptr, devpriv->dma_maxsize);
+ if (size == 0) {
+ rt_printk("dt282x: AO underrun\n");
+ dt282x_ao_cancel(dev, s);
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ return;
+ }
+ prep_ao_dma(dev, i, size);
+ return;
+}
+
+static void dt282x_ai_dma_interrupt(struct comedi_device * dev)
+{
+ void *ptr;
+ int size;
+ int i;
+ int ret;
+ struct comedi_subdevice *s = dev->subdevices;
+
+ update_supcsr(DT2821_CLRDMADNE);
+
+ if (!s->async->prealloc_buf) {
+ printk("async->data disappeared. dang!\n");
+ return;
+ }
+
+ i = devpriv->current_dma_index;
+ ptr = devpriv->dma[i].buf;
+ size = devpriv->dma[i].size;
+
+ disable_dma(devpriv->dma[i].chan);
+
+ devpriv->current_dma_index = 1 - i;
+
+ dt282x_munge(dev, ptr, size);
+ ret = cfc_write_array_to_buffer(s, ptr, size);
+ if (ret != size) {
+ dt282x_ai_cancel(dev, s);
+ return;
+ }
+ devpriv->nread -= size / 2;
+
+ if (devpriv->nread < 0) {
+ printk("dt282x: off by one\n");
+ devpriv->nread = 0;
+ }
+ if (!devpriv->nread) {
+ dt282x_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ return;
+ }
+#if 0
+ /* 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);
+}
+
+static int prep_ai_dma(struct comedi_device * dev, int dma_index, 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 * 2)
+ n = devpriv->ntrig * 2;
+ devpriv->ntrig -= n / 2;
+
+ devpriv->dma[dma_index].size = n;
+ dma_chan = devpriv->dma[dma_index].chan;
+ dma_ptr = virt_to_bus(devpriv->dma[dma_index].buf);
+
+ set_dma_mode(dma_chan, DMA_MODE_READ);
+ flags = claim_dma_lock();
+ clear_dma_ff(dma_chan);
+ set_dma_addr(dma_chan, dma_ptr);
+ set_dma_count(dma_chan, n);
+ release_dma_lock(flags);
+
+ enable_dma(dma_chan);
+
+ return n;
+}
+
+static int prep_ao_dma(struct comedi_device * dev, int dma_index, int n)
+{
+ int dma_chan;
+ unsigned long dma_ptr;
+ unsigned long flags;
+
+ devpriv->dma[dma_index].size = n;
+ dma_chan = devpriv->dma[dma_index].chan;
+ dma_ptr = virt_to_bus(devpriv->dma[dma_index].buf);
+
+ set_dma_mode(dma_chan, DMA_MODE_WRITE);
+ flags = claim_dma_lock();
+ clear_dma_ff(dma_chan);
+ set_dma_addr(dma_chan, dma_ptr);
+ set_dma_count(dma_chan, n);
+ release_dma_lock(flags);
+
+ enable_dma(dma_chan);
+
+ return n;
+}
+
+static irqreturn_t dt282x_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s;
+ struct comedi_subdevice *s_ao;
+ unsigned int supcsr, adcsr, dacsr;
+ int handled = 0;
+
+ if (!dev->attached) {
+ comedi_error(dev, "spurious interrupt");
+ return IRQ_HANDLED;
+ }
+
+ s = dev->subdevices + 0;
+ s_ao = dev->subdevices + 1;
+ adcsr = inw(dev->iobase + DT2821_ADCSR);
+ dacsr = inw(dev->iobase + DT2821_DACSR);
+ supcsr = inw(dev->iobase + DT2821_SUPCSR);
+ if (supcsr & DT2821_DMAD) {
+ if (devpriv->dma_dir == DMA_MODE_READ)
+ dt282x_ai_dma_interrupt(dev);
+ else
+ dt282x_ao_dma_interrupt(dev);
+ handled = 1;
+ }
+ if (adcsr & DT2821_ADERR) {
+ if (devpriv->nread != 0) {
+ comedi_error(dev, "A/D error");
+ dt282x_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_ERROR;
+ }
+ handled = 1;
+ }
+ if (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_ao);
+ s->async->events |= COMEDI_CB_ERROR;
+ handled = 1;
+ }
+#if 0
+ if (adcsr & DT2821_ADDONE) {
+ int ret;
+ short data;
+
+ data = (short) inw(dev->iobase + DT2821_ADDAT);
+ data &= (1 << boardtype.adbits) - 1;
+ if (devpriv->ad_2scomp) {
+ data ^= 1 << (boardtype.adbits - 1);
+ }
+ ret = comedi_buf_put(s->async, data);
+ if (ret == 0) {
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ }
+
+ devpriv->nread--;
+ if (!devpriv->nread) {
+ s->async->events |= COMEDI_CB_EOA;
+ } else {
+ if (supcsr & DT2821_SCDN)
+ update_supcsr(DT2821_STRIG);
+ }
+ handled = 1;
+ }
+#endif
+ comedi_event(dev, s);
+ /* printk("adcsr=0x%02x dacsr-0x%02x supcsr=0x%02x\n", adcsr, dacsr, supcsr); */
+ return IRQ_RETVAL(handled);
+}
+
+static void dt282x_load_changain(struct 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_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+
+ /* XXX should we really be enabling the ad clock here? */
+ devpriv->adcsr = DT2821_ADCLK;
+ update_adcsr(0);
+
+ dt282x_load_changain(dev, 1, &insn->chanspec);
+
+ update_supcsr(DT2821_PRLD);
+ wait_for(!mux_busy(), comedi_error(dev, "timeout\n");
+ return -ETIME;
+ );
+
+ for (i = 0; i < insn->n; i++) {
+ update_supcsr(DT2821_STRIG);
+ wait_for(ad_done(), comedi_error(dev, "timeout\n");
+ return -ETIME;
+ );
+
+ data[i] =
+ inw(dev->iobase +
+ DT2821_ADDAT) & ((1 << boardtype.adbits) - 1);
+ if (devpriv->ad_2scomp)
+ data[i] ^= (1 << (boardtype.adbits - 1));
+ }
+
+ return i;
+}
+
+static int dt282x_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ /* note that mutual compatiblity is not an issue here */
+ if (cmd->scan_begin_src != TRIG_FOLLOW &&
+ cmd->scan_begin_src != TRIG_EXT)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ /* internal trigger */
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ } else {
+ /* external trigger */
+ /* should be level/edge, hi/lo specification here */
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ }
+ if (cmd->convert_arg < 4000) {
+ /* XXX board dependent */
+ cmd->convert_arg = 4000;
+ err++;
+ }
+#define SLOWEST_TIMER (250*(1<<15)*255)
+ if (cmd->convert_arg > SLOWEST_TIMER) {
+ cmd->convert_arg = SLOWEST_TIMER;
+ err++;
+ }
+ if (cmd->convert_arg < this_board->ai_speed) {
+ cmd->convert_arg = this_board->ai_speed;
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ /* any count is allowed */
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ tmp = cmd->convert_arg;
+ dt282x_ns_to_timer(&cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->convert_arg)
+ err++;
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int dt282x_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int timer;
+
+ if (devpriv->usedma == 0) {
+ comedi_error(dev,
+ "driver requires 2 dma channels to execute command");
+ return -EIO;
+ }
+
+ dt282x_disable_dma(dev);
+
+ if (cmd->convert_arg < this_board->ai_speed)
+ cmd->convert_arg = this_board->ai_speed;
+ timer = dt282x_ns_to_timer(&cmd->convert_arg, TRIG_ROUND_NEAREST);
+ outw(timer, dev->iobase + DT2821_TMRCTR);
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ /* internal trigger */
+ devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS0;
+ } else {
+ /* external trigger */
+ devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS0 | DT2821_DS1;
+ }
+ update_supcsr(DT2821_CLRDMADNE | DT2821_BUFFB | DT2821_ADCINIT);
+
+ devpriv->ntrig = cmd->stop_arg * cmd->scan_end_arg;
+ devpriv->nread = devpriv->ntrig;
+
+ devpriv->dma_dir = DMA_MODE_READ;
+ devpriv->current_dma_index = 0;
+ prep_ai_dma(dev, 0, 0);
+ if (devpriv->ntrig) {
+ prep_ai_dma(dev, 1, 0);
+ devpriv->supcsr |= DT2821_DDMA;
+ update_supcsr(0);
+ }
+
+ devpriv->adcsr = 0;
+
+ dt282x_load_changain(dev, cmd->chanlist_len, cmd->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;
+ );
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ update_supcsr(DT2821_STRIG);
+ } else {
+ devpriv->supcsr |= DT2821_XTRIG;
+ update_supcsr(0);
+ }
+
+ return 0;
+}
+
+static void dt282x_disable_dma(struct comedi_device * dev)
+{
+ if (devpriv->usedma) {
+ disable_dma(devpriv->dma[0].chan);
+ disable_dma(devpriv->dma[1].chan);
+ }
+}
+
+static int dt282x_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ dt282x_disable_dma(dev);
+
+ devpriv->adcsr = 0;
+ update_adcsr(0);
+
+ devpriv->supcsr = 0;
+ update_supcsr(DT2821_ADCINIT);
+
+ return 0;
+}
+
+static int dt282x_ns_to_timer(int *nanosec, int round_mode)
+{
+ int prescale, base, divider;
+
+ for (prescale = 0; prescale < 16; prescale++) {
+ if (prescale == 1)
+ continue;
+ base = 250 * (1 << prescale);
+ switch (round_mode) {
+ case TRIG_ROUND_NEAREST:
+ default:
+ divider = (*nanosec + base / 2) / base;
+ break;
+ case TRIG_ROUND_DOWN:
+ divider = (*nanosec) / base;
+ break;
+ case TRIG_ROUND_UP:
+ divider = (*nanosec + base - 1) / base;
+ break;
+ }
+ if (divider < 256) {
+ *nanosec = divider * base;
+ return (prescale << 8) | (255 - divider);
+ }
+ }
+ base = 250 * (1 << 15);
+ divider = 255;
+ *nanosec = divider * base;
+ return (15 << 8) | (255 - divider);
+}
+
+/*
+ * Analog output routine. Selects single channel conversion,
+ * selects correct channel, converts from 2's compliment to
+ * offset binary if necessary, loads the data into the DAC
+ * data register, and performs the conversion.
+ */
+static int dt282x_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = devpriv->ao[CR_CHAN(insn->chanspec)];
+
+ return 1;
+}
+
+static int dt282x_ao_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ short d;
+ unsigned int chan;
+
+ chan = CR_CHAN(insn->chanspec);
+ d = data[0];
+ d &= (1 << boardtype.dabits) - 1;
+ devpriv->ao[chan] = d;
+
+ devpriv->dacsr |= DT2821_SSEL;
+
+ if (chan) {
+ /* select channel */
+ devpriv->dacsr |= DT2821_YSEL;
+ if (devpriv->da0_2scomp)
+ d ^= (1 << (boardtype.dabits - 1));
+ } else {
+ devpriv->dacsr &= ~DT2821_YSEL;
+ if (devpriv->da1_2scomp)
+ d ^= (1 << (boardtype.dabits - 1));
+ }
+
+ update_dacsr(0);
+
+ outw(d, dev->iobase + DT2821_DADAT);
+
+ update_supcsr(DT2821_DACON);
+
+ return 1;
+}
+
+static int dt282x_ao_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_INT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ /* note that mutual compatiblity is not an issue here */
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ if (cmd->scan_begin_arg < 5000 /* XXX unknown */ ) {
+ cmd->scan_begin_arg = 5000;
+ err++;
+ }
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ if (cmd->scan_end_arg > 2) {
+ /* XXX chanlist stuff? */
+ cmd->scan_end_arg = 2;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ /* any count is allowed */
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ tmp = cmd->scan_begin_arg;
+ dt282x_ns_to_timer(&cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+
+ if (err)
+ return 4;
+
+ return 0;
+
+}
+
+static int dt282x_ao_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int x)
+{
+ int size;
+
+ if (x != 0)
+ return -EINVAL;
+
+ size = cfc_read_array_from_buffer(s, devpriv->dma[0].buf,
+ devpriv->dma_maxsize);
+ if (size == 0) {
+ rt_printk("dt282x: AO underrun\n");
+ return -EPIPE;
+ }
+ prep_ao_dma(dev, 0, size);
+
+ size = cfc_read_array_from_buffer(s, devpriv->dma[1].buf,
+ devpriv->dma_maxsize);
+ if (size == 0) {
+ rt_printk("dt282x: AO underrun\n");
+ return -EPIPE;
+ }
+ prep_ao_dma(dev, 1, size);
+
+ update_supcsr(DT2821_STRIG);
+ s->async->inttrig = NULL;
+
+ return 1;
+}
+
+static int dt282x_ao_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int timer;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (devpriv->usedma == 0) {
+ comedi_error(dev,
+ "driver requires 2 dma channels to execute command");
+ return -EIO;
+ }
+
+ dt282x_disable_dma(dev);
+
+ devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS1 | DT2821_DDMA;
+ update_supcsr(DT2821_CLRDMADNE | DT2821_BUFFB | DT2821_DACINIT);
+
+ devpriv->ntrig = cmd->stop_arg * cmd->chanlist_len;
+ devpriv->nread = devpriv->ntrig;
+
+ devpriv->dma_dir = DMA_MODE_WRITE;
+ devpriv->current_dma_index = 0;
+
+ timer = dt282x_ns_to_timer(&cmd->scan_begin_arg, TRIG_ROUND_NEAREST);
+ outw(timer, dev->iobase + DT2821_TMRCTR);
+
+ devpriv->dacsr = DT2821_SSEL | DT2821_DACLK | DT2821_IDARDY;
+ update_dacsr(0);
+
+ s->async->inttrig = dt282x_ao_inttrig;
+
+ return 0;
+}
+
+static int dt282x_ao_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ dt282x_disable_dma(dev);
+
+ devpriv->dacsr = 0;
+ update_dacsr(0);
+
+ devpriv->supcsr = 0;
+ update_supcsr(DT2821_DACINIT);
+
+ return 0;
+}
+
+static int dt282x_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+
+ outw(s->state, dev->iobase + DT2821_DIODAT);
+ }
+ data[1] = inw(dev->iobase + DT2821_DIODAT);
+
+ return 2;
+}
+
+static int dt282x_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int mask;
+
+ mask = (CR_CHAN(insn->chanspec) < 8) ? 0x00ff : 0xff00;
+ if (data[0])
+ 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);
+
+ return 1;
+}
+
+static const struct comedi_lrange *const ai_range_table[] = {
+ &range_dt282x_ai_lo_bipolar,
+ &range_dt282x_ai_lo_unipolar,
+ &range_dt282x_ai_5_bipolar,
+ &range_dt282x_ai_5_unipolar
+};
+static const struct comedi_lrange *const ai_range_pgl_table[] = {
+ &range_dt282x_ai_hi_bipolar,
+ &range_dt282x_ai_hi_unipolar
+};
+static const struct comedi_lrange *opt_ai_range_lkup(int ispgl, int x)
+{
+ if (ispgl) {
+ if (x < 0 || x >= 2)
+ x = 0;
+ return ai_range_pgl_table[x];
+ } else {
+ if (x < 0 || x >= 4)
+ x = 0;
+ return ai_range_table[x];
+ }
+}
+static const struct comedi_lrange *const ao_range_table[] = {
+ &range_bipolar10,
+ &range_unipolar10,
+ &range_bipolar5,
+ &range_unipolar5,
+ &range_bipolar2_5
+};
+static const struct comedi_lrange *opt_ao_range_lkup(int x)
+{
+ if (x < 0 || 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 */
+};
+
+/*
+ options:
+ 0 i/o base
+ 1 irq
+ 2 dma1
+ 3 dma2
+ 4 0=single ended, 1=differential
+ 5 ai 0=straight binary, 1=2's comp
+ 6 ao0 0=straight binary, 1=2's comp
+ 7 ao1 0=straight binary, 1=2's comp
+ 8 ai 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V
+ 9 ao0 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V, 4=±2.5 V
+ 10 ao1 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V, 4=±2.5 V
+ */
+static int dt282x_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int i, irq;
+ int ret;
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+
+ dev->board_name = this_board->name;
+
+ iobase = it->options[opt_iobase];
+ if (!iobase)
+ iobase = 0x240;
+
+ printk("comedi%d: dt282x: 0x%04lx", dev->minor, iobase);
+ if (!request_region(iobase, DT2821_SIZE, "dt282x")) {
+ printk(" I/O port conflict\n");
+ return -EBUSY;
+ }
+ dev->iobase = iobase;
+
+ 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 0
+ if (irq < 0) {
+ unsigned long flags;
+ int irqs;
+
+ save_flags(flags);
+ sti();
+ irqs = probe_irq_on();
+
+ /* trigger interrupt */
+
+ comedi_udelay(100);
+
+ irq = probe_irq_off(irqs);
+ restore_flags(flags);
+ if (0 /* error */ ) {
+ printk(" error probing irq (bad)");
+ }
+ }
+#endif
+ if (irq > 0) {
+ printk(" ( irq = %d )", irq);
+ ret = comedi_request_irq(irq, dt282x_interrupt, 0, "dt282x",
+ dev);
+ if (ret < 0) {
+ printk(" failed to get irq\n");
+ return -EIO;
+ }
+ dev->irq = irq;
+ } else if (irq == 0) {
+ printk(" (no irq)");
+ } else {
+#if 0
+ printk(" (probe returned multiple irqs--bad)");
+#else
+ printk(" (irq probe not implemented)");
+#endif
+ }
+
+ if ((ret = alloc_private(dev, sizeof(struct dt282x_private))) < 0)
+ return ret;
+
+ ret = dt282x_grab_dma(dev, it->options[opt_dma1],
+ it->options[opt_dma2]);
+ if (ret < 0)
+ return ret;
+
+ if ((ret = alloc_subdevices(dev, 3)) < 0)
+ return ret;
+
+ s = dev->subdevices + 0;
+
+ dev->read_subdev = s;
+ /* ai subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ |
+ ((it->options[opt_diff]) ? SDF_DIFF : SDF_COMMON);
+ s->n_chan =
+ (it->options[opt_diff]) ? boardtype.adchan_di : boardtype.
+ adchan_se;
+ s->insn_read = dt282x_ai_insn_read;
+ s->do_cmdtest = dt282x_ai_cmdtest;
+ s->do_cmd = dt282x_ai_cmd;
+ s->cancel = dt282x_ai_cancel;
+ s->maxdata = (1 << boardtype.adbits) - 1;
+ s->len_chanlist = 16;
+ s->range_table =
+ opt_ai_range_lkup(boardtype.ispgl, it->options[opt_ai_range]);
+ devpriv->ad_2scomp = it->options[opt_ai_twos];
+
+ s++;
+ if ((s->n_chan = boardtype.dachan)) {
+ /* ao subsystem */
+ s->type = COMEDI_SUBD_AO;
+ dev->write_subdev = s;
+ s->subdev_flags = SDF_WRITABLE | SDF_CMD_WRITE;
+ s->insn_read = dt282x_ao_insn_read;
+ s->insn_write = dt282x_ao_insn_write;
+ s->do_cmdtest = dt282x_ao_cmdtest;
+ s->do_cmd = dt282x_ao_cmd;
+ s->cancel = dt282x_ao_cancel;
+ s->maxdata = (1 << boardtype.dabits) - 1;
+ s->len_chanlist = 2;
+ s->range_table_list = devpriv->darangelist;
+ 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_WRITABLE;
+ s->n_chan = 16;
+ s->insn_bits = dt282x_dio_insn_bits;
+ s->insn_config = dt282x_dio_insn_config;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+
+ printk("\n");
+
+ return 0;
+}
+
+static void free_resources(struct comedi_device * dev)
+{
+ if (dev->irq) {
+ comedi_free_irq(dev->irq, dev);
+ }
+ if (dev->iobase)
+ release_region(dev->iobase, DT2821_SIZE);
+ 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(struct comedi_device * dev)
+{
+ printk("comedi%d: dt282x: remove\n", dev->minor);
+
+ free_resources(dev);
+
+ return 0;
+}
+
+static int dt282x_grab_dma(struct comedi_device * dev, int dma1, int dma2)
+{
+ int ret;
+
+ devpriv->usedma = 0;
+
+ if (!dma1 && !dma2) {
+ printk(" (no dma)");
+ return 0;
+ }
+
+ if (dma1 == dma2 || dma1 < 5 || dma2 < 5 || dma1 > 7 || dma2 > 7)
+ return -EINVAL;
+
+ if (dma2 < dma1) {
+ int i;
+ i = dma1;
+ dma1 = dma2;
+ dma2 = i;
+ }
+
+ ret = request_dma(dma1, "dt282x A");
+ if (ret)
+ return -EBUSY;
+ devpriv->dma[0].chan = dma1;
+
+ ret = request_dma(dma2, "dt282x B");
+ if (ret)
+ return -EBUSY;
+ devpriv->dma[1].chan = dma2;
+
+ devpriv->dma_maxsize = PAGE_SIZE;
+ 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/drivers/staging/comedi/drivers/dt3000.c b/drivers/staging/comedi/drivers/dt3000.c
new file mode 100644
index 000000000000..d9467984706e
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt3000.c
@@ -0,0 +1,983 @@
+/*
+ comedi/drivers/dt3000.c
+ Data Translation DT3000 series driver
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: dt3000
+Description: Data Translation DT3000 series
+Author: ds
+Devices: [Data Translation] DT3001 (dt3000), DT3001-PGL, DT3002, DT3003,
+ DT3003-PGL, DT3004, DT3005, DT3004-200
+Updated: Mon, 14 Apr 2008 15:41:24 +0100
+Status: works
+
+Configuration Options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first supported
+ PCI device found will be used.
+
+There is code to support AI commands, but it may not work.
+
+AO commands are not supported.
+*/
+
+/*
+ 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.
+*/
+
+#define DEBUG 1
+
+#include "../comedidev.h"
+#include <linux/delay.h>
+
+#include "comedi_pci.h"
+
+#define PCI_VENDOR_ID_DT 0x1116
+
+static const struct comedi_lrange range_dt3000_ai = { 4, {
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ RANGE(-2.5, 2.5),
+ RANGE(-1.25, 1.25)
+ }
+};
+static const struct comedi_lrange range_dt3000_ai_pgl = { 4, {
+ RANGE(-10, 10),
+ RANGE(-1, 1),
+ RANGE(-0.1, 0.1),
+ RANGE(-0.02, 0.02)
+ }
+};
+
+struct dt3k_boardtype {
+
+ const char *name;
+ unsigned int device_id;
+ int adchan;
+ int adbits;
+ int ai_speed;
+ const struct comedi_lrange *adrange;
+ int dachan;
+ int dabits;
+};
+
+
+static const struct dt3k_boardtype dt3k_boardtypes[] = {
+ {name:"dt3001",
+ device_id:0x22,
+ adchan: 16,
+ adbits: 12,
+ adrange: &range_dt3000_ai,
+ ai_speed:3000,
+ dachan: 2,
+ dabits: 12,
+ },
+ {name:"dt3001-pgl",
+ device_id:0x27,
+ adchan: 16,
+ adbits: 12,
+ adrange: &range_dt3000_ai_pgl,
+ ai_speed:3000,
+ dachan: 2,
+ dabits: 12,
+ },
+ {name:"dt3002",
+ device_id:0x23,
+ adchan: 32,
+ adbits: 12,
+ adrange: &range_dt3000_ai,
+ ai_speed:3000,
+ dachan: 0,
+ dabits: 0,
+ },
+ {name:"dt3003",
+ device_id:0x24,
+ adchan: 64,
+ adbits: 12,
+ adrange: &range_dt3000_ai,
+ ai_speed:3000,
+ dachan: 2,
+ dabits: 12,
+ },
+ {name:"dt3003-pgl",
+ device_id:0x28,
+ adchan: 64,
+ adbits: 12,
+ adrange: &range_dt3000_ai_pgl,
+ ai_speed:3000,
+ dachan: 2,
+ dabits: 12,
+ },
+ {name:"dt3004",
+ device_id:0x25,
+ adchan: 16,
+ adbits: 16,
+ adrange: &range_dt3000_ai,
+ ai_speed:10000,
+ dachan: 2,
+ dabits: 12,
+ },
+ {name:"dt3005", /* a.k.a. 3004-200 */
+ device_id:0x26,
+ adchan: 16,
+ adbits: 16,
+ adrange: &range_dt3000_ai,
+ ai_speed:5000,
+ dachan: 2,
+ dabits: 12,
+ },
+};
+
+#define n_dt3k_boards sizeof(dt3k_boardtypes)/sizeof(struct dt3k_boardtype)
+#define this_board ((const struct dt3k_boardtype *)dev->board_ptr)
+
+static DEFINE_PCI_DEVICE_TABLE(dt3k_pci_table) = {
+ {PCI_VENDOR_ID_DT, 0x0022, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_DT, 0x0027, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_DT, 0x0023, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_DT, 0x0024, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_DT, 0x0028, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_DT, 0x0025, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_DT, 0x0026, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, dt3k_pci_table);
+
+#define DT3000_SIZE (4*0x1000)
+
+/* dual-ported RAM location definitions */
+
+#define DPR_DAC_buffer (4*0x000)
+#define DPR_ADC_buffer (4*0x800)
+#define DPR_Command (4*0xfd3)
+#define DPR_SubSys (4*0xfd3)
+#define DPR_Encode (4*0xfd4)
+#define DPR_Params(a) (4*(0xfd5+(a)))
+#define DPR_Tick_Reg_Lo (4*0xff5)
+#define DPR_Tick_Reg_Hi (4*0xff6)
+#define DPR_DA_Buf_Front (4*0xff7)
+#define DPR_DA_Buf_Rear (4*0xff8)
+#define DPR_AD_Buf_Front (4*0xff9)
+#define DPR_AD_Buf_Rear (4*0xffa)
+#define DPR_Int_Mask (4*0xffb)
+#define DPR_Intr_Flag (4*0xffc)
+#define DPR_Response_Mbx (4*0xffe)
+#define DPR_Command_Mbx (4*0xfff)
+
+#define AI_FIFO_DEPTH 2003
+#define AO_FIFO_DEPTH 2048
+
+/* 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
+
+struct dt3k_private {
+
+ struct pci_dev *pci_dev;
+ resource_size_t phys_addr;
+ void *io_addr;
+ unsigned int lock;
+ unsigned int ao_readback[2];
+ unsigned int ai_front;
+ unsigned int ai_rear;
+};
+
+#define devpriv ((struct dt3k_private *)dev->private)
+
+static int dt3000_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int dt3000_detach(struct comedi_device * dev);
+static struct comedi_driver driver_dt3000 = {
+ driver_name:"dt3000",
+ module:THIS_MODULE,
+ attach:dt3000_attach,
+ detach:dt3000_detach,
+};
+
+COMEDI_PCI_INITCLEANUP(driver_dt3000, dt3k_pci_table);
+
+static void dt3k_ai_empty_fifo(struct comedi_device * dev, struct comedi_subdevice * s);
+static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *arg,
+ unsigned int round_mode);
+static int dt3k_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+#ifdef DEBUG
+static void debug_intr_flags(unsigned int flags);
+#endif
+
+#define TIMEOUT 100
+
+static int dt3k_send_cmd(struct comedi_device * dev, unsigned int cmd)
+{
+ int i;
+ unsigned int status = 0;
+
+ writew(cmd, devpriv->io_addr + DPR_Command_Mbx);
+
+ for (i = 0; i < TIMEOUT; i++) {
+ status = readw(devpriv->io_addr + DPR_Command_Mbx);
+ if ((status & DT3000_COMPLETION_MASK) != DT3000_NOTPROCESSED)
+ break;
+ comedi_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(struct comedi_device * dev, unsigned int subsys,
+ unsigned int chan, unsigned int gain)
+{
+ writew(subsys, devpriv->io_addr + DPR_SubSys);
+
+ writew(chan, devpriv->io_addr + DPR_Params(0));
+ writew(gain, devpriv->io_addr + DPR_Params(1));
+
+ dt3k_send_cmd(dev, CMD_READSINGLE);
+
+ return readw(devpriv->io_addr + DPR_Params(2));
+}
+
+static void dt3k_writesingle(struct comedi_device * dev, unsigned int subsys,
+ unsigned int chan, unsigned int data)
+{
+ writew(subsys, devpriv->io_addr + DPR_SubSys);
+
+ writew(chan, devpriv->io_addr + DPR_Params(0));
+ writew(0, devpriv->io_addr + DPR_Params(1));
+ writew(data, devpriv->io_addr + DPR_Params(2));
+
+ dt3k_send_cmd(dev, CMD_WRITESINGLE);
+}
+
+static int debug_n_ints = 0;
+
+// FIXME! Assumes shared interrupt is for this card.
+// What's this debug_n_ints stuff? Obviously needs some work...
+static irqreturn_t dt3k_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s;
+ unsigned int status;
+
+ if (!dev->attached) {
+ return IRQ_NONE;
+ }
+
+ s = dev->subdevices + 0;
+ status = readw(devpriv->io_addr + DPR_Intr_Flag);
+#ifdef DEBUG
+ debug_intr_flags(status);
+#endif
+
+ if (status & DT3000_ADFULL) {
+ dt3k_ai_empty_fifo(dev, s);
+ s->async->events |= COMEDI_CB_BLOCK;
+ }
+
+ if (status & (DT3000_ADSWERR | DT3000_ADHWERR)) {
+ s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ }
+
+ debug_n_ints++;
+ if (debug_n_ints >= 10) {
+ dt3k_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ }
+
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+}
+
+#ifdef DEBUG
+static char *intr_flags[] = {
+ "AdFull", "AdSwError", "AdHwError", "DaEmpty",
+ "DaSwError", "DaHwError", "CtDone", "CmDone",
+};
+static void debug_intr_flags(unsigned int flags)
+{
+ int i;
+ printk("dt3k: intr_flags:");
+ for (i = 0; i < 8; i++) {
+ if (flags & (1 << i)) {
+ printk(" %s", intr_flags[i]);
+ }
+ }
+ printk("\n");
+}
+#endif
+
+static void dt3k_ai_empty_fifo(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int front;
+ int rear;
+ int count;
+ int i;
+ short data;
+
+ front = readw(devpriv->io_addr + DPR_AD_Buf_Front);
+ count = front - devpriv->ai_front;
+ if (count < 0)
+ count += AI_FIFO_DEPTH;
+
+ printk("reading %d samples\n", count);
+
+ rear = devpriv->ai_rear;
+
+ for (i = 0; i < count; i++) {
+ data = readw(devpriv->io_addr + DPR_ADC_buffer + rear);
+ comedi_buf_put(s->async, data);
+ rear++;
+ if (rear >= AI_FIFO_DEPTH)
+ rear = 0;
+ }
+
+ devpriv->ai_rear = rear;
+ writew(rear, devpriv->io_addr + DPR_AD_Buf_Rear);
+}
+
+static int dt3k_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->scan_begin_arg < this_board->ai_speed) {
+ cmd->scan_begin_arg = this_board->ai_speed;
+ err++;
+ }
+ if (cmd->scan_begin_arg > 100 * 16 * 65535) {
+ cmd->scan_begin_arg = 100 * 16 * 65535;
+ err++;
+ }
+ } else {
+ /* not supported */
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < this_board->ai_speed) {
+ cmd->convert_arg = this_board->ai_speed;
+ err++;
+ }
+ if (cmd->convert_arg > 50 * 16 * 65535) {
+ cmd->convert_arg = 50 * 16 * 65535;
+ err++;
+ }
+ } else {
+ /* not supported */
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (cmd->stop_arg > 0x00ffffff) {
+ cmd->stop_arg = 0x00ffffff;
+ err++;
+ }
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ } else {
+ /* not supported */
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ dt3k_ns_to_timer(50, &cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->convert_arg)
+ err++;
+ if (cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->scan_begin_arg <
+ cmd->convert_arg * cmd->scan_end_arg) {
+ cmd->scan_begin_arg =
+ cmd->convert_arg * cmd->scan_end_arg;
+ err++;
+ }
+ } else {
+ /* not supported */
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec,
+ unsigned int round_mode)
+{
+ int divider, base, prescale;
+
+ /* This function needs improvment */
+ /* Don't know if divider==0 works. */
+
+ for (prescale = 0; prescale < 16; prescale++) {
+ base = timer_base * (prescale + 1);
+ switch (round_mode) {
+ case TRIG_ROUND_NEAREST:
+ default:
+ divider = (*nanosec + base / 2) / base;
+ break;
+ case TRIG_ROUND_DOWN:
+ divider = (*nanosec) / base;
+ break;
+ case TRIG_ROUND_UP:
+ divider = (*nanosec) / base;
+ break;
+ }
+ if (divider < 65536) {
+ *nanosec = divider * base;
+ return (prescale << 16) | (divider);
+ }
+ }
+
+ prescale = 15;
+ base = timer_base * (1 << prescale);
+ divider = 65535;
+ *nanosec = divider * base;
+ return (prescale << 16) | (divider);
+}
+
+static int dt3k_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int i;
+ unsigned int chan, range, aref;
+ unsigned int divider;
+ unsigned int tscandiv;
+ int ret;
+ unsigned int mode;
+
+ printk("dt3k_ai_cmd:\n");
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ chan = CR_CHAN(cmd->chanlist[i]);
+ range = CR_RANGE(cmd->chanlist[i]);
+
+ writew((range << 6) | chan,
+ devpriv->io_addr + DPR_ADC_buffer + i);
+ }
+ aref = CR_AREF(cmd->chanlist[0]);
+
+ writew(cmd->scan_end_arg, devpriv->io_addr + DPR_Params(0));
+ printk("param[0]=0x%04x\n", cmd->scan_end_arg);
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ divider = dt3k_ns_to_timer(50, &cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ writew((divider >> 16), devpriv->io_addr + DPR_Params(1));
+ printk("param[1]=0x%04x\n", divider >> 16);
+ writew((divider & 0xffff), devpriv->io_addr + DPR_Params(2));
+ printk("param[2]=0x%04x\n", divider & 0xffff);
+ } else {
+ /* not supported */
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tscandiv = dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ writew((tscandiv >> 16), devpriv->io_addr + DPR_Params(3));
+ printk("param[3]=0x%04x\n", tscandiv >> 16);
+ writew((tscandiv & 0xffff), devpriv->io_addr + DPR_Params(4));
+ printk("param[4]=0x%04x\n", tscandiv & 0xffff);
+ } else {
+ /* not supported */
+ }
+
+ mode = DT3000_AD_RETRIG_INTERNAL | 0 | 0;
+ writew(mode, devpriv->io_addr + DPR_Params(5));
+ printk("param[5]=0x%04x\n", mode);
+ writew(aref == AREF_DIFF, devpriv->io_addr + DPR_Params(6));
+ printk("param[6]=0x%04x\n", aref == AREF_DIFF);
+
+ writew(AI_FIFO_DEPTH / 2, devpriv->io_addr + DPR_Params(7));
+ printk("param[7]=0x%04x\n", AI_FIFO_DEPTH / 2);
+
+ writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
+ ret = dt3k_send_cmd(dev, CMD_CONFIG);
+
+ writew(DT3000_ADFULL | DT3000_ADSWERR | DT3000_ADHWERR,
+ devpriv->io_addr + DPR_Int_Mask);
+
+ debug_n_ints = 0;
+
+ writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
+ ret = dt3k_send_cmd(dev, CMD_START);
+
+ return 0;
+}
+
+static int dt3k_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int ret;
+
+ writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
+ ret = dt3k_send_cmd(dev, CMD_STOP);
+
+ writew(0, devpriv->io_addr + DPR_Int_Mask);
+
+ return 0;
+}
+
+static int dt3k_ai_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ unsigned int chan, gain, aref;
+
+ chan = CR_CHAN(insn->chanspec);
+ gain = CR_RANGE(insn->chanspec);
+ /* XXX docs don't explain how to select aref */
+ aref = CR_AREF(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++) {
+ data[i] = dt3k_readsingle(dev, SUBS_AI, chan, gain);
+ }
+
+ return i;
+}
+
+static int dt3k_ao_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ unsigned int chan;
+
+ chan = CR_CHAN(insn->chanspec);
+ for (i = 0; i < insn->n; i++) {
+ dt3k_writesingle(dev, SUBS_AO, chan, data[i]);
+ devpriv->ao_readback[chan] = data[i];
+ }
+
+ return i;
+}
+
+static int dt3k_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ unsigned int chan;
+
+ chan = CR_CHAN(insn->chanspec);
+ for (i = 0; i < insn->n; i++) {
+ data[i] = devpriv->ao_readback[chan];
+ }
+
+ return i;
+}
+
+static void dt3k_dio_config(struct comedi_device * dev, int bits)
+{
+ /* XXX */
+ writew(SUBS_DOUT, devpriv->io_addr + DPR_SubSys);
+
+ writew(bits, devpriv->io_addr + DPR_Params(0));
+#if 0
+ /* don't know */
+ writew(0, devpriv->io_addr + DPR_Params(1));
+ writew(0, devpriv->io_addr + DPR_Params(2));
+#endif
+
+ dt3k_send_cmd(dev, CMD_CONFIG);
+}
+
+static int dt3k_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int mask;
+
+ mask = (CR_CHAN(insn->chanspec) < 4) ? 0x0f : 0xf0;
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= mask;
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~mask;
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (s->io_bits & (1 << CR_CHAN(insn->
+ chanspec))) ? COMEDI_OUTPUT :
+ COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ mask = (s->io_bits & 0x01) | ((s->io_bits & 0x10) >> 3);
+ dt3k_dio_config(dev, mask);
+
+ return insn->n;
+}
+
+static int dt3k_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= data[1] & data[0];
+ dt3k_writesingle(dev, SUBS_DOUT, 0, s->state);
+ }
+ data[1] = dt3k_readsingle(dev, SUBS_DIN, 0, 0);
+
+ return 2;
+}
+
+static int dt3k_mem_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int addr = CR_CHAN(insn->chanspec);
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ writew(SUBS_MEM, devpriv->io_addr + DPR_SubSys);
+ writew(addr, devpriv->io_addr + DPR_Params(0));
+ writew(1, devpriv->io_addr + DPR_Params(1));
+
+ dt3k_send_cmd(dev, CMD_READCODE);
+
+ data[i] = readw(devpriv->io_addr + DPR_Params(2));
+ }
+
+ return i;
+}
+
+static int dt_pci_probe(struct comedi_device * dev, int bus, int slot);
+
+static int dt3000_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int bus, slot;
+ int ret = 0;
+
+ printk("dt3000:");
+ bus = it->options[0];
+ slot = it->options[1];
+
+ if ((ret = alloc_private(dev, sizeof(struct dt3k_private))) < 0)
+ return ret;
+
+ ret = dt_pci_probe(dev, bus, slot);
+ if (ret < 0)
+ return ret;
+ if (ret == 0) {
+ printk(" no DT board found\n");
+ return -ENODEV;
+ }
+
+ dev->board_name = this_board->name;
+
+ if (comedi_request_irq(devpriv->pci_dev->irq, dt3k_interrupt,
+ IRQF_SHARED, "dt3000", dev)) {
+ printk(" unable to allocate IRQ %u\n", devpriv->pci_dev->irq);
+ return -EINVAL;
+ }
+ dev->irq = devpriv->pci_dev->irq;
+
+ if ((ret = alloc_subdevices(dev, 4)) < 0)
+ return ret;
+
+ s = dev->subdevices;
+ dev->read_subdev = s;
+
+ /* ai subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
+ s->n_chan = this_board->adchan;
+ s->insn_read = dt3k_ai_insn;
+ s->maxdata = (1 << this_board->adbits) - 1;
+ s->len_chanlist = 512;
+ s->range_table = &range_dt3000_ai; /* XXX */
+ s->do_cmd = dt3k_ai_cmd;
+ s->do_cmdtest = dt3k_ai_cmdtest;
+ s->cancel = dt3k_ai_cancel;
+
+ s++;
+ /* ao subsystem */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->insn_read = dt3k_ao_insn_read;
+ s->insn_write = dt3k_ao_insn;
+ s->maxdata = (1 << this_board->dabits) - 1;
+ s->len_chanlist = 1;
+ s->range_table = &range_bipolar10;
+
+ s++;
+ /* dio subsystem */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->insn_config = dt3k_dio_insn_config;
+ s->insn_bits = dt3k_dio_insn_bits;
+ s->maxdata = 1;
+ s->len_chanlist = 8;
+ s->range_table = &range_digital;
+
+ s++;
+ /* mem subsystem */
+ s->type = COMEDI_SUBD_MEMORY;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 0x1000;
+ s->insn_read = dt3k_mem_insn_read;
+ s->maxdata = 0xff;
+ s->len_chanlist = 1;
+ s->range_table = &range_unknown;
+
+#if 0
+ s++;
+ /* proc subsystem */
+ s->type = COMEDI_SUBD_PROC;
+#endif
+
+ return 0;
+}
+
+static int dt3000_detach(struct comedi_device * dev)
+{
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+
+ if (devpriv) {
+ if (devpriv->pci_dev) {
+ if (devpriv->phys_addr) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+ pci_dev_put(devpriv->pci_dev);
+ }
+ if (devpriv->io_addr)
+ iounmap(devpriv->io_addr);
+ }
+ /* XXX */
+
+ return 0;
+}
+
+static struct pci_dev *dt_pci_find_device(struct pci_dev *from, int *board);
+static int setup_pci(struct comedi_device * dev);
+
+static int dt_pci_probe(struct comedi_device * dev, int bus, int slot)
+{
+ int board;
+ int ret;
+ struct pci_dev *pcidev;
+
+ pcidev = NULL;
+ while ((pcidev = dt_pci_find_device(pcidev, &board)) != NULL) {
+ if ((bus == 0 && slot == 0) ||
+ (pcidev->bus->number == bus &&
+ PCI_SLOT(pcidev->devfn) == slot)) {
+ break;
+ }
+ }
+ devpriv->pci_dev = pcidev;
+
+ if (board >= 0)
+ dev->board_ptr = dt3k_boardtypes + board;
+
+ if (!devpriv->pci_dev)
+ return 0;
+
+ if ((ret = setup_pci(dev)) < 0)
+ return ret;
+
+ return 1;
+}
+
+static int setup_pci(struct comedi_device * dev)
+{
+ resource_size_t addr;
+ int ret;
+
+ ret = comedi_pci_enable(devpriv->pci_dev, "dt3000");
+ if (ret < 0)
+ return ret;
+
+ addr = pci_resource_start(devpriv->pci_dev, 0);
+ devpriv->phys_addr = addr;
+ devpriv->io_addr = ioremap(devpriv->phys_addr, DT3000_SIZE);
+ if (!devpriv->io_addr)
+ return -ENOMEM;
+#if DEBUG
+ printk("0x%08llx mapped to %p, ",
+ (unsigned long long)devpriv->phys_addr, devpriv->io_addr);
+#endif
+
+ return 0;
+}
+
+static struct pci_dev *dt_pci_find_device(struct pci_dev *from, int *board)
+{
+ int i;
+
+ for (from = pci_get_device(PCI_VENDOR_ID_DT, PCI_ANY_ID, from);
+ from != NULL;
+ from = pci_get_device(PCI_VENDOR_ID_DT, PCI_ANY_ID, from)) {
+ for (i = 0; i < n_dt3k_boards; i++) {
+ if (from->device == dt3k_boardtypes[i].device_id) {
+ *board = i;
+ return from;
+ }
+ }
+ printk("unknown Data Translation PCI device found with device_id=0x%04x\n", from->device);
+ }
+ *board = -1;
+ return from;
+}
diff --git a/drivers/staging/comedi/drivers/dt9812.c b/drivers/staging/comedi/drivers/dt9812.c
index f2d2173d7219..cc4c04630086 100644
--- a/drivers/staging/comedi/drivers/dt9812.c
+++ b/drivers/staging/comedi/drivers/dt9812.c
@@ -300,22 +300,22 @@ struct slot_dt9812 {
struct comedi_dt9812 *comedi;
};
-static const comedi_lrange dt9812_10_ain_range = { 1, {
+static const struct comedi_lrange dt9812_10_ain_range = { 1, {
BIP_RANGE(10),
}
};
-static const comedi_lrange dt9812_2pt5_ain_range = { 1, {
+static const struct comedi_lrange dt9812_2pt5_ain_range = { 1, {
UNI_RANGE(2.5),
}
};
-static const comedi_lrange dt9812_10_aout_range = { 1, {
+static const struct comedi_lrange dt9812_10_aout_range = { 1, {
BIP_RANGE(10),
}
};
-static const comedi_lrange dt9812_2pt5_aout_range = { 1, {
+static const struct comedi_lrange dt9812_2pt5_aout_range = { 1, {
UNI_RANGE(2.5),
}
};
@@ -892,12 +892,12 @@ static struct usb_driver dt9812_usb_driver = {
* Comedi functions
*/
-static void dt9812_comedi_open(comedi_device *dev)
+static void dt9812_comedi_open(struct comedi_device *dev)
{
down(&devpriv->slot->mutex);
if (devpriv->slot->usb) {
/* We have an attached device, fill in current range info */
- comedi_subdevice *s;
+ struct comedi_subdevice *s;
s = &dev->subdevices[0];
s->n_chan = 8;
@@ -940,8 +940,8 @@ static void dt9812_comedi_open(comedi_device *dev)
up(&devpriv->slot->mutex);
}
-static int dt9812_di_rinsn(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int dt9812_di_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int n;
u8 bits = 0;
@@ -952,8 +952,8 @@ static int dt9812_di_rinsn(comedi_device *dev, comedi_subdevice *s,
return n;
}
-static int dt9812_do_winsn(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int dt9812_do_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int n;
u8 bits = 0;
@@ -970,8 +970,8 @@ static int dt9812_do_winsn(comedi_device *dev, comedi_subdevice *s,
return n;
}
-static int dt9812_ai_rinsn(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int dt9812_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int n;
@@ -985,8 +985,8 @@ static int dt9812_ai_rinsn(comedi_device *dev, comedi_subdevice *s,
return n;
}
-static int dt9812_ao_rinsn(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int dt9812_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int n;
u16 value;
@@ -999,8 +999,8 @@ static int dt9812_ao_rinsn(comedi_device *dev, comedi_subdevice *s,
return n;
}
-static int dt9812_ao_winsn(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int dt9812_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int n;
@@ -1009,10 +1009,10 @@ static int dt9812_ao_winsn(comedi_device *dev, comedi_subdevice *s,
return n;
}
-static int dt9812_attach(comedi_device *dev, comedi_devconfig *it)
+static int dt9812_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
int i;
- comedi_subdevice *s;
+ struct comedi_subdevice *s;
dev->board_name = "dt9812";
@@ -1103,12 +1103,12 @@ static int dt9812_attach(comedi_device *dev, comedi_devconfig *it)
return 0;
}
-static int dt9812_detach(comedi_device *dev)
+static int dt9812_detach(struct comedi_device *dev)
{
return 0;
}
-static comedi_driver dt9812_comedi_driver = {
+static struct comedi_driver dt9812_comedi_driver = {
.module = THIS_MODULE,
.driver_name = "dt9812",
.attach = dt9812_attach,
diff --git a/drivers/staging/comedi/drivers/fl512.c b/drivers/staging/comedi/drivers/fl512.c
new file mode 100644
index 000000000000..6d7bb37d2b1c
--- /dev/null
+++ b/drivers/staging/comedi/drivers/fl512.c
@@ -0,0 +1,186 @@
+/*
+ comedi/drivers/fl512.c
+ Anders Gnistrup <ex18@kalman.iau.dtu.dk>
+*/
+
+/*
+Driver: fl512
+Description: unknown
+Author: Anders Gnistrup <ex18@kalman.iau.dtu.dk>
+Devices: [unknown] FL512 (fl512)
+Status: unknown
+
+Digital I/O is not supported.
+
+Configuration options:
+ [0] - I/O port base address
+*/
+
+#define DEBUG 0
+
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+#include <linux/ioport.h>
+
+#define FL512_SIZE 16 /* the size of the used memory */
+struct fl512_private {
+
+ short ao_readback[2];
+};
+
+#define devpriv ((struct fl512_private *) dev->private)
+
+static const struct comedi_lrange range_fl512 = { 4, {
+ BIP_RANGE(0.5),
+ BIP_RANGE(1),
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(5),
+ UNI_RANGE(10),
+ }
+};
+
+static int fl512_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int fl512_detach(struct comedi_device * dev);
+
+static struct comedi_driver driver_fl512 = {
+ driver_name:"fl512",
+ module:THIS_MODULE,
+ attach:fl512_attach,
+ detach:fl512_detach,
+};
+
+COMEDI_INITCLEANUP(driver_fl512);
+
+static int fl512_ai_insn(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data);
+static int fl512_ao_insn(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data);
+static int fl512_ao_insn_readback(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data);
+
+/*
+ * fl512_ai_insn : this is the analog input function
+ */
+static int fl512_ai_insn(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ unsigned int lo_byte, hi_byte;
+ char chan = CR_CHAN(insn->chanspec);
+ unsigned long iobase = dev->iobase;
+
+ for (n = 0; n < insn->n; n++) { /* sample n times on selected channel */
+ /* XXX probably can move next step out of for() loop -- will make
+ * AI a little bit faster. */
+ outb(chan, iobase + 2); /* select chan */
+ outb(0, iobase + 3); /* start conversion */
+ /* XXX should test "done" flag instead of delay */
+ comedi_udelay(30); /* sleep 30 usec */
+ lo_byte = inb(iobase + 2); /* low 8 byte */
+ hi_byte = inb(iobase + 3) & 0xf; /* high 4 bit and mask */
+ data[n] = lo_byte + (hi_byte << 8);
+ }
+ return n;
+}
+
+/*
+ * fl512_ao_insn : used to write to a DA port n times
+ */
+static int fl512_ao_insn(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int chan = CR_CHAN(insn->chanspec); /* get chan to write */
+ unsigned long iobase = dev->iobase; /* get base address */
+
+ for (n = 0; n < insn->n; n++) { /* write n data set */
+ outb(data[n] & 0x0ff, iobase + 4 + 2 * chan); /* write low byte */
+ outb((data[n] & 0xf00) >> 8, iobase + 4 + 2 * chan); /* write high byte */
+ inb(iobase + 4 + 2 * chan); /* trig */
+
+ devpriv->ao_readback[chan] = data[n];
+ }
+ return n;
+}
+
+/*
+ * fl512_ao_insn_readback : used to read previous values written to
+ * DA port
+ */
+static int fl512_ao_insn_readback(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (n = 0; n < insn->n; n++) {
+ data[n] = devpriv->ao_readback[chan];
+ }
+
+ return n;
+}
+
+/*
+ * start attach
+ */
+static int fl512_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ unsigned long iobase;
+ struct comedi_subdevice *s; /* pointer to the subdevice:
+ Analog in, Analog out, ( not made ->and Digital IO) */
+
+ iobase = it->options[0];
+ printk("comedi:%d fl512: 0x%04lx", dev->minor, iobase);
+ if (!request_region(iobase, FL512_SIZE, "fl512")) {
+ printk(" I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+ dev->board_name = "fl512";
+ if (alloc_private(dev, sizeof(struct fl512_private)) < 0)
+ return -ENOMEM;
+
+#if DEBUG
+ printk("malloc ok\n");
+#endif
+
+ if (alloc_subdevices(dev, 2) < 0)
+ return -ENOMEM;
+
+ /*
+ * this if the definitions of the supdevices, 2 have been defined
+ */
+ /* Analog indput */
+ s = dev->subdevices + 0;
+ s->type = COMEDI_SUBD_AI; /* define subdevice as Analog In */
+ s->subdev_flags = SDF_READABLE | SDF_GROUND; /* you can read it from userspace */
+ s->n_chan = 16; /* Number of Analog input channels */
+ s->maxdata = 0x0fff; /* accept only 12 bits of data */
+ s->range_table = &range_fl512; /* device use one of the ranges */
+ s->insn_read = fl512_ai_insn; /* function to call when read AD */
+ printk("comedi: fl512: subdevice 0 initialized\n");
+
+ /* Analog output */
+ s = dev->subdevices + 1;
+ s->type = COMEDI_SUBD_AO; /* define subdevice as Analog OUT */
+ s->subdev_flags = SDF_WRITABLE; /* you can write it from userspace */
+ s->n_chan = 2; /* Number of Analog output channels */
+ s->maxdata = 0x0fff; /* accept only 12 bits of data */
+ s->range_table = &range_fl512; /* device use one of the ranges */
+ s->insn_write = fl512_ao_insn; /* function to call when write DA */
+ s->insn_read = fl512_ao_insn_readback; /* function to call when reading DA */
+ printk("comedi: fl512: subdevice 1 initialized\n");
+
+ return 1;
+}
+
+static int fl512_detach(struct comedi_device * dev)
+{
+ if (dev->iobase)
+ release_region(dev->iobase, FL512_SIZE);
+ printk("comedi%d: fl512: dummy i detach\n", dev->minor);
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/gsc_hpdi.c b/drivers/staging/comedi/drivers/gsc_hpdi.c
new file mode 100644
index 000000000000..49e5c86c2dd8
--- /dev/null
+++ b/drivers/staging/comedi/drivers/gsc_hpdi.c
@@ -0,0 +1,1060 @@
+/*
+ comedi/drivers/gsc_hpdi.c
+ This is a driver for the General Standards Corporation High
+ Speed Parallel Digital Interface rs485 boards.
+
+ Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+ Copyright (C) 2003 Coherent Imaging Systems
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+************************************************************************/
+
+/*
+
+Driver: gsc_hpdi
+Description: General Standards Corporation High
+ Speed Parallel Digital Interface rs485 boards
+Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+Status: only receive mode works, transmit not supported
+Updated: 2003-02-20
+Devices: [General Standards Corporation] PCI-HPDI32 (gsc_hpdi),
+ PMC-HPDI32
+
+Configuration options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+
+There are some additional hpdi models available from GSC for which
+support could be added to this driver.
+
+*/
+
+#include "../comedidev.h"
+#include <linux/delay.h>
+
+#include "comedi_pci.h"
+#include "plx9080.h"
+#include "comedi_fc.h"
+
+static int hpdi_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int hpdi_detach(struct comedi_device * dev);
+void abort_dma(struct comedi_device * dev, unsigned int channel);
+static int hpdi_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int hpdi_cmd_test(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int hpdi_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static irqreturn_t handle_interrupt(int irq, void *d PT_REGS_ARG);
+static int dio_config_block_size(struct comedi_device * dev, unsigned int * data);
+
+#undef HPDI_DEBUG // disable debugging messages
+//#define HPDI_DEBUG // enable debugging code
+
+#ifdef HPDI_DEBUG
+#define DEBUG_PRINT(format, args...) rt_printk(format , ## args )
+#else
+#define DEBUG_PRINT(format, args...)
+#endif
+
+#define TIMER_BASE 50 // 20MHz master clock
+#define DMA_BUFFER_SIZE 0x10000
+#define NUM_DMA_BUFFERS 4
+#define NUM_DMA_DESCRIPTORS 256
+
+// indices of base address regions
+enum base_address_regions {
+ PLX9080_BADDRINDEX = 0,
+ HPDI_BADDRINDEX = 2,
+};
+
+enum hpdi_registers {
+ FIRMWARE_REV_REG = 0x0,
+ BOARD_CONTROL_REG = 0x4,
+ BOARD_STATUS_REG = 0x8,
+ TX_PROG_ALMOST_REG = 0xc,
+ RX_PROG_ALMOST_REG = 0x10,
+ FEATURES_REG = 0x14,
+ FIFO_REG = 0x18,
+ TX_STATUS_COUNT_REG = 0x1c,
+ TX_LINE_VALID_COUNT_REG = 0x20,
+ TX_LINE_INVALID_COUNT_REG = 0x24,
+ RX_STATUS_COUNT_REG = 0x28,
+ RX_LINE_COUNT_REG = 0x2c,
+ INTERRUPT_CONTROL_REG = 0x30,
+ INTERRUPT_STATUS_REG = 0x34,
+ TX_CLOCK_DIVIDER_REG = 0x38,
+ TX_FIFO_SIZE_REG = 0x40,
+ RX_FIFO_SIZE_REG = 0x44,
+ TX_FIFO_WORDS_REG = 0x48,
+ RX_FIFO_WORDS_REG = 0x4c,
+ INTERRUPT_EDGE_LEVEL_REG = 0x50,
+ INTERRUPT_POLARITY_REG = 0x54,
+};
+
+int command_channel_valid(unsigned int channel)
+{
+ if (channel == 0 || channel > 6) {
+ rt_printk("gsc_hpdi: bug! invalid cable command channel\n");
+ return 0;
+ }
+ return 1;
+}
+
+// bit definitions
+
+enum firmware_revision_bits {
+ FEATURES_REG_PRESENT_BIT = 0x8000,
+};
+int firmware_revision(uint32_t fwr_bits)
+{
+ return fwr_bits & 0xff;
+}
+
+int pcb_revision(uint32_t fwr_bits)
+{
+ return (fwr_bits >> 8) & 0xff;
+}
+
+int hpdi_subid(uint32_t fwr_bits)
+{
+ return (fwr_bits >> 16) & 0xff;
+}
+
+enum board_control_bits {
+ BOARD_RESET_BIT = 0x1, /* wait 10usec before accessing fifos */
+ TX_FIFO_RESET_BIT = 0x2,
+ RX_FIFO_RESET_BIT = 0x4,
+ TX_ENABLE_BIT = 0x10,
+ RX_ENABLE_BIT = 0x20,
+ DEMAND_DMA_DIRECTION_TX_BIT = 0x40, /* for channel 0, channel 1 can only transmit (when present) */
+ LINE_VALID_ON_STATUS_VALID_BIT = 0x80,
+ START_TX_BIT = 0x10,
+ CABLE_THROTTLE_ENABLE_BIT = 0x20,
+ TEST_MODE_ENABLE_BIT = 0x80000000,
+};
+uint32_t command_discrete_output_bits(unsigned int channel, int output,
+ int output_value)
+{
+ uint32_t bits = 0;
+
+ if (command_channel_valid(channel) == 0)
+ return 0;
+ if (output) {
+ bits |= 0x1 << (16 + channel);
+ if (output_value)
+ bits |= 0x1 << (24 + channel);
+ } else
+ bits |= 0x1 << (24 + channel);
+
+ return bits;
+}
+
+enum board_status_bits {
+ COMMAND_LINE_STATUS_MASK = 0x7f,
+ TX_IN_PROGRESS_BIT = 0x80,
+ TX_NOT_EMPTY_BIT = 0x100,
+ TX_NOT_ALMOST_EMPTY_BIT = 0x200,
+ TX_NOT_ALMOST_FULL_BIT = 0x400,
+ TX_NOT_FULL_BIT = 0x800,
+ RX_NOT_EMPTY_BIT = 0x1000,
+ RX_NOT_ALMOST_EMPTY_BIT = 0x2000,
+ RX_NOT_ALMOST_FULL_BIT = 0x4000,
+ RX_NOT_FULL_BIT = 0x8000,
+ BOARD_JUMPER0_INSTALLED_BIT = 0x10000,
+ BOARD_JUMPER1_INSTALLED_BIT = 0x20000,
+ TX_OVERRUN_BIT = 0x200000,
+ RX_UNDERRUN_BIT = 0x400000,
+ RX_OVERRUN_BIT = 0x800000,
+};
+
+uint32_t almost_full_bits(unsigned int num_words)
+{
+// XXX need to add or subtract one?
+ return (num_words << 16) & 0xff0000;
+}
+
+uint32_t almost_empty_bits(unsigned int num_words)
+{
+ return num_words & 0xffff;
+}
+unsigned int almost_full_num_words(uint32_t bits)
+{
+// XXX need to add or subtract one?
+ return (bits >> 16) & 0xffff;
+}
+unsigned int almost_empty_num_words(uint32_t bits)
+{
+ return bits & 0xffff;
+}
+
+enum features_bits {
+ FIFO_SIZE_PRESENT_BIT = 0x1,
+ FIFO_WORDS_PRESENT_BIT = 0x2,
+ LEVEL_EDGE_INTERRUPTS_PRESENT_BIT = 0x4,
+ GPIO_SUPPORTED_BIT = 0x8,
+ PLX_DMA_CH1_SUPPORTED_BIT = 0x10,
+ OVERRUN_UNDERRUN_SUPPORTED_BIT = 0x20,
+};
+
+enum interrupt_sources {
+ FRAME_VALID_START_INTR = 0,
+ FRAME_VALID_END_INTR = 1,
+ TX_FIFO_EMPTY_INTR = 8,
+ TX_FIFO_ALMOST_EMPTY_INTR = 9,
+ TX_FIFO_ALMOST_FULL_INTR = 10,
+ TX_FIFO_FULL_INTR = 11,
+ RX_EMPTY_INTR = 12,
+ RX_ALMOST_EMPTY_INTR = 13,
+ RX_ALMOST_FULL_INTR = 14,
+ RX_FULL_INTR = 15,
+};
+int command_intr_source(unsigned int channel)
+{
+ if (command_channel_valid(channel) == 0)
+ channel = 1;
+ return channel + 1;
+}
+
+uint32_t intr_bit(int interrupt_source)
+{
+ return 0x1 << interrupt_source;
+}
+
+uint32_t tx_clock_divisor_bits(unsigned int divisor)
+{
+ return divisor & 0xff;
+}
+
+unsigned int fifo_size(uint32_t fifo_size_bits)
+{
+ return fifo_size_bits & 0xfffff;
+}
+
+unsigned int fifo_words(uint32_t fifo_words_bits)
+{
+ return fifo_words_bits & 0xfffff;
+}
+
+uint32_t intr_edge_bit(int interrupt_source)
+{
+ return 0x1 << interrupt_source;
+}
+
+uint32_t intr_active_high_bit(int interrupt_source)
+{
+ return 0x1 << interrupt_source;
+}
+
+struct hpdi_board {
+
+ char *name;
+ int device_id; // pci device id
+ int subdevice_id; // pci subdevice id
+};
+
+
+static const struct hpdi_board hpdi_boards[] = {
+ {
+ name: "pci-hpdi32",
+ device_id:PCI_DEVICE_ID_PLX_9080,
+ subdevice_id:0x2400,
+ },
+#if 0
+ {
+ name: "pxi-hpdi32",
+ device_id:0x9656,
+ subdevice_id:0x2705,
+ },
+#endif
+};
+
+static inline unsigned int num_boards(void)
+{
+ return sizeof(hpdi_boards) / sizeof(struct hpdi_board);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(hpdi_pci_table) = {
+ {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9080, PCI_VENDOR_ID_PLX, 0x2400,
+ 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, hpdi_pci_table);
+
+static inline struct hpdi_board *board(const struct comedi_device * dev)
+{
+ return (struct hpdi_board *) dev->board_ptr;
+}
+
+struct hpdi_private {
+
+ struct pci_dev *hw_dev; // pointer to board's pci_dev struct
+ // base addresses (physical)
+ resource_size_t plx9080_phys_iobase;
+ resource_size_t hpdi_phys_iobase;
+ // base addresses (ioremapped)
+ void *plx9080_iobase;
+ void *hpdi_iobase;
+ uint32_t *dio_buffer[NUM_DMA_BUFFERS]; // dma buffers
+ dma_addr_t dio_buffer_phys_addr[NUM_DMA_BUFFERS]; // physical addresses of dma buffers
+ struct plx_dma_desc *dma_desc; // array of dma descriptors read by plx9080, allocated to get proper alignment
+ dma_addr_t dma_desc_phys_addr; // physical address of dma descriptor array
+ unsigned int num_dma_descriptors;
+ uint32_t *desc_dio_buffer[NUM_DMA_DESCRIPTORS]; // pointer to start of buffers indexed by descriptor
+ volatile unsigned int dma_desc_index; // index of the dma descriptor that is currently being used
+ unsigned int tx_fifo_size;
+ unsigned int rx_fifo_size;
+ volatile unsigned long dio_count;
+ volatile uint32_t bits[24]; // software copies of values written to hpdi registers
+ volatile unsigned int block_size; // number of bytes at which to generate COMEDI_CB_BLOCK events
+ unsigned dio_config_output:1;
+};
+
+
+static inline struct hpdi_private *priv(struct comedi_device * dev)
+{
+ return dev->private;
+}
+
+static struct comedi_driver driver_hpdi = {
+ driver_name:"gsc_hpdi",
+ module:THIS_MODULE,
+ attach:hpdi_attach,
+ detach:hpdi_detach,
+};
+
+COMEDI_PCI_INITCLEANUP(driver_hpdi, hpdi_pci_table);
+
+static int dio_config_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ priv(dev)->dio_config_output = 1;
+ return insn->n;
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ priv(dev)->dio_config_output = 0;
+ return insn->n;
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ priv(dev)->
+ dio_config_output ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+ break;
+ case INSN_CONFIG_BLOCK_SIZE:
+ return dio_config_block_size(dev, data);
+ break;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static void disable_plx_interrupts(struct comedi_device * dev)
+{
+ writel(0, priv(dev)->plx9080_iobase + PLX_INTRCS_REG);
+}
+
+// initialize plx9080 chip
+static void init_plx9080(struct comedi_device * dev)
+{
+ uint32_t bits;
+ void *plx_iobase = priv(dev)->plx9080_iobase;
+
+ // plx9080 dump
+ DEBUG_PRINT(" plx interrupt status 0x%x\n",
+ readl(plx_iobase + PLX_INTRCS_REG));
+ DEBUG_PRINT(" plx id bits 0x%x\n", readl(plx_iobase + PLX_ID_REG));
+ DEBUG_PRINT(" plx control reg 0x%x\n",
+ readl(priv(dev)->plx9080_iobase + PLX_CONTROL_REG));
+
+ DEBUG_PRINT(" plx revision 0x%x\n",
+ readl(plx_iobase + PLX_REVISION_REG));
+ DEBUG_PRINT(" plx dma channel 0 mode 0x%x\n",
+ readl(plx_iobase + PLX_DMA0_MODE_REG));
+ DEBUG_PRINT(" plx dma channel 1 mode 0x%x\n",
+ readl(plx_iobase + PLX_DMA1_MODE_REG));
+ DEBUG_PRINT(" plx dma channel 0 pci address 0x%x\n",
+ readl(plx_iobase + PLX_DMA0_PCI_ADDRESS_REG));
+ DEBUG_PRINT(" plx dma channel 0 local address 0x%x\n",
+ readl(plx_iobase + PLX_DMA0_LOCAL_ADDRESS_REG));
+ DEBUG_PRINT(" plx dma channel 0 transfer size 0x%x\n",
+ readl(plx_iobase + PLX_DMA0_TRANSFER_SIZE_REG));
+ DEBUG_PRINT(" plx dma channel 0 descriptor 0x%x\n",
+ readl(plx_iobase + PLX_DMA0_DESCRIPTOR_REG));
+ DEBUG_PRINT(" plx dma channel 0 command status 0x%x\n",
+ readb(plx_iobase + PLX_DMA0_CS_REG));
+ DEBUG_PRINT(" plx dma channel 0 threshold 0x%x\n",
+ readl(plx_iobase + PLX_DMA0_THRESHOLD_REG));
+ DEBUG_PRINT(" plx bigend 0x%x\n", readl(plx_iobase + PLX_BIGEND_REG));
+#ifdef __BIG_ENDIAN
+ bits = BIGEND_DMA0 | BIGEND_DMA1;
+#else
+ bits = 0;
+#endif
+ writel(bits, priv(dev)->plx9080_iobase + PLX_BIGEND_REG);
+
+ disable_plx_interrupts(dev);
+
+ abort_dma(dev, 0);
+ abort_dma(dev, 1);
+
+ // configure dma0 mode
+ bits = 0;
+ // enable ready input
+ bits |= PLX_DMA_EN_READYIN_BIT;
+ // enable dma chaining
+ bits |= PLX_EN_CHAIN_BIT;
+ // enable interrupt on dma done (probably don't need this, since chain never finishes)
+ bits |= PLX_EN_DMA_DONE_INTR_BIT;
+ // don't increment local address during transfers (we are transferring from a fixed fifo register)
+ bits |= PLX_LOCAL_ADDR_CONST_BIT;
+ // route dma interrupt to pci bus
+ bits |= PLX_DMA_INTR_PCI_BIT;
+ // enable demand mode
+ bits |= PLX_DEMAND_MODE_BIT;
+ // enable local burst mode
+ bits |= PLX_DMA_LOCAL_BURST_EN_BIT;
+ bits |= PLX_LOCAL_BUS_32_WIDE_BITS;
+ writel(bits, plx_iobase + PLX_DMA0_MODE_REG);
+}
+
+/* Allocate and initialize the subdevice structures.
+ */
+static int setup_subdevices(struct comedi_device * dev)
+{
+ struct comedi_subdevice *s;
+
+ if (alloc_subdevices(dev, 1) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ /* analog input subdevice */
+ dev->read_subdev = s;
+/* dev->write_subdev = s; */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags =
+ SDF_READABLE | SDF_WRITEABLE | SDF_LSAMPL | SDF_CMD_READ;
+ s->n_chan = 32;
+ s->len_chanlist = 32;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_config = dio_config_insn;
+ s->do_cmd = hpdi_cmd;
+ s->do_cmdtest = hpdi_cmd_test;
+ s->cancel = hpdi_cancel;
+
+ return 0;
+}
+
+static int init_hpdi(struct comedi_device * dev)
+{
+ uint32_t plx_intcsr_bits;
+
+ writel(BOARD_RESET_BIT, priv(dev)->hpdi_iobase + BOARD_CONTROL_REG);
+ comedi_udelay(10);
+
+ writel(almost_empty_bits(32) | almost_full_bits(32),
+ priv(dev)->hpdi_iobase + RX_PROG_ALMOST_REG);
+ writel(almost_empty_bits(32) | almost_full_bits(32),
+ priv(dev)->hpdi_iobase + TX_PROG_ALMOST_REG);
+
+ priv(dev)->tx_fifo_size = fifo_size(readl(priv(dev)->hpdi_iobase +
+ TX_FIFO_SIZE_REG));
+ priv(dev)->rx_fifo_size = fifo_size(readl(priv(dev)->hpdi_iobase +
+ RX_FIFO_SIZE_REG));
+
+ writel(0, priv(dev)->hpdi_iobase + INTERRUPT_CONTROL_REG);
+
+ // enable interrupts
+ plx_intcsr_bits =
+ ICS_AERR | ICS_PERR | ICS_PIE | ICS_PLIE | ICS_PAIE | ICS_LIE |
+ ICS_DMA0_E;
+ writel(plx_intcsr_bits, priv(dev)->plx9080_iobase + PLX_INTRCS_REG);
+
+ return 0;
+}
+
+// setup dma descriptors so a link completes every 'transfer_size' bytes
+static int setup_dma_descriptors(struct comedi_device * dev,
+ unsigned int transfer_size)
+{
+ unsigned int buffer_index, buffer_offset;
+ uint32_t next_bits = PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT |
+ PLX_XFER_LOCAL_TO_PCI;
+ unsigned int i;
+
+ if (transfer_size > DMA_BUFFER_SIZE)
+ transfer_size = DMA_BUFFER_SIZE;
+ transfer_size -= transfer_size % sizeof(uint32_t);
+ if (transfer_size == 0)
+ return -1;
+
+ DEBUG_PRINT(" transfer_size %i\n", transfer_size);
+ DEBUG_PRINT(" descriptors at 0x%lx\n",
+ (unsigned long)priv(dev)->dma_desc_phys_addr);
+
+ buffer_offset = 0;
+ buffer_index = 0;
+ for (i = 0; i < NUM_DMA_DESCRIPTORS &&
+ buffer_index < NUM_DMA_BUFFERS; i++) {
+ priv(dev)->dma_desc[i].pci_start_addr =
+ cpu_to_le32(priv(dev)->
+ dio_buffer_phys_addr[buffer_index] + buffer_offset);
+ priv(dev)->dma_desc[i].local_start_addr = cpu_to_le32(FIFO_REG);
+ priv(dev)->dma_desc[i].transfer_size =
+ cpu_to_le32(transfer_size);
+ priv(dev)->dma_desc[i].next =
+ cpu_to_le32((priv(dev)->dma_desc_phys_addr + (i +
+ 1) *
+ sizeof(priv(dev)->dma_desc[0])) | next_bits);
+
+ priv(dev)->desc_dio_buffer[i] =
+ priv(dev)->dio_buffer[buffer_index] +
+ (buffer_offset / sizeof(uint32_t));
+
+ buffer_offset += transfer_size;
+ if (transfer_size + buffer_offset > DMA_BUFFER_SIZE) {
+ buffer_offset = 0;
+ buffer_index++;
+ }
+
+ DEBUG_PRINT(" desc %i\n", i);
+ DEBUG_PRINT(" start addr virt 0x%p, phys 0x%lx\n",
+ priv(dev)->desc_dio_buffer[i],
+ (unsigned long)priv(dev)->dma_desc[i].pci_start_addr);
+ DEBUG_PRINT(" next 0x%lx\n",
+ (unsigned long)priv(dev)->dma_desc[i].next);
+ }
+ priv(dev)->num_dma_descriptors = i;
+ // fix last descriptor to point back to first
+ priv(dev)->dma_desc[i - 1].next =
+ cpu_to_le32(priv(dev)->dma_desc_phys_addr | next_bits);
+ DEBUG_PRINT(" desc %i next fixup 0x%lx\n", i - 1,
+ (unsigned long)priv(dev)->dma_desc[i - 1].next);
+
+ priv(dev)->block_size = transfer_size;
+
+ return transfer_size;
+}
+
+static int hpdi_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct pci_dev *pcidev;
+ int i;
+ int retval;
+
+ printk("comedi%d: gsc_hpdi\n", dev->minor);
+
+ if (alloc_private(dev, sizeof(struct hpdi_private)) < 0)
+ return -ENOMEM;
+
+ pcidev = NULL;
+ for (i = 0; i < num_boards() && dev->board_ptr == NULL; i++) {
+ do {
+ pcidev = pci_get_subsys(PCI_VENDOR_ID_PLX,
+ hpdi_boards[i].device_id, PCI_VENDOR_ID_PLX,
+ hpdi_boards[i].subdevice_id, pcidev);
+ // was a particular bus/slot requested?
+ if (it->options[0] || it->options[1]) {
+ // are we on the wrong bus/slot?
+ if (pcidev->bus->number != it->options[0] ||
+ PCI_SLOT(pcidev->devfn) !=
+ it->options[1])
+ continue;
+ }
+ if (pcidev) {
+ priv(dev)->hw_dev = pcidev;
+ dev->board_ptr = hpdi_boards + i;
+ break;
+ }
+ } while (pcidev != NULL);
+ }
+ if (dev->board_ptr == NULL) {
+ printk("gsc_hpdi: no hpdi card found\n");
+ return -EIO;
+ }
+
+ printk("gsc_hpdi: found %s on bus %i, slot %i\n", board(dev)->name,
+ pcidev->bus->number, PCI_SLOT(pcidev->devfn));
+
+ if (comedi_pci_enable(pcidev, driver_hpdi.driver_name)) {
+ printk(KERN_WARNING
+ " failed enable PCI device and request regions\n");
+ return -EIO;
+ }
+ pci_set_master(pcidev);
+
+ //Initialize dev->board_name
+ dev->board_name = board(dev)->name;
+
+ priv(dev)->plx9080_phys_iobase =
+ pci_resource_start(pcidev, PLX9080_BADDRINDEX);
+ priv(dev)->hpdi_phys_iobase =
+ pci_resource_start(pcidev, HPDI_BADDRINDEX);
+
+ // remap, won't work with 2.0 kernels but who cares
+ priv(dev)->plx9080_iobase = ioremap(priv(dev)->plx9080_phys_iobase,
+ pci_resource_len(pcidev, PLX9080_BADDRINDEX));
+ priv(dev)->hpdi_iobase = ioremap(priv(dev)->hpdi_phys_iobase,
+ pci_resource_len(pcidev, HPDI_BADDRINDEX));
+ if (!priv(dev)->plx9080_iobase || !priv(dev)->hpdi_iobase) {
+ printk(" failed to remap io memory\n");
+ return -ENOMEM;
+ }
+
+ DEBUG_PRINT(" plx9080 remapped to 0x%p\n", priv(dev)->plx9080_iobase);
+ DEBUG_PRINT(" hpdi remapped to 0x%p\n", priv(dev)->hpdi_iobase);
+
+ init_plx9080(dev);
+
+ // get irq
+ if (comedi_request_irq(pcidev->irq, handle_interrupt, IRQF_SHARED,
+ driver_hpdi.driver_name, dev)) {
+ printk(" unable to allocate irq %u\n", pcidev->irq);
+ return -EINVAL;
+ }
+ dev->irq = pcidev->irq;
+
+ printk(" irq %u\n", dev->irq);
+
+ // alocate pci dma buffers
+ for (i = 0; i < NUM_DMA_BUFFERS; i++) {
+ priv(dev)->dio_buffer[i] =
+ pci_alloc_consistent(priv(dev)->hw_dev, DMA_BUFFER_SIZE,
+ &priv(dev)->dio_buffer_phys_addr[i]);
+ DEBUG_PRINT("dio_buffer at virt 0x%p, phys 0x%lx\n",
+ priv(dev)->dio_buffer[i],
+ (unsigned long)priv(dev)->dio_buffer_phys_addr[i]);
+ }
+ // allocate dma descriptors
+ priv(dev)->dma_desc = pci_alloc_consistent(priv(dev)->hw_dev,
+ sizeof(struct plx_dma_desc) * NUM_DMA_DESCRIPTORS,
+ &priv(dev)->dma_desc_phys_addr);
+ if (priv(dev)->dma_desc_phys_addr & 0xf) {
+ printk(" dma descriptors not quad-word aligned (bug)\n");
+ return -EIO;
+ }
+
+ retval = setup_dma_descriptors(dev, 0x1000);
+ if (retval < 0)
+ return retval;
+
+ retval = setup_subdevices(dev);
+ if (retval < 0)
+ return retval;
+
+ return init_hpdi(dev);
+}
+
+static int hpdi_detach(struct comedi_device * dev)
+{
+ unsigned int i;
+
+ printk("comedi%d: gsc_hpdi: remove\n", dev->minor);
+
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+ if (priv(dev)) {
+ if (priv(dev)->hw_dev) {
+ if (priv(dev)->plx9080_iobase) {
+ disable_plx_interrupts(dev);
+ iounmap((void *)priv(dev)->plx9080_iobase);
+ }
+ if (priv(dev)->hpdi_iobase)
+ iounmap((void *)priv(dev)->hpdi_iobase);
+ // free pci dma buffers
+ for (i = 0; i < NUM_DMA_BUFFERS; i++) {
+ if (priv(dev)->dio_buffer[i])
+ pci_free_consistent(priv(dev)->hw_dev,
+ DMA_BUFFER_SIZE,
+ priv(dev)->dio_buffer[i],
+ priv(dev)->
+ dio_buffer_phys_addr[i]);
+ }
+ // free dma descriptors
+ if (priv(dev)->dma_desc)
+ pci_free_consistent(priv(dev)->hw_dev,
+ sizeof(struct plx_dma_desc) *
+ NUM_DMA_DESCRIPTORS,
+ priv(dev)->dma_desc,
+ priv(dev)->dma_desc_phys_addr);
+ if (priv(dev)->hpdi_phys_iobase) {
+ comedi_pci_disable(priv(dev)->hw_dev);
+ }
+ pci_dev_put(priv(dev)->hw_dev);
+ }
+ }
+ return 0;
+}
+
+static int dio_config_block_size(struct comedi_device * dev, unsigned int * data)
+{
+ unsigned int requested_block_size;
+ int retval;
+
+ requested_block_size = data[1];
+
+ retval = setup_dma_descriptors(dev, requested_block_size);
+ if (retval < 0)
+ return retval;
+
+ data[1] = retval;
+
+ return 2;
+}
+
+static int di_cmd_test(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+ int i;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ // uniqueness check
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (!cmd->chanlist_len) {
+ cmd->chanlist_len = 32;
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ if (!cmd->stop_arg) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ break;
+ case TRIG_NONE:
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (err)
+ return 4;
+
+ if (cmd->chanlist) {
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ if (CR_CHAN(cmd->chanlist[i]) != i) {
+ // XXX could support 8 channels or 16 channels
+ comedi_error(dev,
+ "chanlist must be channels 0 to 31 in order");
+ err++;
+ break;
+ }
+ }
+ }
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int hpdi_cmd_test(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ if (priv(dev)->dio_config_output) {
+ return -EINVAL;
+ } else
+ return di_cmd_test(dev, s, cmd);
+}
+
+static inline void hpdi_writel(struct comedi_device * dev, uint32_t bits,
+ unsigned int offset)
+{
+ writel(bits | priv(dev)->bits[offset / sizeof(uint32_t)],
+ priv(dev)->hpdi_iobase + offset);
+}
+
+static int di_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ uint32_t bits;
+ unsigned long flags;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+
+ hpdi_writel(dev, RX_FIFO_RESET_BIT, BOARD_CONTROL_REG);
+
+ DEBUG_PRINT("hpdi: in di_cmd\n");
+
+ abort_dma(dev, 0);
+
+ priv(dev)->dma_desc_index = 0;
+
+ /* These register are supposedly unused during chained dma,
+ * but I have found that left over values from last operation
+ * occasionally cause problems with transfer of first dma
+ * block. Initializing them to zero seems to fix the problem. */
+ writel(0, priv(dev)->plx9080_iobase + PLX_DMA0_TRANSFER_SIZE_REG);
+ writel(0, priv(dev)->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG);
+ writel(0, priv(dev)->plx9080_iobase + PLX_DMA0_LOCAL_ADDRESS_REG);
+ // give location of first dma descriptor
+ bits = priv(dev)->
+ dma_desc_phys_addr | PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT |
+ PLX_XFER_LOCAL_TO_PCI;
+ writel(bits, priv(dev)->plx9080_iobase + PLX_DMA0_DESCRIPTOR_REG);
+
+ // spinlock for plx dma control/status reg
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ // enable dma transfer
+ writeb(PLX_DMA_EN_BIT | PLX_DMA_START_BIT | PLX_CLEAR_DMA_INTR_BIT,
+ priv(dev)->plx9080_iobase + PLX_DMA0_CS_REG);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ priv(dev)->dio_count = cmd->stop_arg;
+ else
+ priv(dev)->dio_count = 1;
+
+ // clear over/under run status flags
+ writel(RX_UNDERRUN_BIT | RX_OVERRUN_BIT,
+ priv(dev)->hpdi_iobase + BOARD_STATUS_REG);
+ // enable interrupts
+ writel(intr_bit(RX_FULL_INTR),
+ priv(dev)->hpdi_iobase + INTERRUPT_CONTROL_REG);
+
+ DEBUG_PRINT("hpdi: starting rx\n");
+ hpdi_writel(dev, RX_ENABLE_BIT, BOARD_CONTROL_REG);
+
+ return 0;
+}
+
+static int hpdi_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ if (priv(dev)->dio_config_output) {
+ return -EINVAL;
+ } else
+ return di_cmd(dev, s);
+}
+
+static void drain_dma_buffers(struct comedi_device * dev, unsigned int channel)
+{
+ struct comedi_async *async = dev->read_subdev->async;
+ uint32_t next_transfer_addr;
+ int j;
+ int num_samples = 0;
+ void *pci_addr_reg;
+
+ if (channel)
+ pci_addr_reg =
+ priv(dev)->plx9080_iobase + PLX_DMA1_PCI_ADDRESS_REG;
+ else
+ pci_addr_reg =
+ priv(dev)->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG;
+
+ // loop until we have read all the full buffers
+ j = 0;
+ for (next_transfer_addr = readl(pci_addr_reg);
+ (next_transfer_addr <
+ le32_to_cpu(priv(dev)->dma_desc[priv(dev)->
+ dma_desc_index].pci_start_addr)
+ || next_transfer_addr >=
+ le32_to_cpu(priv(dev)->dma_desc[priv(dev)->
+ dma_desc_index].pci_start_addr) +
+ priv(dev)->block_size)
+ && j < priv(dev)->num_dma_descriptors; j++) {
+ // transfer data from dma buffer to comedi buffer
+ num_samples = priv(dev)->block_size / sizeof(uint32_t);
+ if (async->cmd.stop_src == TRIG_COUNT) {
+ if (num_samples > priv(dev)->dio_count)
+ num_samples = priv(dev)->dio_count;
+ priv(dev)->dio_count -= num_samples;
+ }
+ cfc_write_array_to_buffer(dev->read_subdev,
+ priv(dev)->desc_dio_buffer[priv(dev)->dma_desc_index],
+ num_samples * sizeof(uint32_t));
+ priv(dev)->dma_desc_index++;
+ priv(dev)->dma_desc_index %= priv(dev)->num_dma_descriptors;
+
+ DEBUG_PRINT("next desc addr 0x%lx\n", (unsigned long)
+ priv(dev)->dma_desc[priv(dev)->dma_desc_index].next);
+ DEBUG_PRINT("pci addr reg 0x%x\n", next_transfer_addr);
+ }
+ // XXX check for buffer overrun somehow
+}
+
+static irqreturn_t handle_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ uint32_t hpdi_intr_status, hpdi_board_status;
+ uint32_t plx_status;
+ uint32_t plx_bits;
+ uint8_t dma0_status, dma1_status;
+ unsigned long flags;
+
+ if (!dev->attached) {
+ return IRQ_NONE;
+ }
+
+ plx_status = readl(priv(dev)->plx9080_iobase + PLX_INTRCS_REG);
+ if ((plx_status & (ICS_DMA0_A | ICS_DMA1_A | ICS_LIA)) == 0) {
+ return IRQ_NONE;
+ }
+
+ hpdi_intr_status = readl(priv(dev)->hpdi_iobase + INTERRUPT_STATUS_REG);
+ hpdi_board_status = readl(priv(dev)->hpdi_iobase + BOARD_STATUS_REG);
+
+ async->events = 0;
+
+ if (hpdi_intr_status) {
+ DEBUG_PRINT("hpdi: intr status 0x%x, ", hpdi_intr_status);
+ writel(hpdi_intr_status,
+ priv(dev)->hpdi_iobase + INTERRUPT_STATUS_REG);
+ }
+ // spin lock makes sure noone else changes plx dma control reg
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ dma0_status = readb(priv(dev)->plx9080_iobase + PLX_DMA0_CS_REG);
+ if (plx_status & ICS_DMA0_A) { // dma chan 0 interrupt
+ writeb((dma0_status & PLX_DMA_EN_BIT) | PLX_CLEAR_DMA_INTR_BIT,
+ priv(dev)->plx9080_iobase + PLX_DMA0_CS_REG);
+
+ DEBUG_PRINT("dma0 status 0x%x\n", dma0_status);
+ if (dma0_status & PLX_DMA_EN_BIT) {
+ drain_dma_buffers(dev, 0);
+ }
+ DEBUG_PRINT(" cleared dma ch0 interrupt\n");
+ }
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ // spin lock makes sure noone else changes plx dma control reg
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ dma1_status = readb(priv(dev)->plx9080_iobase + PLX_DMA1_CS_REG);
+ if (plx_status & ICS_DMA1_A) // XXX
+ { // dma chan 1 interrupt
+ writeb((dma1_status & PLX_DMA_EN_BIT) | PLX_CLEAR_DMA_INTR_BIT,
+ priv(dev)->plx9080_iobase + PLX_DMA1_CS_REG);
+ DEBUG_PRINT("dma1 status 0x%x\n", dma1_status);
+
+ DEBUG_PRINT(" cleared dma ch1 interrupt\n");
+ }
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ // clear possible plx9080 interrupt sources
+ if (plx_status & ICS_LDIA) { // clear local doorbell interrupt
+ plx_bits = readl(priv(dev)->plx9080_iobase + PLX_DBR_OUT_REG);
+ writel(plx_bits, priv(dev)->plx9080_iobase + PLX_DBR_OUT_REG);
+ DEBUG_PRINT(" cleared local doorbell bits 0x%x\n", plx_bits);
+ }
+
+ if (hpdi_board_status & RX_OVERRUN_BIT) {
+ comedi_error(dev, "rx fifo overrun");
+ async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ DEBUG_PRINT("dma0_status 0x%x\n",
+ (int)readb(priv(dev)->plx9080_iobase +
+ PLX_DMA0_CS_REG));
+ }
+
+ if (hpdi_board_status & RX_UNDERRUN_BIT) {
+ comedi_error(dev, "rx fifo underrun");
+ async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ }
+
+ if (priv(dev)->dio_count == 0)
+ async->events |= COMEDI_CB_EOA;
+
+ DEBUG_PRINT("board status 0x%x, ", hpdi_board_status);
+ DEBUG_PRINT("plx status 0x%x\n", plx_status);
+ if (async->events)
+ DEBUG_PRINT(" events 0x%x\n", async->events);
+
+ cfc_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+void abort_dma(struct comedi_device * dev, unsigned int channel)
+{
+ unsigned long flags;
+
+ // spinlock for plx dma control/status reg
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+
+ plx9080_abort_dma(priv(dev)->plx9080_iobase, channel);
+
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+static int hpdi_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ hpdi_writel(dev, 0, BOARD_CONTROL_REG);
+
+ writel(0, priv(dev)->hpdi_iobase + INTERRUPT_CONTROL_REG);
+
+ abort_dma(dev, 0);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/icp_multi.c b/drivers/staging/comedi/drivers/icp_multi.c
index 59144d7cb0bc..15fce0190b2f 100644
--- a/drivers/staging/comedi/drivers/icp_multi.c
+++ b/drivers/staging/comedi/drivers/icp_multi.c
@@ -61,7 +61,7 @@ Options:
#define ICP_MULTI_EXTDEBUG
-// Hardware types of the cards
+/* Hardware types of the cards */
#define TYPE_ICP_MULTI 0
#define IORANGE_ICP_MULTI 32
@@ -81,20 +81,20 @@ Options:
#define ICP_MULTI_SIZE 0x20 /* 32 bytes */
-// Define bits from ADC command/status register
+/* Define bits from ADC command/status register */
#define ADC_ST 0x0001 /* Start ADC */
#define ADC_BSY 0x0001 /* ADC busy */
#define ADC_BI 0x0010 /* Bipolar input range 1 = bipolar */
#define ADC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */
#define ADC_DI 0x0040 /* Differential input mode 1 = differential */
-// Define bits from DAC command/status register
+/* Define bits from DAC command/status register */
#define DAC_ST 0x0001 /* Start DAC */
#define DAC_BSY 0x0001 /* DAC busy */
#define DAC_BI 0x0010 /* Bipolar input range 1 = bipolar */
#define DAC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */
-// Define bits from interrupt enable/status registers
+/* Define bits from interrupt enable/status registers */
#define ADC_READY 0x0001 /* A/d conversion ready interrupt */
#define DAC_READY 0x0002 /* D/a conversion ready interrupt */
#define DOUT_ERROR 0x0004 /* Digital output error interrupt */
@@ -104,11 +104,11 @@ Options:
#define CIE2 0x0040 /* Counter 2 overrun interrupt */
#define CIE3 0x0080 /* Counter 3 overrun interrupt */
-// Useful definitions
-#define Status_IRQ 0x00ff // All interrupts
+/* Useful definitions */
+#define Status_IRQ 0x00ff /* All interrupts */
-// Define analogue range
-static const comedi_lrange range_analog = { 4, {
+/* Define analogue range */
+static const struct comedi_lrange range_analog = { 4, {
UNI_RANGE(5),
UNI_RANGE(10),
BIP_RANGE(5),
@@ -123,8 +123,8 @@ static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
Forward declarations
==============================================================================
*/
-static int icp_multi_attach(comedi_device * dev, comedi_devconfig * it);
-static int icp_multi_detach(comedi_device * dev);
+static int icp_multi_attach(struct comedi_device *dev, struct comedi_devconfig *it);
+static int icp_multi_detach(struct comedi_device *dev);
/*
==============================================================================
@@ -133,79 +133,79 @@ static int icp_multi_detach(comedi_device * dev);
*/
static unsigned short pci_list_builded = 0; /*>0 list of card is known */
-typedef struct {
- const char *name; // driver name
+struct boardtype {
+ const char *name; /* driver name */
int device_id;
- int iorange; // I/O range len
- char have_irq; // 1=card support IRQ
- char cardtype; // 0=ICP Multi
- int n_aichan; // num of A/D chans
- int n_aichand; // num of A/D chans in diff mode
- int n_aochan; // num of D/A chans
- int n_dichan; // num of DI chans
- int n_dochan; // num of DO chans
- int n_ctrs; // num of counters
- int ai_maxdata; // resolution of A/D
- int ao_maxdata; // resolution of D/A
- const comedi_lrange *rangelist_ai; // rangelist for A/D
- const char *rangecode; // range codes for programming
- const comedi_lrange *rangelist_ao; // rangelist for D/A
-} boardtype;
-
-static const boardtype boardtypes[] = {
- {"icp_multi", // Driver name
- DEVICE_ID, // PCI device ID
- IORANGE_ICP_MULTI, // I/O range length
- 1, // 1=Card supports interrupts
- TYPE_ICP_MULTI, // Card type = ICP MULTI
- 16, // Num of A/D channels
- 8, // Num of A/D channels in diff mode
- 4, // Num of D/A channels
- 16, // Num of digital inputs
- 8, // Num of digital outputs
- 4, // Num of counters
- 0x0fff, // Resolution of A/D
- 0x0fff, // Resolution of D/A
- &range_analog, // Rangelist for A/D
- range_codes_analog, // Range codes for programming
- &range_analog}, // Rangelist for D/A
+ int iorange; /* I/O range len */
+ char have_irq; /* 1=card support IRQ */
+ char cardtype; /* 0=ICP Multi */
+ int n_aichan; /* num of A/D chans */
+ int n_aichand; /* num of A/D chans in diff mode */
+ int n_aochan; /* num of D/A chans */
+ int n_dichan; /* num of DI chans */
+ int n_dochan; /* num of DO chans */
+ int n_ctrs; /* num of counters */
+ int ai_maxdata; /* resolution of A/D */
+ int ao_maxdata; /* resolution of D/A */
+ const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
+ const char *rangecode; /* range codes for programming */
+ const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */
};
-#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
+static const struct boardtype boardtypes[] = {
+ {"icp_multi", /* Driver name */
+ DEVICE_ID, /* PCI device ID */
+ IORANGE_ICP_MULTI, /* I/O range length */
+ 1, /* 1=Card supports interrupts */
+ TYPE_ICP_MULTI, /* Card type = ICP MULTI */
+ 16, /* Num of A/D channels */
+ 8, /* Num of A/D channels in diff mode */
+ 4, /* Num of D/A channels */
+ 16, /* Num of digital inputs */
+ 8, /* Num of digital outputs */
+ 4, /* Num of counters */
+ 0x0fff, /* Resolution of A/D */
+ 0x0fff, /* Resolution of D/A */
+ &range_analog, /* Rangelist for A/D */
+ range_codes_analog, /* Range codes for programming */
+ &range_analog}, /* Rangelist for D/A */
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype))
-static comedi_driver driver_icp_multi = {
+static struct comedi_driver driver_icp_multi = {
driver_name:"icp_multi",
- module:THIS_MODULE,
- attach:icp_multi_attach,
- detach:icp_multi_detach,
- num_names:n_boardtypes,
- board_name:&boardtypes[0].name,
- offset:sizeof(boardtype),
+ module : THIS_MODULE,
+ attach : icp_multi_attach,
+ detach : icp_multi_detach,
+ num_names : n_boardtypes,
+ board_name : &boardtypes[0].name,
+ offset : sizeof(struct boardtype),
};
COMEDI_INITCLEANUP(driver_icp_multi);
-typedef struct {
- struct pcilst_struct *card; // pointer to card
- char valid; // card is usable
- void *io_addr; // Pointer to mapped io address
- resource_size_t phys_iobase; // Physical io address
- unsigned int AdcCmdStatus; // ADC Command/Status register
- unsigned int DacCmdStatus; // DAC Command/Status register
- unsigned int IntEnable; // Interrupt Enable register
- unsigned int IntStatus; // Interrupt Status register
- unsigned int act_chanlist[32]; // list of scaned channel
- unsigned char act_chanlist_len; // len of scanlist
- unsigned char act_chanlist_pos; // actual position in MUX list
- unsigned int *ai_chanlist; // actaul chanlist
- sampl_t *ai_data; // data buffer
- sampl_t ao_data[4]; // data output buffer
- sampl_t di_data; // Digital input data
- unsigned int do_data; // Remember digital output data
-} icp_multi_private;
-
-#define devpriv ((icp_multi_private *)dev->private)
-#define this_board ((const boardtype *)dev->board_ptr)
+struct icp_multi_private {
+ struct pcilst_struct *card; /* pointer to card */
+ char valid; /* card is usable */
+ void *io_addr; /* Pointer to mapped io address */
+ resource_size_t phys_iobase; /* Physical io address */
+ unsigned int AdcCmdStatus; /* ADC Command/Status register */
+ unsigned int DacCmdStatus; /* DAC Command/Status register */
+ unsigned int IntEnable; /* Interrupt Enable register */
+ unsigned int IntStatus; /* Interrupt Status register */
+ unsigned int act_chanlist[32]; /* list of scaned channel */
+ unsigned char act_chanlist_len; /* len of scanlist */
+ unsigned char act_chanlist_pos; /* actual position in MUX list */
+ unsigned int *ai_chanlist; /* actaul chanlist */
+ short *ai_data; /* data buffer */
+ short ao_data[4]; /* data output buffer */
+ short di_data; /* Digital input data */
+ unsigned int do_data; /* Remember digital output data */
+};
+
+#define devpriv ((struct icp_multi_private *)dev->private)
+#define this_board ((const struct boardtype *)dev->board_ptr)
/*
==============================================================================
@@ -214,12 +214,12 @@ typedef struct {
*/
#if 0
-static int check_channel_list(comedi_device * dev, comedi_subdevice * s,
+static int check_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
unsigned int *chanlist, unsigned int n_chan);
#endif
-static void setup_channel_list(comedi_device * dev, comedi_subdevice * s,
+static void setup_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
unsigned int *chanlist, unsigned int n_chan);
-static int icp_multi_reset(comedi_device * dev);
+static int icp_multi_reset(struct comedi_device *dev);
/*
==============================================================================
@@ -236,32 +236,32 @@ static int icp_multi_reset(comedi_device * dev);
This function reads a single analogue input.
Parameters:
- comedi_device *dev Pointer to current device structure
- comedi_subdevice *s Pointer to current subdevice structure
- comedi_insn *insn Pointer to current comedi instruction
- lsampl_t *data Pointer to analogue input data
+ struct comedi_device *dev Pointer to current device structure
+ struct comedi_subdevice *s Pointer to current subdevice structure
+ struct comedi_insn *insn Pointer to current comedi instruction
+ unsigned int *data Pointer to analogue input data
Returns:int Nmuber of instructions executed
==============================================================================
*/
-static int icp_multi_insn_read_ai(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int icp_multi_insn_read_ai(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int n, timeout;
#ifdef ICP_MULTI_EXTDEBUG
printk("icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
#endif
- // Disable A/D conversion ready interrupt
+ /* Disable A/D conversion ready interrupt */
devpriv->IntEnable &= ~ADC_READY;
writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
- // Clear interrupt status
+ /* Clear interrupt status */
devpriv->IntStatus |= ADC_READY;
writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
- // Set up appropriate channel, mode and range data, for specified channel
+ /* Set up appropriate channel, mode and range data, for specified channel */
setup_channel_list(dev, s, &insn->chanspec, 1);
#ifdef ICP_MULTI_EXTDEBUG
@@ -271,7 +271,7 @@ static int icp_multi_insn_read_ai(comedi_device * dev, comedi_subdevice * s,
#endif
for (n = 0; n < insn->n; n++) {
- // Set start ADC bit
+ /* Set start ADC bit */
devpriv->AdcCmdStatus |= ADC_ST;
writew(devpriv->AdcCmdStatus,
devpriv->io_addr + ICP_MULTI_ADC_CSR);
@@ -289,7 +289,7 @@ static int icp_multi_insn_read_ai(comedi_device * dev, comedi_subdevice * s,
readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
#endif
- // Wait for conversion to complete, or get fed up waiting
+ /* Wait for conversion to complete, or get fed up waiting */
timeout = 100;
while (timeout--) {
if (!(readw(devpriv->io_addr +
@@ -307,19 +307,19 @@ static int icp_multi_insn_read_ai(comedi_device * dev, comedi_subdevice * s,
comedi_udelay(1);
}
- // If we reach here, a timeout has occurred
+ /* If we reach here, a timeout has occurred */
comedi_error(dev, "A/D insn timeout");
- // Disable interrupt
+ /* Disable interrupt */
devpriv->IntEnable &= ~ADC_READY;
writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
- // Clear interrupt status
+ /* Clear interrupt status */
devpriv->IntStatus |= ADC_READY;
writew(devpriv->IntStatus,
devpriv->io_addr + ICP_MULTI_INT_STAT);
- // Clear data received
+ /* Clear data received */
data[n] = 0;
#ifdef ICP_MULTI_EXTDEBUG
@@ -332,11 +332,11 @@ static int icp_multi_insn_read_ai(comedi_device * dev, comedi_subdevice * s,
(readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
}
- // Disable interrupt
+ /* Disable interrupt */
devpriv->IntEnable &= ~ADC_READY;
writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
- // Clear interrupt status
+ /* Clear interrupt status */
devpriv->IntStatus |= ADC_READY;
writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
@@ -355,40 +355,40 @@ static int icp_multi_insn_read_ai(comedi_device * dev, comedi_subdevice * s,
This function writes a single analogue output.
Parameters:
- comedi_device *dev Pointer to current device structure
- comedi_subdevice *s Pointer to current subdevice structure
- comedi_insn *insn Pointer to current comedi instruction
- lsampl_t *data Pointer to analogue output data
+ struct comedi_device *dev Pointer to current device structure
+ struct comedi_subdevice *s Pointer to current subdevice structure
+ struct comedi_insn *insn Pointer to current comedi instruction
+ unsigned int *data Pointer to analogue output data
Returns:int Nmuber of instructions executed
==============================================================================
*/
-static int icp_multi_insn_write_ao(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int icp_multi_insn_write_ao(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int n, chan, range, timeout;
#ifdef ICP_MULTI_EXTDEBUG
printk("icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
#endif
- // Disable D/A conversion ready interrupt
+ /* Disable D/A conversion ready interrupt */
devpriv->IntEnable &= ~DAC_READY;
writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
- // Clear interrupt status
+ /* Clear interrupt status */
devpriv->IntStatus |= DAC_READY;
writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
- // Get channel number and range
+ /* Get channel number and range */
chan = CR_CHAN(insn->chanspec);
range = CR_RANGE(insn->chanspec);
- // Set up range and channel data
- // Bit 4 = 1 : Bipolar
- // Bit 5 = 0 : 5V
- // Bit 5 = 1 : 10V
- // Bits 8-9 : Channel number
+ /* Set up range and channel data */
+ /* Bit 4 = 1 : Bipolar */
+ /* Bit 5 = 0 : 5V */
+ /* Bit 5 = 1 : 10V */
+ /* Bits 8-9 : Channel number */
devpriv->DacCmdStatus &= 0xfccf;
devpriv->DacCmdStatus |= this_board->rangecode[range];
devpriv->DacCmdStatus |= (chan << 8);
@@ -396,7 +396,7 @@ static int icp_multi_insn_write_ao(comedi_device * dev, comedi_subdevice * s,
writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
for (n = 0; n < insn->n; n++) {
- // Wait for analogue output data register to be ready for new data, or get fed up waiting
+ /* Wait for analogue output data register to be ready for new data, or get fed up waiting */
timeout = 100;
while (timeout--) {
if (!(readw(devpriv->io_addr +
@@ -414,19 +414,19 @@ static int icp_multi_insn_write_ao(comedi_device * dev, comedi_subdevice * s,
comedi_udelay(1);
}
- // If we reach here, a timeout has occurred
+ /* If we reach here, a timeout has occurred */
comedi_error(dev, "D/A insn timeout");
- // Disable interrupt
+ /* Disable interrupt */
devpriv->IntEnable &= ~DAC_READY;
writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
- // Clear interrupt status
+ /* Clear interrupt status */
devpriv->IntStatus |= DAC_READY;
writew(devpriv->IntStatus,
devpriv->io_addr + ICP_MULTI_INT_STAT);
- // Clear data received
+ /* Clear data received */
devpriv->ao_data[chan] = 0;
#ifdef ICP_MULTI_EXTDEBUG
@@ -435,16 +435,16 @@ static int icp_multi_insn_write_ao(comedi_device * dev, comedi_subdevice * s,
return -ETIME;
dac_ready:
- // Write data to analogue output data register
+ /* Write data to analogue output data register */
writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
- // Set DAC_ST bit to write the data to selected channel
+ /* Set DAC_ST bit to write the data to selected channel */
devpriv->DacCmdStatus |= DAC_ST;
writew(devpriv->DacCmdStatus,
devpriv->io_addr + ICP_MULTI_DAC_CSR);
devpriv->DacCmdStatus &= ~DAC_ST;
- // Save analogue output data
+ /* Save analogue output data */
devpriv->ao_data[chan] = data[n];
}
@@ -463,24 +463,24 @@ static int icp_multi_insn_write_ao(comedi_device * dev, comedi_subdevice * s,
This function reads a single analogue output.
Parameters:
- comedi_device *dev Pointer to current device structure
- comedi_subdevice *s Pointer to current subdevice structure
- comedi_insn *insn Pointer to current comedi instruction
- lsampl_t *data Pointer to analogue output data
+ struct comedi_device *dev Pointer to current device structure
+ struct comedi_subdevice *s Pointer to current subdevice structure
+ struct comedi_insn *insn Pointer to current comedi instruction
+ unsigned int *data Pointer to analogue output data
Returns:int Nmuber of instructions executed
==============================================================================
*/
-static int icp_multi_insn_read_ao(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int icp_multi_insn_read_ao(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int n, chan;
- // Get channel number
+ /* Get channel number */
chan = CR_CHAN(insn->chanspec);
- // Read analogue outputs
+ /* Read analogue outputs */
for (n = 0; n < insn->n; n++)
data[n] = devpriv->ao_data[chan];
@@ -496,17 +496,17 @@ static int icp_multi_insn_read_ao(comedi_device * dev, comedi_subdevice * s,
This function reads the digital inputs.
Parameters:
- comedi_device *dev Pointer to current device structure
- comedi_subdevice *s Pointer to current subdevice structure
- comedi_insn *insn Pointer to current comedi instruction
- lsampl_t *data Pointer to analogue output data
+ struct comedi_device *dev Pointer to current device structure
+ struct comedi_subdevice *s Pointer to current subdevice structure
+ struct comedi_insn *insn Pointer to current comedi instruction
+ unsigned int *data Pointer to analogue output data
Returns:int Nmuber of instructions executed
==============================================================================
*/
-static int icp_multi_insn_bits_di(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int icp_multi_insn_bits_di(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
@@ -522,17 +522,17 @@ static int icp_multi_insn_bits_di(comedi_device * dev, comedi_subdevice * s,
This function writes the appropriate digital outputs.
Parameters:
- comedi_device *dev Pointer to current device structure
- comedi_subdevice *s Pointer to current subdevice structure
- comedi_insn *insn Pointer to current comedi instruction
- lsampl_t *data Pointer to analogue output data
+ struct comedi_device *dev Pointer to current device structure
+ struct comedi_subdevice *s Pointer to current subdevice structure
+ struct comedi_insn *insn Pointer to current comedi instruction
+ unsigned int *data Pointer to analogue output data
Returns:int Nmuber of instructions executed
==============================================================================
*/
-static int icp_multi_insn_bits_do(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int icp_multi_insn_bits_do(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
#ifdef ICP_MULTI_EXTDEBUG
printk("icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
@@ -564,17 +564,17 @@ static int icp_multi_insn_bits_do(comedi_device * dev, comedi_subdevice * s,
This function reads the specified counter.
Parameters:
- comedi_device *dev Pointer to current device structure
- comedi_subdevice *s Pointer to current subdevice structure
- comedi_insn *insn Pointer to current comedi instruction
- lsampl_t *data Pointer to counter data
+ struct comedi_device *dev Pointer to current device structure
+ struct comedi_subdevice *s Pointer to current subdevice structure
+ struct comedi_insn *insn Pointer to current comedi instruction
+ unsigned int *data Pointer to counter data
Returns:int Nmuber of instructions executed
==============================================================================
*/
-static int icp_multi_insn_read_ctr(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int icp_multi_insn_read_ctr(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
return 0;
}
@@ -588,17 +588,17 @@ static int icp_multi_insn_read_ctr(comedi_device * dev, comedi_subdevice * s,
This function write to the specified counter.
Parameters:
- comedi_device *dev Pointer to current device structure
- comedi_subdevice *s Pointer to current subdevice structure
- comedi_insn *insn Pointer to current comedi instruction
- lsampl_t *data Pointer to counter data
+ struct comedi_device *dev Pointer to current device structure
+ struct comedi_subdevice *s Pointer to current subdevice structure
+ struct comedi_insn *insn Pointer to current comedi instruction
+ unsigned int *data Pointer to counter data
Returns:int Nmuber of instructions executed
==============================================================================
*/
-static int icp_multi_insn_write_ctr(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int icp_multi_insn_write_ctr(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
return 0;
}
@@ -620,7 +620,7 @@ static int icp_multi_insn_write_ctr(comedi_device * dev, comedi_subdevice * s,
*/
static irqreturn_t interrupt_service_icp_multi(int irq, void *d PT_REGS_ARG)
{
- comedi_device *dev = d;
+ struct comedi_device *dev = d;
int int_no;
#ifdef ICP_MULTI_EXTDEBUG
@@ -628,10 +628,10 @@ static irqreturn_t interrupt_service_icp_multi(int irq, void *d PT_REGS_ARG)
irq);
#endif
- // Is this interrupt from our board?
+ /* Is this interrupt from our board? */
int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
if (!int_no)
- // No, exit
+ /* No, exit */
return IRQ_NONE;
#ifdef ICP_MULTI_EXTDEBUG
@@ -639,7 +639,7 @@ static irqreturn_t interrupt_service_icp_multi(int irq, void *d PT_REGS_ARG)
readw(devpriv->io_addr + ICP_MULTI_INT_STAT));
#endif
- // Determine which interrupt is active & handle it
+ /* Determine which interrupt is active & handle it */
switch (int_no) {
case ADC_READY:
break;
@@ -679,8 +679,8 @@ static irqreturn_t interrupt_service_icp_multi(int irq, void *d PT_REGS_ARG)
is built correctly
Parameters:
- comedi_device *dev Pointer to current sevice structure
- comedi_subdevice *s Pointer to current subdevice structure
+ struct comedi_device *dev Pointer to current sevice structure
+ struct comedi_subdevice *s Pointer to current subdevice structure
unsigned int *chanlist Pointer to packed channel list
unsigned int n_chan Number of channels to scan
@@ -689,7 +689,7 @@ static irqreturn_t interrupt_service_icp_multi(int irq, void *d PT_REGS_ARG)
==============================================================================
*/
-static int check_channel_list(comedi_device * dev, comedi_subdevice * s,
+static int check_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
unsigned int *chanlist, unsigned int n_chan)
{
unsigned int i;
@@ -697,14 +697,14 @@ static int check_channel_list(comedi_device * dev, comedi_subdevice * s,
#ifdef ICP_MULTI_EXTDEBUG
printk("icp multi EDBG: check_channel_list(...,%d)\n", n_chan);
#endif
- // Check that we at least have one channel to check
+ /* Check that we at least have one channel to check */
if (n_chan < 1) {
comedi_error(dev, "range/channel list is empty!");
return 0;
}
- // Check all channels
+ /* Check all channels */
for (i = 0; i < n_chan; i++) {
- // Check that channel number is < maximum
+ /* Check that channel number is < maximum */
if (CR_AREF(chanlist[i]) == AREF_DIFF) {
if (CR_CHAN(chanlist[i]) > this_board->n_aichand) {
comedi_error(dev,
@@ -734,8 +734,8 @@ static int check_channel_list(comedi_device * dev, comedi_subdevice * s,
Status register.
Parameters:
- comedi_device *dev Pointer to current sevice structure
- comedi_subdevice *s Pointer to current subdevice structure
+ struct comedi_device *dev Pointer to current sevice structure
+ struct comedi_subdevice *s Pointer to current subdevice structure
unsigned int *chanlist Pointer to packed channel list
unsigned int n_chan Number of channels to scan
@@ -743,7 +743,7 @@ static int check_channel_list(comedi_device * dev, comedi_subdevice * s,
==============================================================================
*/
-static void setup_channel_list(comedi_device * dev, comedi_subdevice * s,
+static void setup_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
unsigned int *chanlist, unsigned int n_chan)
{
unsigned int i, range, chanprog;
@@ -756,10 +756,10 @@ static void setup_channel_list(comedi_device * dev, comedi_subdevice * s,
devpriv->act_chanlist_pos = 0;
for (i = 0; i < n_chan; i++) {
- // Get channel
+ /* Get channel */
chanprog = CR_CHAN(chanlist[i]);
- // Determine if it is a differential channel (Bit 15 = 1)
+ /* Determine if it is a differential channel (Bit 15 = 1) */
if (CR_AREF(chanlist[i]) == AREF_DIFF) {
diff = 1;
chanprog &= 0x0007;
@@ -768,21 +768,21 @@ static void setup_channel_list(comedi_device * dev, comedi_subdevice * s,
chanprog &= 0x000f;
}
- // Clear channel, range and input mode bits in A/D command/status register
+ /* Clear channel, range and input mode bits in A/D command/status register */
devpriv->AdcCmdStatus &= 0xf00f;
- // Set channel number and differential mode status bit
+ /* Set channel number and differential mode status bit */
if (diff) {
- // Set channel number, bits 9-11 & mode, bit 6
+ /* Set channel number, bits 9-11 & mode, bit 6 */
devpriv->AdcCmdStatus |= (chanprog << 9);
devpriv->AdcCmdStatus |= ADC_DI;
} else
- // Set channel number, bits 8-11
+ /* Set channel number, bits 8-11 */
devpriv->AdcCmdStatus |= (chanprog << 8);
- // Get range for current channel
+ /* Get range for current channel */
range = this_board->rangecode[CR_RANGE(chanlist[i])];
- // Set range. bits 4-5
+ /* Set range. bits 4-5 */
devpriv->AdcCmdStatus |= range;
/* Output channel, range, mode to ICP Multi */
@@ -806,45 +806,45 @@ static void setup_channel_list(comedi_device * dev, comedi_subdevice * s,
This function resets the icp multi device to a 'safe' state
Parameters:
- comedi_device *dev Pointer to current sevice structure
+ struct comedi_device *dev Pointer to current sevice structure
Returns:int 0 = success
==============================================================================
*/
-static int icp_multi_reset(comedi_device * dev)
+static int icp_multi_reset(struct comedi_device *dev)
{
unsigned int i;
#ifdef ICP_MULTI_EXTDEBUG
printk("icp_multi EDBG: BGN: icp_multi_reset(...)\n");
#endif
- // Clear INT enables and requests
+ /* Clear INT enables and requests */
writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
if (this_board->n_aochan)
- // Set DACs to 0..5V range and 0V output
+ /* Set DACs to 0..5V range and 0V output */
for (i = 0; i < this_board->n_aochan; i++) {
devpriv->DacCmdStatus &= 0xfcce;
- // Set channel number
+ /* Set channel number */
devpriv->DacCmdStatus |= (i << 8);
- // Output 0V
+ /* Output 0V */
writew(0, devpriv->io_addr + ICP_MULTI_AO);
- // Set start conversion bit
+ /* Set start conversion bit */
devpriv->DacCmdStatus |= DAC_ST;
- // Output to command / status register
+ /* Output to command / status register */
writew(devpriv->DacCmdStatus,
devpriv->io_addr + ICP_MULTI_DAC_CSR);
- // Delay to allow DAC time to recover
+ /* Delay to allow DAC time to recover */
comedi_udelay(1);
}
- // Digital outputs to 0
+ /* Digital outputs to 0 */
writew(0, devpriv->io_addr + ICP_MULTI_DO);
#ifdef ICP_MULTI_EXTDEBUG
@@ -863,16 +863,16 @@ static int icp_multi_reset(comedi_device * dev)
device.
Parameters:
- comedi_device *dev Pointer to current device structure
- comedi_devconfig *it Pointer to current device configuration
+ struct comedi_device *dev Pointer to current device structure
+ struct comedi_devconfig *it Pointer to current device configuration
Returns:int 0 = success
==============================================================================
*/
-static int icp_multi_attach(comedi_device * dev, comedi_devconfig * it)
+static int icp_multi_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
- comedi_subdevice *s;
+ struct comedi_subdevice *s;
int ret, subdev, n_subdevices;
unsigned int irq;
struct pcilst_struct *card = NULL;
@@ -881,11 +881,12 @@ static int icp_multi_attach(comedi_device * dev, comedi_devconfig * it)
printk("icp_multi EDBG: BGN: icp_multi_attach(...)\n");
- // Alocate private data storage space
- if ((ret = alloc_private(dev, sizeof(icp_multi_private))) < 0)
+ /* Alocate private data storage space */
+ ret = alloc_private(dev, sizeof(struct icp_multi_private));
+ if (ret < 0)
return ret;
- // Initialise list of PCI cards in system, if not already done so
+ /* Initialise list of PCI cards in system, if not already done so */
if (pci_list_builded++ == 0) {
pci_card_list_init(PCI_VENDOR_ID_ICP,
#ifdef ICP_MULTI_EXTDEBUG
@@ -899,9 +900,11 @@ static int icp_multi_attach(comedi_device * dev, comedi_devconfig * it)
printk("Anne's comedi%d: icp_multi: board=%s", dev->minor,
this_board->name);
- if ((card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
- this_board->device_id, it->options[0],
- it->options[1])) == NULL)
+ card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
+ this_board->device_id, it->options[0],
+ it->options[1]);
+
+ if (card == NULL)
return -EIO;
devpriv->card = card;
@@ -943,9 +946,9 @@ static int icp_multi_attach(comedi_device * dev, comedi_devconfig * it)
if (this_board->n_ctrs)
n_subdevices++;
- if ((ret = alloc_subdevices(dev, n_subdevices)) < 0) {
+ ret = alloc_subdevices(dev, n_subdevices);
+ if (ret < 0)
return ret;
- }
icp_multi_reset(dev);
@@ -1055,13 +1058,13 @@ static int icp_multi_attach(comedi_device * dev, comedi_devconfig * it)
device.
Parameters:
- comedi_device *dev Pointer to current device structure
+ struct comedi_device *dev Pointer to current device structure
Returns:int 0 = success
==============================================================================
*/
-static int icp_multi_detach(comedi_device * dev)
+static int icp_multi_detach(struct comedi_device *dev)
{
if (dev->private)
@@ -1077,9 +1080,8 @@ static int icp_multi_detach(comedi_device * dev)
if (dev->private && devpriv->card)
pci_card_free(devpriv->card);
- if (--pci_list_builded == 0) {
+ if (--pci_list_builded == 0)
pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
- }
return 0;
}
diff --git a/drivers/staging/comedi/drivers/icp_multi.h b/drivers/staging/comedi/drivers/icp_multi.h
index 6df4a8d15ff2..21d84766fea8 100644
--- a/drivers/staging/comedi/drivers/icp_multi.h
+++ b/drivers/staging/comedi/drivers/icp_multi.h
@@ -28,7 +28,8 @@ struct pcilst_struct {
unsigned int irq;
};
-struct pcilst_struct *inova_devices; // ptr to root list of all Inova devices
+struct pcilst_struct *inova_devices;
+/* ptr to root list of all Inova devices */
/****************************************************************************/
@@ -150,14 +151,14 @@ static int find_free_pci_card_by_position(unsigned short vendor_id,
&& (inova->pci_slot == pci_slot)) {
if (!(inova->used)) {
*card = inova;
- return 0; // ok, card is found
+ return 0; /* ok, card is found */
} else {
- return 2; // card exist but is used
+ return 2; /* card exist but is used */
}
}
}
- return 1; // no card found
+ return 1; /* no card found */
}
/****************************************************************************/
@@ -243,9 +244,10 @@ static struct pcilst_struct *select_and_alloc_pci_card(unsigned short vendor_id,
struct pcilst_struct *card;
int err;
- if ((pci_bus < 1) & (pci_slot < 1)) { // use autodetection
- if ((card = find_free_pci_card_by_device(vendor_id,
- device_id)) == NULL) {
+ if ((pci_bus < 1) & (pci_slot < 1)) { /* use autodetection */
+
+ card = find_free_pci_card_by_device(vendor_id, device_id);
+ if (card == NULL) {
rt_printk(" - Unused card not found in system!\n");
return NULL;
}
@@ -265,7 +267,8 @@ static struct pcilst_struct *select_and_alloc_pci_card(unsigned short vendor_id,
}
}
- if ((err = pci_card_alloc(card)) != 0) {
+ err = pci_card_alloc(card);
+ if (err != 0) {
if (err > 0)
rt_printk(" - Can't allocate card!\n");
/* else: error already printed. */
diff --git a/drivers/staging/comedi/drivers/ii_pci20kc.c b/drivers/staging/comedi/drivers/ii_pci20kc.c
new file mode 100644
index 000000000000..80825ba25418
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ii_pci20kc.c
@@ -0,0 +1,612 @@
+/*
+ * comedi/drivers/ii_pci20kc.c
+ * Driver for Intelligent Instruments PCI-20001C carrier board
+ * and modules.
+ *
+ * Copyright (C) 2000 Markus Kempf <kempf@matsci.uni-sb.de>
+ * with suggestions from David Schleef
+ * 16.06.2000
+ *
+ * Linux device driver for COMEDI
+ * Intelligent Instrumentation
+ * PCI-20001 C-2A Carrier Board
+ * PCI-20341 M-1A 16-Bit analog input module
+ * - differential
+ * - range (-5V - +5V)
+ * - 16 bit
+ * PCI-20006 M-2 16-Bit analog output module
+ * - ranges (-10V - +10V) (0V - +10V) (-5V - +5V)
+ * - 16 bit
+ *
+ * only ONE PCI-20341 module possible
+ * only ONE PCI-20006 module possible
+ * no extern trigger implemented
+ *
+ * NOT WORKING (but soon) only 4 on-board differential channels supported
+ * NOT WORKING (but soon) only ONE di-port and ONE do-port supported instead of 4 digital ports
+ * di-port == Port 0
+ * do-port == Port 1
+ *
+ * The state of this driver is only a starting point for a complete
+ * COMEDI-driver. The final driver should support all features of the
+ * carrier board and modules.
+ *
+ * The test configuration:
+ *
+ * kernel 2.2.14 with RTAI v1.2 and patch-2.2.14rthal2
+ * COMEDI 0.7.45
+ * COMEDILIB 0.7.9
+ *
+ */
+/*
+Driver: ii_pci20kc
+Description: Intelligent Instruments PCI-20001C carrier board
+Author: Markus Kempf <kempf@matsci.uni-sb.de>
+Devices: [Intelligent Instrumentation] PCI-20001C (ii_pci20kc)
+Status: works
+
+Supports the PCI-20001 C-2a Carrier board, and could probably support
+the other carrier boards with small modifications. Modules supported
+are:
+ PCI-20006 M-2 16-bit analog output module
+ PCI-20341 M-1A 16-bit analog input module
+
+Options:
+ 0 Board base address
+ 1 IRQ
+ 2 first option for module 1
+ 3 second option for module 1
+ 4 first option for module 2
+ 5 second option for module 2
+ 6 first option for module 3
+ 7 second option for module 3
+
+options for PCI-20006M:
+ first: Analog output channel 0 range configuration
+ 0 bipolar 10 (-10V -- +10V)
+ 1 unipolar 10 (0V -- +10V)
+ 2 bipolar 5 (-5V -- 5V)
+ second: Analog output channel 1 range configuration
+
+options for PCI-20341M:
+ first: Analog input gain configuration
+ 0 1
+ 1 10
+ 2 100
+ 3 200
+*/
+
+/* XXX needs to use ioremap() for compatibility with 2.4 kernels. Should also
+ * check_mem_region() etc. - fmhess */
+
+#include "../comedidev.h"
+
+#define PCI20000_ID 0x1d
+#define PCI20341_ID 0x77
+#define PCI20006_ID 0xe3
+#define PCI20xxx_EMPTY_ID 0xff
+
+#define PCI20000_OFFSET 0x100
+#define PCI20000_MODULES 3
+
+#define PCI20000_DIO_0 0x80
+#define PCI20000_DIO_1 0x81
+#define PCI20000_DIO_2 0xc0
+#define PCI20000_DIO_3 0xc1
+#define PCI20000_DIO_CONTROL_01 0x83 /* port 0, 1 control */
+#define PCI20000_DIO_CONTROL_23 0xc3 /* port 2, 3 control */
+#define PCI20000_DIO_BUFFER 0x82 /* buffer direction and enable */
+#define PCI20000_DIO_EOC 0xef /* even port, control output */
+#define PCI20000_DIO_OOC 0xfd /* odd port, control output */
+#define PCI20000_DIO_EIC 0x90 /* even port, control input */
+#define PCI20000_DIO_OIC 0x82 /* odd port, control input */
+#define DIO_CAND 0x12 /* and bit 1, bit 4 of control */
+#define DIO_BE 0x01 /* buffer: port enable */
+#define DIO_BO 0x04 /* buffer: output */
+#define DIO_BI 0x05 /* buffer: input */
+#define DIO_PS_0 0x00 /* buffer: port shift 0 */
+#define DIO_PS_1 0x01 /* buffer: port shift 1 */
+#define DIO_PS_2 0x04 /* buffer: port shift 2 */
+#define DIO_PS_3 0x05 /* buffer: port shift 3 */
+
+#define PCI20006_LCHAN0 0x0d
+#define PCI20006_STROBE0 0x0b
+#define PCI20006_LCHAN1 0x15
+#define PCI20006_STROBE1 0x13
+
+#define PCI20341_INIT 0x04
+#define PCI20341_REPMODE 0x00 /* single shot mode */
+#define PCI20341_PACER 0x00 /* Hardware Pacer disabled */
+#define PCI20341_CHAN_NR 0x04 /* number of input channels */
+#define PCI20341_CONFIG_REG 0x10
+#define PCI20341_MOD_STATUS 0x01
+#define PCI20341_OPT_REG 0x11
+#define PCI20341_SET_TIME_REG 0x15
+#define PCI20341_LCHAN_ADDR_REG 0x13
+#define PCI20341_CHAN_LIST 0x80
+#define PCI20341_CC_RESET 0x1b
+#define PCI20341_CHAN_RESET 0x19
+#define PCI20341_SOFT_PACER 0x04
+#define PCI20341_STATUS_REG 0x12
+#define PCI20341_LDATA 0x02
+#define PCI20341_DAISY_CHAIN 0x20 /* On-board inputs only */
+#define PCI20341_MUX 0x04 /* Enable on-board MUX */
+#define PCI20341_SCANLIST 0x80 /* Channel/Gain Scan List */
+
+union pci20xxx_subdev_private {
+ void *iobase;
+ struct {
+ void *iobase;
+ const struct comedi_lrange *ao_range_list[2]; /* range of channels of ao module */
+ unsigned int last_data[2];
+ } pci20006;
+ struct {
+ void *iobase;
+ int timebase;
+ int settling_time;
+ int ai_gain;
+ } pci20341;
+};
+
+struct pci20xxx_private {
+
+ void *ioaddr;
+ union pci20xxx_subdev_private subdev_private[PCI20000_MODULES];
+};
+
+
+#define devpriv ((struct pci20xxx_private *)dev->private)
+#define CHAN (CR_CHAN(it->chanlist[0]))
+
+static int pci20xxx_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pci20xxx_detach(struct comedi_device * dev);
+
+static struct comedi_driver driver_pci20xxx = {
+ driver_name:"ii_pci20kc",
+ module:THIS_MODULE,
+ attach:pci20xxx_attach,
+ detach:pci20xxx_detach,
+};
+
+static int pci20006_init(struct comedi_device * dev, struct comedi_subdevice * s,
+ int opt0, int opt1);
+static int pci20341_init(struct comedi_device * dev, struct comedi_subdevice * s,
+ int opt0, int opt1);
+static int pci20xxx_dio_init(struct comedi_device * dev, struct comedi_subdevice * s);
+
+/*
+ options[0] Board base address
+ options[1] IRQ
+ options[2] first option for module 1
+ options[3] second option for module 1
+ options[4] first option for module 2
+ options[5] second option for module 2
+ options[6] first option for module 3
+ options[7] second option for module 3
+
+ options for PCI-20341M:
+ first Analog input gain configuration
+ 0 == 1
+ 1 == 10
+ 2 == 100
+ 3 == 200
+
+ options for PCI-20006M:
+ first Analog output channel 0 range configuration
+ 0 == bipolar 10 (-10V -- +10V)
+ 1 == unipolar 10V (0V -- +10V)
+ 2 == bipolar 5V (-5V -- +5V)
+ second Analog output channel 1 range configuration
+ 0 == bipolar 10 (-10V -- +10V)
+ 1 == unipolar 10V (0V -- +10V)
+ 2 == bipolar 5V (-5V -- +5V)
+*/
+static int pci20xxx_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ unsigned char i;
+ int ret;
+ int id;
+ struct comedi_subdevice *s;
+ union pci20xxx_subdev_private *sdp;
+
+ if ((ret = alloc_subdevices(dev, 1 + PCI20000_MODULES)) < 0)
+ return ret;
+ if ((ret = alloc_private(dev, sizeof(struct pci20xxx_private))) < 0)
+ return ret;
+
+ devpriv->ioaddr = (void *)(unsigned long)it->options[0];
+ dev->board_name = "pci20kc";
+
+ /* Check PCI-20001 C-2A Carrier Board ID */
+ if ((readb(devpriv->ioaddr) & PCI20000_ID) != PCI20000_ID) {
+ printk("comedi%d: ii_pci20kc", dev->minor);
+ printk(" PCI-20001 C-2A Carrier Board at base=0x%p not found !\n", devpriv->ioaddr);
+ return -EINVAL;
+ }
+ printk("comedi%d:\n", dev->minor);
+ printk("ii_pci20kc: PCI-20001 C-2A at base=0x%p\n", devpriv->ioaddr);
+
+ for (i = 0; i < PCI20000_MODULES; i++) {
+ s = dev->subdevices + i;
+ id = readb(devpriv->ioaddr + (i + 1) * PCI20000_OFFSET);
+ s->private = devpriv->subdev_private + i;
+ sdp = s->private;
+ switch (id) {
+ case PCI20006_ID:
+ sdp->pci20006.iobase =
+ devpriv->ioaddr + (i + 1) * PCI20000_OFFSET;
+ pci20006_init(dev, s, it->options[2 * i + 2],
+ it->options[2 * i + 3]);
+ printk("comedi%d: ii_pci20kc", dev->minor);
+ printk(" PCI-20006 module in slot %d \n", i + 1);
+ break;
+ case PCI20341_ID:
+ sdp->pci20341.iobase =
+ devpriv->ioaddr + (i + 1) * PCI20000_OFFSET;
+ pci20341_init(dev, s, it->options[2 * i + 2],
+ it->options[2 * i + 3]);
+ printk("comedi%d: ii_pci20kc", dev->minor);
+ printk(" PCI-20341 module in slot %d \n", i + 1);
+ break;
+ default:
+ printk("ii_pci20kc: unknown module code 0x%02x in slot %d: module disabled\n", id, i);
+ /* fall through */
+ case PCI20xxx_EMPTY_ID:
+ s->type = COMEDI_SUBD_UNUSED;
+ break;
+ }
+ }
+
+ /* initialize struct pci20xxx_private */
+ pci20xxx_dio_init(dev, dev->subdevices + PCI20000_MODULES);
+
+ return 1;
+}
+
+static int pci20xxx_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: pci20xxx: remove\n", dev->minor);
+
+ return 0;
+}
+
+/* pci20006m */
+
+static int pci20006_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int pci20006_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static const struct comedi_lrange *pci20006_range_list[] = {
+ &range_bipolar10,
+ &range_unipolar10,
+ &range_bipolar5,
+};
+
+static int pci20006_init(struct comedi_device * dev, struct comedi_subdevice * s,
+ int opt0, int opt1)
+{
+ union pci20xxx_subdev_private *sdp = s->private;
+
+ if (opt0 < 0 || opt0 > 2)
+ opt0 = 0;
+ if (opt1 < 0 || opt1 > 2)
+ opt1 = 0;
+
+ sdp->pci20006.ao_range_list[0] = pci20006_range_list[opt0];
+ sdp->pci20006.ao_range_list[1] = pci20006_range_list[opt1];
+
+ /* ao subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->len_chanlist = 2;
+ s->insn_read = pci20006_insn_read;
+ s->insn_write = pci20006_insn_write;
+ s->maxdata = 0xffff;
+ s->range_table_list = sdp->pci20006.ao_range_list;
+ return 0;
+}
+
+static int pci20006_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ union pci20xxx_subdev_private *sdp = s->private;
+
+ data[0] = sdp->pci20006.last_data[CR_CHAN(insn->chanspec)];
+
+ return 1;
+}
+
+static int pci20006_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ union pci20xxx_subdev_private *sdp = s->private;
+ int hi, lo;
+ unsigned int boarddata;
+
+ sdp->pci20006.last_data[CR_CHAN(insn->chanspec)] = data[0];
+ boarddata = (((unsigned int)data[0] + 0x8000) & 0xffff); /* comedi-data -> board-data */
+ lo = (boarddata & 0xff);
+ hi = ((boarddata >> 8) & 0xff);
+
+ switch (CR_CHAN(insn->chanspec)) {
+ case 0:
+ writeb(lo, sdp->iobase + PCI20006_LCHAN0);
+ writeb(hi, sdp->iobase + PCI20006_LCHAN0 + 1);
+ writeb(0x00, sdp->iobase + PCI20006_STROBE0);
+ break;
+ case 1:
+ writeb(lo, sdp->iobase + PCI20006_LCHAN1);
+ writeb(hi, sdp->iobase + PCI20006_LCHAN1 + 1);
+ writeb(0x00, sdp->iobase + PCI20006_STROBE1);
+ break;
+ default:
+ printk(" comedi%d: pci20xxx: ao channel Error!\n", dev->minor);
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+/* PCI20341M */
+
+static int pci20341_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static const int pci20341_timebase[] = { 0x00, 0x00, 0x00, 0x04 };
+static const int pci20341_settling_time[] = { 0x58, 0x58, 0x93, 0x99 };
+
+static const struct comedi_lrange range_bipolar0_5 = { 1, {BIP_RANGE(0.5)} };
+static const struct comedi_lrange range_bipolar0_05 = { 1, {BIP_RANGE(0.05)} };
+static const struct comedi_lrange range_bipolar0_025 = { 1, {BIP_RANGE(0.025)} };
+
+static const struct comedi_lrange *const pci20341_ranges[] = {
+ &range_bipolar5,
+ &range_bipolar0_5,
+ &range_bipolar0_05,
+ &range_bipolar0_025,
+};
+
+static int pci20341_init(struct comedi_device * dev, struct comedi_subdevice * s,
+ int opt0, int opt1)
+{
+ union pci20xxx_subdev_private *sdp = s->private;
+ int option;
+
+ /* options handling */
+ if (opt0 < 0 || opt0 > 3)
+ opt0 = 0;
+ sdp->pci20341.timebase = pci20341_timebase[opt0];
+ sdp->pci20341.settling_time = pci20341_settling_time[opt0];
+
+ /* ai subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = PCI20341_CHAN_NR;
+ s->len_chanlist = PCI20341_SCANLIST;
+ s->insn_read = pci20341_insn_read;
+ s->maxdata = 0xffff;
+ s->range_table = pci20341_ranges[opt0];
+
+ option = sdp->pci20341.timebase | PCI20341_REPMODE; /* depends on gain, trigger, repetition mode */
+
+ writeb(PCI20341_INIT, sdp->iobase + PCI20341_CONFIG_REG); /* initialize Module */
+ writeb(PCI20341_PACER, sdp->iobase + PCI20341_MOD_STATUS); /* set Pacer */
+ writeb(option, sdp->iobase + PCI20341_OPT_REG); /* option register */
+ writeb(sdp->pci20341.settling_time, sdp->iobase + PCI20341_SET_TIME_REG); /* settling time counter */
+ /* trigger not implemented */
+ return 0;
+}
+
+static int pci20341_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ union pci20xxx_subdev_private *sdp = s->private;
+ unsigned int i = 0, j = 0;
+ int lo, hi;
+ unsigned char eoc; /* end of conversion */
+ unsigned int clb; /* channel list byte */
+ unsigned int boarddata;
+
+ writeb(1, sdp->iobase + PCI20341_LCHAN_ADDR_REG); /* write number of input channels */
+ clb = PCI20341_DAISY_CHAIN | PCI20341_MUX | (sdp->pci20341.ai_gain << 3)
+ | CR_CHAN(insn->chanspec);
+ writeb(clb, sdp->iobase + PCI20341_CHAN_LIST);
+ writeb(0x00, sdp->iobase + PCI20341_CC_RESET); /* reset settling time counter and trigger delay counter */
+ writeb(0x00, sdp->iobase + PCI20341_CHAN_RESET);
+
+ /* generate Pacer */
+
+ for (i = 0; i < insn->n; i++) {
+ /* data polling isn't the niciest way to get the data, I know,
+ * but there are only 6 cycles (mean) and it is easier than
+ * the whole interrupt stuff
+ */
+ j = 0;
+ readb(sdp->iobase + PCI20341_SOFT_PACER); /* generate Pacer */
+ eoc = readb(sdp->iobase + PCI20341_STATUS_REG);
+ while ((eoc < 0x80) && j < 100) { /* poll Interrupt Flag */
+ j++;
+ eoc = readb(sdp->iobase + PCI20341_STATUS_REG);
+ }
+ if (j >= 100) {
+ printk("comedi%d: pci20xxx: AI interrupt channel %i polling exit !\n", dev->minor, i);
+ return -EINVAL;
+ }
+ lo = readb(sdp->iobase + PCI20341_LDATA);
+ hi = readb(sdp->iobase + PCI20341_LDATA + 1);
+ boarddata = lo + 0x100 * hi;
+ data[i] = (short) ((boarddata + 0x8000) & 0xffff); /* board-data -> comedi-data */
+ }
+
+ return i;
+}
+
+/* native DIO */
+
+static void pci20xxx_dio_config(struct comedi_device * dev, struct comedi_subdevice * s);
+static int pci20xxx_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int pci20xxx_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+/* initialize struct pci20xxx_private */
+static int pci20xxx_dio_init(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 32;
+ s->insn_bits = pci20xxx_dio_insn_bits;
+ s->insn_config = pci20xxx_dio_insn_config;
+ s->maxdata = 1;
+ s->len_chanlist = 32;
+ s->range_table = &range_digital;
+ s->io_bits = 0;
+
+ /* digital I/O lines default to input on board reset. */
+ pci20xxx_dio_config(dev, s);
+
+ return 0;
+}
+
+static int pci20xxx_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int mask, bits;
+
+ mask = 1 << CR_CHAN(insn->chanspec);
+ if (mask & 0x000000ff) {
+ bits = 0x000000ff;
+ } else if (mask & 0x0000ff00) {
+ bits = 0x0000ff00;
+ } else if (mask & 0x00ff0000) {
+ bits = 0x00ff0000;
+ } else {
+ bits = 0xff000000;
+ }
+ if (data[0]) {
+ s->io_bits |= bits;
+ } else {
+ s->io_bits &= ~bits;
+ }
+ pci20xxx_dio_config(dev, s);
+
+ return 1;
+}
+
+static int pci20xxx_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int mask = data[0];
+
+ s->state &= ~mask;
+ s->state |= (mask & data[1]);
+
+ mask &= s->io_bits;
+ if (mask & 0x000000ff)
+ writeb((s->state >> 0) & 0xff,
+ devpriv->ioaddr + PCI20000_DIO_0);
+ if (mask & 0x0000ff00)
+ writeb((s->state >> 8) & 0xff,
+ devpriv->ioaddr + PCI20000_DIO_1);
+ if (mask & 0x00ff0000)
+ writeb((s->state >> 16) & 0xff,
+ devpriv->ioaddr + PCI20000_DIO_2);
+ if (mask & 0xff000000)
+ writeb((s->state >> 24) & 0xff,
+ devpriv->ioaddr + PCI20000_DIO_3);
+
+ data[1] = readb(devpriv->ioaddr + PCI20000_DIO_0);
+ data[1] |= readb(devpriv->ioaddr + PCI20000_DIO_1) << 8;
+ data[1] |= readb(devpriv->ioaddr + PCI20000_DIO_2) << 16;
+ data[1] |= readb(devpriv->ioaddr + PCI20000_DIO_3) << 24;
+
+ return 2;
+}
+
+static void pci20xxx_dio_config(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned char control_01;
+ unsigned char control_23;
+ unsigned char buffer;
+
+ control_01 = readb(devpriv->ioaddr + PCI20000_DIO_CONTROL_01);
+ control_23 = readb(devpriv->ioaddr + PCI20000_DIO_CONTROL_23);
+ buffer = readb(devpriv->ioaddr + PCI20000_DIO_BUFFER);
+
+ if (s->io_bits & 0x000000ff) {
+ /* output port 0 */
+ control_01 &= PCI20000_DIO_EOC;
+ buffer = (buffer & (~(DIO_BE << DIO_PS_0))) | (DIO_BO <<
+ DIO_PS_0);
+ } else {
+ /* input port 0 */
+ control_01 = (control_01 & DIO_CAND) | PCI20000_DIO_EIC;
+ buffer = (buffer & (~(DIO_BI << DIO_PS_0)));
+ }
+ if (s->io_bits & 0x0000ff00) {
+ /* output port 1 */
+ control_01 &= PCI20000_DIO_OOC;
+ buffer = (buffer & (~(DIO_BE << DIO_PS_1))) | (DIO_BO <<
+ DIO_PS_1);
+ } else {
+ /* input port 1 */
+ control_01 = (control_01 & DIO_CAND) | PCI20000_DIO_OIC;
+ buffer = (buffer & (~(DIO_BI << DIO_PS_1)));
+ }
+ if (s->io_bits & 0x00ff0000) {
+ /* output port 2 */
+ control_23 &= PCI20000_DIO_EOC;
+ buffer = (buffer & (~(DIO_BE << DIO_PS_2))) | (DIO_BO <<
+ DIO_PS_2);
+ } else {
+ /* input port 2 */
+ control_23 = (control_23 & DIO_CAND) | PCI20000_DIO_EIC;
+ buffer = (buffer & (~(DIO_BI << DIO_PS_2)));
+ }
+ if (s->io_bits & 0xff000000) {
+ /* output port 3 */
+ control_23 &= PCI20000_DIO_OOC;
+ buffer = (buffer & (~(DIO_BE << DIO_PS_3))) | (DIO_BO <<
+ DIO_PS_3);
+ } else {
+ /* input port 3 */
+ control_23 = (control_23 & DIO_CAND) | PCI20000_DIO_OIC;
+ buffer = (buffer & (~(DIO_BI << DIO_PS_3)));
+ }
+ writeb(control_01, devpriv->ioaddr + PCI20000_DIO_CONTROL_01);
+ writeb(control_23, devpriv->ioaddr + PCI20000_DIO_CONTROL_23);
+ writeb(buffer, devpriv->ioaddr + PCI20000_DIO_BUFFER);
+}
+
+#if 0
+static void pci20xxx_do(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ /* XXX if the channel is configured for input, does this
+ do bad things? */
+ /* XXX it would be a good idea to only update the registers
+ that _need_ to be updated. This requires changes to
+ comedi, however. */
+ writeb((s->state >> 0) & 0xff, devpriv->ioaddr + PCI20000_DIO_0);
+ writeb((s->state >> 8) & 0xff, devpriv->ioaddr + PCI20000_DIO_1);
+ writeb((s->state >> 16) & 0xff, devpriv->ioaddr + PCI20000_DIO_2);
+ writeb((s->state >> 24) & 0xff, devpriv->ioaddr + PCI20000_DIO_3);
+}
+
+static unsigned int pci20xxx_di(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ /* XXX same note as above */
+ unsigned int bits;
+
+ bits = readb(devpriv->ioaddr + PCI20000_DIO_0);
+ bits |= readb(devpriv->ioaddr + PCI20000_DIO_1) << 8;
+ bits |= readb(devpriv->ioaddr + PCI20000_DIO_2) << 16;
+ bits |= readb(devpriv->ioaddr + PCI20000_DIO_3) << 24;
+
+ return bits;
+}
+#endif
+
+COMEDI_INITCLEANUP(driver_pci20xxx);
diff --git a/drivers/staging/comedi/drivers/jr3_pci.c b/drivers/staging/comedi/drivers/jr3_pci.c
new file mode 100644
index 000000000000..a3c887f3b2c6
--- /dev/null
+++ b/drivers/staging/comedi/drivers/jr3_pci.c
@@ -0,0 +1,976 @@
+/*
+ comedi/drivers/jr3_pci.c
+ hardware driver for JR3/PCI force sensor board
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2007 Anders Blomdell <anders.blomdell@control.lth.se>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: jr3_pci
+Description: JR3/PCI force sensor board
+Author: Anders Blomdell <anders.blomdell@control.lth.se>
+Status: works
+Devices: [JR3] PCI force sensor board (jr3_pci)
+
+ The DSP on the board requires initialization code, which can
+ be loaded by placing it in /lib/firmware/comedi.
+ The initialization code should be somewhere on the media you got
+ with your card. One version is available from http://www.comedi.org
+ in the comedi_nonfree_firmware tarball.
+
+ Configuration options:
+ [0] - PCI bus number - if bus number and slot number are 0,
+ then driver search for first unused card
+ [1] - PCI slot number
+
+*/
+
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/firmware.h>
+#include "comedi_pci.h"
+#include "jr3_pci.h"
+
+/* Hotplug firmware loading stuff */
+
+static void comedi_fw_release(struct device *dev)
+{
+ printk(KERN_DEBUG "firmware_sample_driver: ghost_release\n");
+}
+
+static struct device comedi_fw_device = {
+ .init_name = "comedi",
+ .release = comedi_fw_release
+};
+
+typedef int comedi_firmware_callback(struct comedi_device * dev,
+ const u8 * data, size_t size);
+
+static int comedi_load_firmware(struct comedi_device * dev,
+ char *name, comedi_firmware_callback cb)
+{
+ int result = 0;
+ const struct firmware *fw;
+ char *firmware_path;
+ static const char *prefix = "comedi/";
+
+ firmware_path = kmalloc(strlen(prefix) + strlen(name) + 1, GFP_KERNEL);
+ if (!firmware_path) {
+ result = -ENOMEM;
+ } else {
+ firmware_path[0] = '\0';
+ strcat(firmware_path, prefix);
+ strcat(firmware_path, name);
+ result = device_register(&comedi_fw_device);
+ if (result == 0) {
+ result = request_firmware(&fw, firmware_path,
+ &comedi_fw_device);
+ if (result == 0) {
+ if (!cb) {
+ result = -EINVAL;
+ } else {
+ result = cb(dev, fw->data, fw->size);
+ }
+ release_firmware(fw);
+ }
+ device_unregister(&comedi_fw_device);
+ }
+ kfree(firmware_path);
+ }
+ return result;
+}
+
+#define PCI_VENDOR_ID_JR3 0x1762
+#define PCI_DEVICE_ID_JR3_1_CHANNEL 0x3111
+#define PCI_DEVICE_ID_JR3_2_CHANNEL 0x3112
+#define PCI_DEVICE_ID_JR3_3_CHANNEL 0x3113
+#define PCI_DEVICE_ID_JR3_4_CHANNEL 0x3114
+
+static int jr3_pci_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int jr3_pci_detach(struct comedi_device * dev);
+
+static struct comedi_driver driver_jr3_pci = {
+ driver_name:"jr3_pci",
+ module:THIS_MODULE,
+ attach:jr3_pci_attach,
+ detach:jr3_pci_detach,
+};
+
+static DEFINE_PCI_DEVICE_TABLE(jr3_pci_pci_table) = {
+ {PCI_VENDOR_ID_JR3, PCI_DEVICE_ID_JR3_1_CHANNEL,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_JR3, PCI_DEVICE_ID_JR3_2_CHANNEL,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_JR3, PCI_DEVICE_ID_JR3_3_CHANNEL,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_JR3, PCI_DEVICE_ID_JR3_4_CHANNEL,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, jr3_pci_pci_table);
+
+struct jr3_pci_dev_private {
+
+ struct pci_dev *pci_dev;
+ int pci_enabled;
+ volatile struct jr3_t *iobase;
+ int n_channels;
+ struct timer_list timer;
+};
+
+
+struct poll_delay_t {
+
+ int min;
+ int max;
+};
+
+
+struct jr3_pci_subdev_private {
+ volatile struct jr3_channel *channel;
+ unsigned long next_time_min;
+ unsigned long next_time_max;
+ enum { state_jr3_poll,
+ state_jr3_init_wait_for_offset,
+ state_jr3_init_transform_complete,
+ state_jr3_init_set_full_scale_complete,
+ state_jr3_init_use_offset_complete,
+ state_jr3_done
+ } state;
+ int channel_no;
+ int serial_no;
+ int model_no;
+ struct {
+ int length;
+ struct comedi_krange range;
+ } range[9];
+ const struct comedi_lrange *range_table_list[8 * 7 + 2];
+ unsigned int maxdata_list[8 * 7 + 2];
+ u16 errors;
+ int retries;
+};
+
+static struct poll_delay_t poll_delay_min_max(int min, int max)
+{
+ struct poll_delay_t result;
+
+ result.min = min;
+ result.max = max;
+ return result;
+}
+
+static int is_complete(volatile struct jr3_channel *channel)
+{
+ return get_s16(&channel->command_word0) == 0;
+}
+
+struct transform_t {
+ struct {
+ u16 link_type;
+ s16 link_amount;
+ } link[8];
+};
+
+static void set_transforms(volatile struct jr3_channel *channel,
+ struct transform_t transf, short num)
+{
+ int i;
+
+ num &= 0x000f; // Make sure that 0 <= num <= 15
+ for (i = 0; i < 8; i++) {
+
+ set_u16(&channel->transforms[num].link[i].link_type,
+ transf.link[i].link_type);
+ comedi_udelay(1);
+ set_s16(&channel->transforms[num].link[i].link_amount,
+ transf.link[i].link_amount);
+ comedi_udelay(1);
+ if (transf.link[i].link_type == end_x_form) {
+ break;
+ }
+ }
+}
+
+static void use_transform(volatile struct jr3_channel *channel, short transf_num)
+{
+ set_s16(&channel->command_word0, 0x0500 + (transf_num & 0x000f));
+}
+
+static void use_offset(volatile struct jr3_channel *channel, short offset_num)
+{
+ set_s16(&channel->command_word0, 0x0600 + (offset_num & 0x000f));
+}
+
+static void set_offset(volatile struct jr3_channel *channel)
+{
+ set_s16(&channel->command_word0, 0x0700);
+}
+
+struct six_axis_t {
+ s16 fx;
+ s16 fy;
+ s16 fz;
+ s16 mx;
+ s16 my;
+ s16 mz;
+};
+
+static void set_full_scales(volatile struct jr3_channel *channel,
+ struct six_axis_t full_scale)
+{
+ printk("%d %d %d %d %d %d\n",
+ full_scale.fx,
+ full_scale.fy,
+ full_scale.fz, full_scale.mx, full_scale.my, full_scale.mz);
+ set_s16(&channel->full_scale.fx, full_scale.fx);
+ set_s16(&channel->full_scale.fy, full_scale.fy);
+ set_s16(&channel->full_scale.fz, full_scale.fz);
+ set_s16(&channel->full_scale.mx, full_scale.mx);
+ set_s16(&channel->full_scale.my, full_scale.my);
+ set_s16(&channel->full_scale.mz, full_scale.mz);
+ set_s16(&channel->command_word0, 0x0a00);
+}
+
+static struct six_axis_t get_min_full_scales(volatile struct jr3_channel *channel)
+{
+ struct six_axis_t result;
+ result.fx = get_s16(&channel->min_full_scale.fx);
+ result.fy = get_s16(&channel->min_full_scale.fy);
+ result.fz = get_s16(&channel->min_full_scale.fz);
+ result.mx = get_s16(&channel->min_full_scale.mx);
+ result.my = get_s16(&channel->min_full_scale.my);
+ result.mz = get_s16(&channel->min_full_scale.mz);
+ return result;
+}
+
+static struct six_axis_t get_max_full_scales(volatile struct jr3_channel *channel)
+{
+ struct six_axis_t result;
+ result.fx = get_s16(&channel->max_full_scale.fx);
+ result.fy = get_s16(&channel->max_full_scale.fy);
+ result.fz = get_s16(&channel->max_full_scale.fz);
+ result.mx = get_s16(&channel->max_full_scale.mx);
+ result.my = get_s16(&channel->max_full_scale.my);
+ result.mz = get_s16(&channel->max_full_scale.mz);
+ return result;
+}
+
+static int jr3_pci_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int result;
+ struct jr3_pci_subdev_private *p;
+ int channel;
+
+ p = s->private;
+ channel = CR_CHAN(insn->chanspec);
+ if (p == NULL || channel > 57) {
+ result = -EINVAL;
+ } else {
+ int i;
+
+ result = insn->n;
+ if (p->state != state_jr3_done ||
+ (get_u16(&p->channel->
+ errors) & (watch_dog | watch_dog2 |
+ sensor_change))) {
+ /* No sensor or sensor changed */
+ if (p->state == state_jr3_done) {
+ /* Restart polling */
+ p->state = state_jr3_poll;
+ }
+ result = -EAGAIN;
+ }
+ for (i = 0; i < insn->n; i++) {
+ if (channel < 56) {
+ int axis, filter;
+
+ axis = channel % 8;
+ filter = channel / 8;
+ if (p->state != state_jr3_done) {
+ data[i] = 0;
+ } else {
+ int F = 0;
+ switch (axis) {
+ case 0:{
+ F = get_s16(&p->
+ channel->
+ filter[filter].
+ fx);
+ }
+ break;
+ case 1:{
+ F = get_s16(&p->
+ channel->
+ filter[filter].
+ fy);
+ }
+ break;
+ case 2:{
+ F = get_s16(&p->
+ channel->
+ filter[filter].
+ fz);
+ }
+ break;
+ case 3:{
+ F = get_s16(&p->
+ channel->
+ filter[filter].
+ mx);
+ }
+ break;
+ case 4:{
+ F = get_s16(&p->
+ channel->
+ filter[filter].
+ my);
+ }
+ break;
+ case 5:{
+ F = get_s16(&p->
+ channel->
+ filter[filter].
+ mz);
+ }
+ break;
+ case 6:{
+ F = get_s16(&p->
+ channel->
+ filter[filter].
+ v1);
+ }
+ break;
+ case 7:{
+ F = get_s16(&p->
+ channel->
+ filter[filter].
+ v2);
+ }
+ break;
+ }
+ data[i] = F + 0x4000;
+ }
+ } else if (channel == 56) {
+ if (p->state != state_jr3_done) {
+ data[i] = 0;
+ } else {
+ data[i] =
+ get_u16(&p->channel->model_no);
+ }
+ } else if (channel == 57) {
+ if (p->state != state_jr3_done) {
+ data[i] = 0;
+ } else {
+ data[i] =
+ get_u16(&p->channel->serial_no);
+ }
+ }
+ }
+ }
+ return result;
+}
+
+static void jr3_pci_open(struct comedi_device * dev)
+{
+ int i;
+ struct jr3_pci_dev_private *devpriv = dev->private;
+
+ printk("jr3_pci_open\n");
+ for (i = 0; i < devpriv->n_channels; i++) {
+ struct jr3_pci_subdev_private *p;
+
+ p = dev->subdevices[i].private;
+ if (p) {
+ printk("serial: %p %d (%d)\n", p, p->serial_no,
+ p->channel_no);
+ }
+ }
+}
+
+int read_idm_word(const u8 * data, size_t size, int *pos, unsigned int *val)
+{
+ int result = 0;
+ if (pos != 0 && val != 0) {
+ // Skip over non hex
+ for (; *pos < size && !isxdigit(data[*pos]); (*pos)++) {
+ }
+ // Collect value
+ *val = 0;
+ for (; *pos < size && isxdigit(data[*pos]); (*pos)++) {
+ char ch = tolower(data[*pos]);
+ result = 1;
+ if ('0' <= ch && ch <= '9') {
+ *val = (*val << 4) + (ch - '0');
+ } else if ('a' <= ch && ch <= 'f') {
+ *val = (*val << 4) + (ch - 'a' + 10);
+ }
+ }
+ }
+ return result;
+}
+
+static int jr3_download_firmware(struct comedi_device * dev, const u8 * data,
+ size_t size)
+{
+ /*
+ * IDM file format is:
+ * { count, address, data <count> } *
+ * ffff
+ */
+ int result, more, pos, OK;
+
+ result = 0;
+ more = 1;
+ pos = 0;
+ OK = 0;
+ while (more) {
+ unsigned int count, addr;
+
+ more = more && read_idm_word(data, size, &pos, &count);
+ if (more && count == 0xffff) {
+ OK = 1;
+ break;
+ }
+ more = more && read_idm_word(data, size, &pos, &addr);
+ while (more && count > 0) {
+ unsigned int dummy;
+ more = more && read_idm_word(data, size, &pos, &dummy);
+ count--;
+ }
+ }
+
+ if (!OK) {
+ result = -ENODATA;
+ } else {
+ int i;
+ struct jr3_pci_dev_private *p = dev->private;
+
+ for (i = 0; i < p->n_channels; i++) {
+ struct jr3_pci_subdev_private *sp;
+
+ sp = dev->subdevices[i].private;
+ more = 1;
+ pos = 0;
+ while (more) {
+ unsigned int count, addr;
+ more = more
+ && read_idm_word(data, size, &pos,
+ &count);
+ if (more && count == 0xffff) {
+ break;
+ }
+ more = more
+ && read_idm_word(data, size, &pos,
+ &addr);
+ printk("Loading#%d %4.4x bytes at %4.4x\n", i,
+ count, addr);
+ while (more && count > 0) {
+ if (addr & 0x4000) {
+ // 16 bit data, never seen in real life!!
+ unsigned int data1;
+
+ more = more
+ && read_idm_word(data,
+ size, &pos, &data1);
+ count--;
+ // printk("jr3_data, not tested\n");
+ // jr3[addr + 0x20000 * pnum] = data1;
+ } else {
+ // Download 24 bit program
+ unsigned int data1, data2;
+
+ more = more
+ && read_idm_word(data,
+ size, &pos, &data1);
+ more = more
+ && read_idm_word(data,
+ size, &pos, &data2);
+ count -= 2;
+ if (more) {
+ set_u16(&p->iobase->
+ channel[i].
+ program_low
+ [addr], data1);
+ comedi_udelay(1);
+ set_u16(&p->iobase->
+ channel[i].
+ program_high
+ [addr], data2);
+ comedi_udelay(1);
+
+ }
+ }
+ addr++;
+ }
+ }
+ }
+ }
+ return result;
+}
+
+static struct poll_delay_t jr3_pci_poll_subdevice(struct comedi_subdevice * s)
+{
+ struct poll_delay_t result = poll_delay_min_max(1000, 2000);
+ struct jr3_pci_subdev_private *p = s->private;
+
+ if (p) {
+ volatile struct jr3_channel *channel = p->channel;
+ int errors = get_u16(&channel->errors);
+
+ if (errors != p->errors) {
+ printk("Errors: %x -> %x\n", p->errors, errors);
+ p->errors = errors;
+ }
+ if (errors & (watch_dog | watch_dog2 | sensor_change)) {
+ // Sensor communication lost, force poll mode
+ p->state = state_jr3_poll;
+
+ }
+ switch (p->state) {
+ case state_jr3_poll:{
+ u16 model_no = get_u16(&channel->model_no);
+ u16 serial_no = get_u16(&channel->serial_no);
+ if ((errors & (watch_dog | watch_dog2)) ||
+ model_no == 0 || serial_no == 0) {
+ // Still no sensor, keep on polling. Since it takes up to
+ // 10 seconds for offsets to stabilize, polling each
+ // second should suffice.
+ result = poll_delay_min_max(1000, 2000);
+ } else {
+ p->retries = 0;
+ p->state =
+ state_jr3_init_wait_for_offset;
+ result = poll_delay_min_max(1000, 2000);
+ }
+ }
+ break;
+ case state_jr3_init_wait_for_offset:{
+ p->retries++;
+ if (p->retries < 10) {
+ // Wait for offeset to stabilize (< 10 s according to manual)
+ result = poll_delay_min_max(1000, 2000);
+ } else {
+ struct transform_t transf;
+
+ p->model_no =
+ get_u16(&channel->model_no);
+ p->serial_no =
+ get_u16(&channel->serial_no);
+
+ printk("Setting transform for channel %d\n", p->channel_no);
+ printk("Sensor Model = %i\n",
+ p->model_no);
+ printk("Sensor Serial = %i\n",
+ p->serial_no);
+
+ // Transformation all zeros
+ transf.link[0].link_type =
+ (enum link_types)0;
+ transf.link[0].link_amount = 0;
+ transf.link[1].link_type =
+ (enum link_types)0;
+ transf.link[1].link_amount = 0;
+ transf.link[2].link_type =
+ (enum link_types)0;
+ transf.link[2].link_amount = 0;
+ transf.link[3].link_type =
+ (enum link_types)0;
+ transf.link[3].link_amount = 0;
+
+ set_transforms(channel, transf, 0);
+ use_transform(channel, 0);
+ p->state =
+ state_jr3_init_transform_complete;
+ result = poll_delay_min_max(20, 100); // Allow 20 ms for completion
+ }
+ } break;
+ case state_jr3_init_transform_complete:{
+ if (!is_complete(channel)) {
+ printk("state_jr3_init_transform_complete complete = %d\n", is_complete(channel));
+ result = poll_delay_min_max(20, 100);
+ } else {
+ // Set full scale
+ struct six_axis_t min_full_scale;
+ struct six_axis_t max_full_scale;
+
+ min_full_scale =
+ get_min_full_scales(channel);
+ printk("Obtained Min. Full Scales:\n");
+ printk("%i ", (min_full_scale).fx);
+ printk("%i ", (min_full_scale).fy);
+ printk("%i ", (min_full_scale).fz);
+ printk("%i ", (min_full_scale).mx);
+ printk("%i ", (min_full_scale).my);
+ printk("%i ", (min_full_scale).mz);
+ printk("\n");
+
+ max_full_scale =
+ get_max_full_scales(channel);
+ printk("Obtained Max. Full Scales:\n");
+ printk("%i ", (max_full_scale).fx);
+ printk("%i ", (max_full_scale).fy);
+ printk("%i ", (max_full_scale).fz);
+ printk("%i ", (max_full_scale).mx);
+ printk("%i ", (max_full_scale).my);
+ printk("%i ", (max_full_scale).mz);
+ printk("\n");
+
+ set_full_scales(channel,
+ max_full_scale);
+
+ p->state =
+ state_jr3_init_set_full_scale_complete;
+ result = poll_delay_min_max(20, 100); // Allow 20 ms for completion
+ }
+ }
+ break;
+ case state_jr3_init_set_full_scale_complete:{
+ if (!is_complete(channel)) {
+ printk("state_jr3_init_set_full_scale_complete complete = %d\n", is_complete(channel));
+ result = poll_delay_min_max(20, 100);
+ } else {
+ volatile struct force_array *full_scale;
+
+ // Use ranges in kN or we will overflow arount 2000N!
+ full_scale = &channel->full_scale;
+ p->range[0].range.min =
+ -get_s16(&full_scale->fx) *
+ 1000;
+ p->range[0].range.max =
+ get_s16(&full_scale->fx) * 1000;
+ p->range[1].range.min =
+ -get_s16(&full_scale->fy) *
+ 1000;
+ p->range[1].range.max =
+ get_s16(&full_scale->fy) * 1000;
+ p->range[2].range.min =
+ -get_s16(&full_scale->fz) *
+ 1000;
+ p->range[2].range.max =
+ get_s16(&full_scale->fz) * 1000;
+ p->range[3].range.min =
+ -get_s16(&full_scale->mx) * 100;
+ p->range[3].range.max =
+ get_s16(&full_scale->mx) * 100;
+ p->range[4].range.min =
+ -get_s16(&full_scale->my) * 100;
+ p->range[4].range.max =
+ get_s16(&full_scale->my) * 100;
+ p->range[5].range.min =
+ -get_s16(&full_scale->mz) * 100;
+ p->range[5].range.max =
+ get_s16(&full_scale->mz) * 100;
+ p->range[6].range.min = -get_s16(&full_scale->v1) * 100; // ??
+ p->range[6].range.max = get_s16(&full_scale->v1) * 100; // ??
+ p->range[7].range.min = -get_s16(&full_scale->v2) * 100; // ??
+ p->range[7].range.max = get_s16(&full_scale->v2) * 100; // ??
+ p->range[8].range.min = 0;
+ p->range[8].range.max = 65535;
+
+ {
+ int i;
+ for (i = 0; i < 9; i++) {
+ printk("%d %d - %d\n",
+ i,
+ p->range[i].
+ range.min,
+ p->range[i].
+ range.max);
+ }
+ }
+
+ use_offset(channel, 0);
+ p->state =
+ state_jr3_init_use_offset_complete;
+ result = poll_delay_min_max(40, 100); // Allow 40 ms for completion
+ }
+ }
+ break;
+ case state_jr3_init_use_offset_complete:{
+ if (!is_complete(channel)) {
+ printk("state_jr3_init_use_offset_complete complete = %d\n", is_complete(channel));
+ result = poll_delay_min_max(20, 100);
+ } else {
+ printk("Default offsets %d %d %d %d %d %d\n", get_s16(&channel->offsets.fx), get_s16(&channel->offsets.fy), get_s16(&channel->offsets.fz), get_s16(&channel->offsets.mx), get_s16(&channel->offsets.my), get_s16(&channel->offsets.mz));
+
+ set_s16(&channel->offsets.fx, 0);
+ set_s16(&channel->offsets.fy, 0);
+ set_s16(&channel->offsets.fz, 0);
+ set_s16(&channel->offsets.mx, 0);
+ set_s16(&channel->offsets.my, 0);
+ set_s16(&channel->offsets.mz, 0);
+
+ set_offset(channel);
+
+ p->state = state_jr3_done;
+ }
+ }
+ break;
+ case state_jr3_done:{
+ poll_delay_min_max(10000, 20000);
+ }
+ break;
+ default:{
+ poll_delay_min_max(1000, 2000);
+ }
+ break;
+ }
+ }
+ return result;
+}
+
+static void jr3_pci_poll_dev(unsigned long data)
+{
+ unsigned long flags;
+ struct comedi_device *dev = (struct comedi_device *) data;
+ struct jr3_pci_dev_private *devpriv = dev->private;
+ unsigned long now;
+ int delay;
+ int i;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ delay = 1000;
+ now = jiffies;
+ // Poll all channels that are ready to be polled
+ for (i = 0; i < devpriv->n_channels; i++) {
+ struct jr3_pci_subdev_private *subdevpriv = dev->subdevices[i].private;
+ if (now > subdevpriv->next_time_min) {
+ struct poll_delay_t sub_delay;
+
+ sub_delay = jr3_pci_poll_subdevice(&dev->subdevices[i]);
+ subdevpriv->next_time_min =
+ jiffies + msecs_to_jiffies(sub_delay.min);
+ subdevpriv->next_time_max =
+ jiffies + msecs_to_jiffies(sub_delay.max);
+ if (sub_delay.max && sub_delay.max < delay) {
+ // Wake up as late as possible -> poll as many channels as
+ // possible at once
+ delay = sub_delay.max;
+ }
+ }
+ }
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ devpriv->timer.expires = jiffies + msecs_to_jiffies(delay);
+ add_timer(&devpriv->timer);
+}
+
+static int jr3_pci_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int result = 0;
+ struct pci_dev *card = NULL;
+ int opt_bus, opt_slot, i;
+ struct jr3_pci_dev_private *devpriv;
+
+ printk("comedi%d: jr3_pci\n", dev->minor);
+
+ opt_bus = it->options[0];
+ opt_slot = it->options[1];
+
+ if (sizeof(struct jr3_channel) != 0xc00) {
+ printk("sizeof(struct jr3_channel) = %x [expected %x]\n",
+ (unsigned)sizeof(struct jr3_channel), 0xc00);
+ return -EINVAL;
+ }
+
+ result = alloc_private(dev, sizeof(struct jr3_pci_dev_private));
+ if (result < 0) {
+ return -ENOMEM;
+ }
+ card = NULL;
+ devpriv = dev->private;
+ init_timer(&devpriv->timer);
+ while (1) {
+ card = pci_get_device(PCI_VENDOR_ID_JR3, PCI_ANY_ID, card);
+ if (card == NULL) {
+ /* No card found */
+ break;
+ } else {
+ switch (card->device) {
+ case PCI_DEVICE_ID_JR3_1_CHANNEL:{
+ devpriv->n_channels = 1;
+ }
+ break;
+ case PCI_DEVICE_ID_JR3_2_CHANNEL:{
+ devpriv->n_channels = 2;
+ }
+ break;
+ case PCI_DEVICE_ID_JR3_3_CHANNEL:{
+ devpriv->n_channels = 3;
+ }
+ break;
+ case PCI_DEVICE_ID_JR3_4_CHANNEL:{
+ devpriv->n_channels = 4;
+ }
+ break;
+ default:{
+ devpriv->n_channels = 0;
+ }
+ }
+ if (devpriv->n_channels >= 1) {
+ if (opt_bus == 0 && opt_slot == 0) {
+ /* Take first available card */
+ break;
+ } else if (opt_bus == card->bus->number &&
+ opt_slot == PCI_SLOT(card->devfn)) {
+ /* Take requested card */
+ break;
+ }
+ }
+ }
+ }
+ if (!card) {
+ printk(" no jr3_pci found\n");
+ return -EIO;
+ } else {
+ devpriv->pci_dev = card;
+ dev->board_name = "jr3_pci";
+ }
+ if ((result = comedi_pci_enable(card, "jr3_pci")) < 0) {
+ return -EIO;
+ }
+ devpriv->pci_enabled = 1;
+ devpriv->iobase = ioremap(pci_resource_start(card, 0), sizeof(struct jr3_t));
+ result = alloc_subdevices(dev, devpriv->n_channels);
+ if (result < 0)
+ goto out;
+
+ dev->open = jr3_pci_open;
+ for (i = 0; i < devpriv->n_channels; i++) {
+ dev->subdevices[i].type = COMEDI_SUBD_AI;
+ dev->subdevices[i].subdev_flags = SDF_READABLE | SDF_GROUND;
+ dev->subdevices[i].n_chan = 8 * 7 + 2;
+ dev->subdevices[i].insn_read = jr3_pci_ai_insn_read;
+ dev->subdevices[i].private =
+ kzalloc(sizeof(struct jr3_pci_subdev_private), GFP_KERNEL);
+ if (dev->subdevices[i].private) {
+ struct jr3_pci_subdev_private *p;
+ int j;
+
+ p = dev->subdevices[i].private;
+ p->channel = &devpriv->iobase->channel[i].data;
+ printk("p->channel %p %p (%tx)\n",
+ p->channel, devpriv->iobase,
+ ((char *)(p->channel) -
+ (char *)(devpriv->iobase)));
+ p->channel_no = i;
+ for (j = 0; j < 8; j++) {
+ int k;
+
+ p->range[j].length = 1;
+ p->range[j].range.min = -1000000;
+ p->range[j].range.max = 1000000;
+ for (k = 0; k < 7; k++) {
+ p->range_table_list[j + k * 8] =
+ (struct comedi_lrange *) & p->range[j];
+ p->maxdata_list[j + k * 8] = 0x7fff;
+ }
+ }
+ p->range[8].length = 1;
+ p->range[8].range.min = 0;
+ p->range[8].range.max = 65536;
+
+ p->range_table_list[56] =
+ (struct comedi_lrange *) & p->range[8];
+ p->range_table_list[57] =
+ (struct comedi_lrange *) & p->range[8];
+ p->maxdata_list[56] = 0xffff;
+ p->maxdata_list[57] = 0xffff;
+ // Channel specific range and maxdata
+ dev->subdevices[i].range_table = 0;
+ dev->subdevices[i].range_table_list =
+ p->range_table_list;
+ dev->subdevices[i].maxdata = 0;
+ dev->subdevices[i].maxdata_list = p->maxdata_list;
+ }
+ }
+
+ // Reset DSP card
+ devpriv->iobase->channel[0].reset = 0;
+
+ result = comedi_load_firmware(dev, "jr3pci.idm", jr3_download_firmware);
+ printk("Firmare load %d\n", result);
+
+ if (result < 0) {
+ goto out;
+ }
+ // TODO: use firmware to load preferred offset tables. Suggested format:
+ // model serial Fx Fy Fz Mx My Mz\n
+ //
+ // comedi_load_firmware(dev, "jr3_offsets_table", jr3_download_firmware);
+
+ // It takes a few milliseconds for software to settle
+ // as much as we can read firmware version
+ msleep_interruptible(25);
+ for (i = 0; i < 0x18; i++) {
+ printk("%c",
+ get_u16(&devpriv->iobase->channel[0].data.
+ copyright[i]) >> 8);
+ }
+
+ // Start card timer
+ for (i = 0; i < devpriv->n_channels; i++) {
+ struct jr3_pci_subdev_private *p = dev->subdevices[i].private;
+
+ p->next_time_min = jiffies + msecs_to_jiffies(500);
+ p->next_time_max = jiffies + msecs_to_jiffies(2000);
+ }
+
+ devpriv->timer.data = (unsigned long)dev;
+ devpriv->timer.function = jr3_pci_poll_dev;
+ devpriv->timer.expires = jiffies + msecs_to_jiffies(1000);
+ add_timer(&devpriv->timer);
+
+ out:
+ return result;
+}
+
+static int jr3_pci_detach(struct comedi_device * dev)
+{
+ int i;
+ struct jr3_pci_dev_private *devpriv = dev->private;
+
+ printk("comedi%d: jr3_pci: remove\n", dev->minor);
+ if (devpriv) {
+ del_timer_sync(&devpriv->timer);
+
+ if (dev->subdevices) {
+ for (i = 0; i < devpriv->n_channels; i++) {
+ kfree(dev->subdevices[i].private);
+ }
+ }
+
+ if (devpriv->iobase) {
+ iounmap((void *)devpriv->iobase);
+ }
+ if (devpriv->pci_enabled) {
+ comedi_pci_disable(devpriv->pci_dev);
+ }
+
+ if (devpriv->pci_dev) {
+ pci_dev_put(devpriv->pci_dev);
+ }
+ }
+ return 0;
+}
+
+COMEDI_PCI_INITCLEANUP(driver_jr3_pci, jr3_pci_pci_table);
diff --git a/drivers/staging/comedi/drivers/jr3_pci.h b/drivers/staging/comedi/drivers/jr3_pci.h
new file mode 100644
index 000000000000..3585f2941bca
--- /dev/null
+++ b/drivers/staging/comedi/drivers/jr3_pci.h
@@ -0,0 +1,681 @@
+/* Helper types to take care of the fact that the DSP card memory
+ * is 16 bits, but aligned on a 32 bit PCI boundary
+ */
+
+static inline u16 get_u16(volatile const u32 * p)
+{
+ return (u16) readl(p);
+}
+
+static inline void set_u16(volatile u32 * p, u16 val)
+{
+ writel(val, p);
+}
+
+static inline s16 get_s16(volatile const s32 * p)
+{
+ return (s16) readl(p);
+}
+
+static inline void set_s16(volatile s32 * p, s16 val)
+{
+ writel(val, p);
+}
+
+/* The raw data is stored in a format which facilitates rapid
+ * processing by the JR3 DSP chip. The raw_channel structure shows the
+ * format for a single channel of data. Each channel takes four,
+ * two-byte words.
+ *
+ * Raw_time is an unsigned integer which shows the value of the JR3
+ * DSP's internal clock at the time the sample was received. The clock
+ * runs at 1/10 the JR3 DSP cycle time. JR3's slowest DSP runs at 10
+ * Mhz. At 10 Mhz raw_time would therefore clock at 1 Mhz.
+ *
+ * Raw_data is the raw data received directly from the sensor. The
+ * sensor data stream is capable of representing 16 different
+ * channels. Channel 0 shows the excitation voltage at the sensor. It
+ * is used to regulate the voltage over various cable lengths.
+ * Channels 1-6 contain the coupled force data Fx through Mz. Channel
+ * 7 contains the sensor's calibration data. The use of channels 8-15
+ * varies with different sensors.
+ */
+
+struct raw_channel {
+ u32 raw_time;
+ s32 raw_data;
+ s32 reserved[2];
+};
+
+/* The force_array structure shows the layout for the decoupled and
+ * filtered force data.
+ */
+struct force_array {
+ s32 fx;
+ s32 fy;
+ s32 fz;
+ s32 mx;
+ s32 my;
+ s32 mz;
+ s32 v1;
+ s32 v2;
+};
+
+/* The six_axis_array structure shows the layout for the offsets and
+ * the full scales.
+ */
+struct six_axis_array {
+ s32 fx;
+ s32 fy;
+ s32 fz;
+ s32 mx;
+ s32 my;
+ s32 mz;
+};
+
+/* VECT_BITS */
+/* The vect_bits structure shows the layout for indicating
+ * which axes to use in computing the vectors. Each bit signifies
+ * selection of a single axis. The V1x axis bit corresponds to a hex
+ * value of 0x0001 and the V2z bit corresponds to a hex value of
+ * 0x0020. Example: to specify the axes V1x, V1y, V2x, and V2z the
+ * pattern would be 0x002b. Vector 1 defaults to a force vector and
+ * vector 2 defaults to a moment vector. It is possible to change one
+ * or the other so that two force vectors or two moment vectors are
+ * calculated. Setting the changeV1 bit or the changeV2 bit will
+ * change that vector to be the opposite of its default. Therefore to
+ * have two force vectors, set changeV1 to 1.
+ */
+
+/* vect_bits appears to be unused at this time */
+enum {
+ fx = 0x0001,
+ fy = 0x0002,
+ fz = 0x0004,
+ mx = 0x0008,
+ my = 0x0010,
+ mz = 0x0020,
+ changeV2 = 0x0040,
+ changeV1 = 0x0080
+} vect_bits_t;
+
+/* WARNING_BITS */
+/* The warning_bits structure shows the bit pattern for the warning
+ * word. The bit fields are shown from bit 0 (lsb) to bit 15 (msb).
+ */
+
+/* XX_NEAR_SET */
+/* The xx_near_sat bits signify that the indicated axis has reached or
+ * exceeded the near saturation value.
+ */
+
+enum {
+ fx_near_sat = 0x0001,
+ fy_near_sat = 0x0002,
+ fz_near_sat = 0x0004,
+ mx_near_sat = 0x0008,
+ my_near_sat = 0x0010,
+ mz_near_sat = 0x0020
+} warning_bits_t;
+
+/* ERROR_BITS */
+/* XX_SAT */
+/* MEMORY_ERROR */
+/* SENSOR_CHANGE */
+
+/* The error_bits structure shows the bit pattern for the error word.
+ * The bit fields are shown from bit 0 (lsb) to bit 15 (msb). The
+ * xx_sat bits signify that the indicated axis has reached or exceeded
+ * the saturation value. The memory_error bit indicates that a problem
+ * was detected in the on-board RAM during the power-up
+ * initialization. The sensor_change bit indicates that a sensor other
+ * than the one originally plugged in has passed its CRC check. This
+ * bit latches, and must be reset by the user.
+ *
+ */
+
+/* SYSTEM_BUSY */
+
+/* The system_busy bit indicates that the JR3 DSP is currently busy
+ * and is not calculating force data. This occurs when a new
+ * coordinate transformation, or new sensor full scale is set by the
+ * user. A very fast system using the force data for feedback might
+ * become unstable during the approximately 4 ms needed to accomplish
+ * these calculations. This bit will also become active when a new
+ * sensor is plugged in and the system needs to recalculate the
+ * calibration CRC.
+ */
+
+/* CAL_CRC_BAD */
+
+/* The cal_crc_bad bit indicates that the calibration CRC has not
+ * calculated to zero. CRC is short for cyclic redundancy code. It is
+ * a method for determining the integrity of messages in data
+ * communication. The calibration data stored inside the sensor is
+ * transmitted to the JR3 DSP along with the sensor data. The
+ * calibration data has a CRC attached to the end of it, to assist in
+ * determining the completeness and integrity of the calibration data
+ * received from the sensor. There are two reasons the CRC may not
+ * have calculated to zero. The first is that all the calibration data
+ * has not yet been received, the second is that the calibration data
+ * has been corrupted. A typical sensor transmits the entire contents
+ * of its calibration matrix over 30 times a second. Therefore, if
+ * this bit is not zero within a couple of seconds after the sensor
+ * has been plugged in, there is a problem with the sensor's
+ * calibration data.
+ */
+
+/* WATCH_DOG */
+/* WATCH_DOG2 */
+
+/* The watch_dog and watch_dog2 bits are sensor, not processor, watch
+ * dog bits. Watch_dog indicates that the sensor data line seems to be
+ * acting correctly, while watch_dog2 indicates that sensor data and
+ * clock are being received. It is possible for watch_dog2 to go off
+ * while watch_dog does not. This would indicate an improper clock
+ * signal, while data is acting correctly. If either watch dog barks,
+ * the sensor data is not being received correctly.
+ */
+
+enum error_bits_t {
+ fx_sat = 0x0001,
+ fy_sat = 0x0002,
+ fz_sat = 0x0004,
+ mx_sat = 0x0008,
+ my_sat = 0x0010,
+ mz_sat = 0x0020,
+ memory_error = 0x0400,
+ sensor_change = 0x0800,
+ system_busy = 0x1000,
+ cal_crc_bad = 0x2000,
+ watch_dog2 = 0x4000,
+ watch_dog = 0x8000
+};
+
+/* THRESH_STRUCT */
+
+/* This structure shows the layout for a single threshold packet inside of a
+ * load envelope. Each load envelope can contain several threshold structures.
+ * 1. data_address contains the address of the data for that threshold. This
+ * includes filtered, unfiltered, raw, rate, counters, error and warning data
+ * 2. threshold is the is the value at which, if data is above or below, the
+ * bits will be set ... (pag.24).
+ * 3. bit_pattern contains the bits that will be set if the threshold value is
+ * met or exceeded.
+ */
+
+struct thresh_struct {
+ s32 data_address;
+ s32 threshold;
+ s32 bit_pattern;
+};
+
+/* LE_STRUCT */
+
+/* Layout of a load enveloped packet. Four thresholds are showed ... for more
+ * see manual (pag.25)
+ * 1. latch_bits is a bit pattern that show which bits the user wants to latch.
+ * The latched bits will not be reset once the threshold which set them is
+ * no longer true. In that case the user must reset them using the reset_bit
+ * command.
+ * 2. number_of_xx_thresholds specify how many GE/LE threshold there are.
+ */
+struct le_struct {
+ s32 latch_bits;
+ s32 number_of_ge_thresholds;
+ s32 number_of_le_thresholds;
+ struct thresh_struct thresholds[4];
+ s32 reserved;
+};
+
+/* LINK_TYPES */
+/* Link types is an enumerated value showing the different possible transform
+ * link types.
+ * 0 - end transform packet
+ * 1 - translate along X axis (TX)
+ * 2 - translate along Y axis (TY)
+ * 3 - translate along Z axis (TZ)
+ * 4 - rotate about X axis (RX)
+ * 5 - rotate about Y axis (RY)
+ * 6 - rotate about Z axis (RZ)
+ * 7 - negate all axes (NEG)
+ */
+
+enum link_types {
+ end_x_form,
+ tx,
+ ty,
+ tz,
+ rx,
+ ry,
+ rz,
+ neg
+};
+
+/* TRANSFORM */
+/* Structure used to describe a transform. */
+struct intern_transform {
+ struct {
+ u32 link_type;
+ s32 link_amount;
+ } link[8];
+};
+
+/* JR3 force/torque sensor data definition. For more information see sensor and */
+/* hardware manuals. */
+
+struct jr3_channel {
+ /* Raw_channels is the area used to store the raw data coming from */
+ /* the sensor. */
+
+ struct raw_channel raw_channels[16]; /* offset 0x0000 */
+
+ /* Copyright is a null terminated ASCII string containing the JR3 */
+ /* copyright notice. */
+
+ u32 copyright[0x0018]; /* offset 0x0040 */
+ s32 reserved1[0x0008]; /* offset 0x0058 */
+
+ /* Shunts contains the sensor shunt readings. Some JR3 sensors have
+ * the ability to have their gains adjusted. This allows the
+ * hardware full scales to be adjusted to potentially allow
+ * better resolution or dynamic range. For sensors that have
+ * this ability, the gain of each sensor channel is measured at
+ * the time of calibration using a shunt resistor. The shunt
+ * resistor is placed across one arm of the resistor bridge, and
+ * the resulting change in the output of that channel is
+ * measured. This measurement is called the shunt reading, and
+ * is recorded here. If the user has changed the gain of the //
+ * sensor, and made new shunt measurements, those shunt
+ * measurements can be placed here. The JR3 DSP will then scale
+ * the calibration matrix such so that the gains are again
+ * proper for the indicated shunt readings. If shunts is 0, then
+ * the sensor cannot have its gain changed. For details on
+ * changing the sensor gain, and making shunts readings, please
+ * see the sensor manual. To make these values take effect the
+ * user must call either command (5) use transform # (pg. 33) or
+ * command (10) set new full scales (pg. 38).
+ */
+
+ struct six_axis_array shunts; /* offset 0x0060 */
+ s32 reserved2[2]; /* offset 0x0066 */
+
+ /* Default_FS contains the full scale that is used if the user does */
+ /* not set a full scale. */
+
+ struct six_axis_array default_FS; /* offset 0x0068 */
+ s32 reserved3; /* offset 0x006e */
+
+ /* Load_envelope_num is the load envelope number that is currently
+ * in use. This value is set by the user after one of the load
+ * envelopes has been initialized.
+ */
+
+ s32 load_envelope_num; /* offset 0x006f */
+
+ /* Min_full_scale is the recommend minimum full scale. */
+
+ /* These values in conjunction with max_full_scale (pg. 9) helps
+ * determine the appropriate value for setting the full scales. The
+ * software allows the user to set the sensor full scale to an
+ * arbitrary value. But setting the full scales has some hazards. If
+ * the full scale is set too low, the data will saturate
+ * prematurely, and dynamic range will be lost. If the full scale is
+ * set too high, then resolution is lost as the data is shifted to
+ * the right and the least significant bits are lost. Therefore the
+ * maximum full scale is the maximum value at which no resolution is
+ * lost, and the minimum full scale is the value at which the data
+ * will not saturate prematurely. These values are calculated
+ * whenever a new coordinate transformation is calculated. It is
+ * possible for the recommended maximum to be less than the
+ * recommended minimum. This comes about primarily when using
+ * coordinate translations. If this is the case, it means that any
+ * full scale selection will be a compromise between dynamic range
+ * and resolution. It is usually recommended to compromise in favor
+ * of resolution which means that the recommend maximum full scale
+ * should be chosen.
+ *
+ * WARNING: Be sure that the full scale is no less than 0.4% of the
+ * recommended minimum full scale. Full scales below this value will
+ * cause erroneous results.
+ */
+
+ struct six_axis_array min_full_scale; /* offset 0x0070 */
+ s32 reserved4; /* offset 0x0076 */
+
+ /* Transform_num is the transform number that is currently in use.
+ * This value is set by the JR3 DSP after the user has used command
+ * (5) use transform # (pg. 33).
+ */
+
+ s32 transform_num; /* offset 0x0077 */
+
+ /* Max_full_scale is the recommended maximum full scale. See */
+ /* min_full_scale (pg. 9) for more details. */
+
+ struct six_axis_array max_full_scale; /* offset 0x0078 */
+ s32 reserved5; /* offset 0x007e */
+
+ /* Peak_address is the address of the data which will be monitored
+ * by the peak routine. This value is set by the user. The peak
+ * routine will monitor any 8 contiguous addresses for peak values.
+ * (ex. to watch filter3 data for peaks, set this value to 0x00a8).
+ */
+
+ s32 peak_address; /* offset 0x007f */
+
+ /* Full_scale is the sensor full scales which are currently in use.
+ * Decoupled and filtered data is scaled so that +/- 16384 is equal
+ * to the full scales. The engineering units used are indicated by
+ * the units value discussed on page 16. The full scales for Fx, Fy,
+ * Fz, Mx, My and Mz can be written by the user prior to calling
+ * command (10) set new full scales (pg. 38). The full scales for V1
+ * and V2 are set whenever the full scales are changed or when the
+ * axes used to calculate the vectors are changed. The full scale of
+ * V1 and V2 will always be equal to the largest full scale of the
+ * axes used for each vector respectively.
+ */
+
+ struct force_array full_scale; /* offset 0x0080 */
+
+ /* Offsets contains the sensor offsets. These values are subtracted from
+ * the sensor data to obtain the decoupled data. The offsets are set a
+ * few seconds (< 10) after the calibration data has been received.
+ * They are set so that the output data will be zero. These values
+ * can be written as well as read. The JR3 DSP will use the values
+ * written here within 2 ms of being written. To set future
+ * decoupled data to zero, add these values to the current decoupled
+ * data values and place the sum here. The JR3 DSP will change these
+ * values when a new transform is applied. So if the offsets are
+ * such that FX is 5 and all other values are zero, after rotating
+ * about Z by 90 degrees, FY would be 5 and all others would be zero.
+ */
+
+ struct six_axis_array offsets; /* offset 0x0088 */
+
+ /* Offset_num is the number of the offset currently in use. This
+ * value is set by the JR3 DSP after the user has executed the use
+ * offset # command (pg. 34). It can vary between 0 and 15.
+ */
+
+ s32 offset_num; /* offset 0x008e */
+
+ /* Vect_axes is a bit map showing which of the axes are being used
+ * in the vector calculations. This value is set by the JR3 DSP
+ * after the user has executed the set vector axes command (pg. 37).
+ */
+
+ u32 vect_axes; /* offset 0x008f */
+
+ /* Filter0 is the decoupled, unfiltered data from the JR3 sensor.
+ * This data has had the offsets removed.
+ *
+ * These force_arrays hold the filtered data. The decoupled data is
+ * passed through cascaded low pass filters. Each succeeding filter
+ * has a cutoff frequency of 1/4 of the preceding filter. The cutoff
+ * frequency of filter1 is 1/16 of the sample rate from the sensor.
+ * For a typical sensor with a sample rate of 8 kHz, the cutoff
+ * frequency of filter1 would be 500 Hz. The following filters would
+ * cutoff at 125 Hz, 31.25 Hz, 7.813 Hz, 1.953 Hz and 0.4883 Hz.
+ */
+
+ struct force_array filter[7]; /* offset 0x0090,
+ offset 0x0098,
+ offset 0x00a0,
+ offset 0x00a8,
+ offset 0x00b0,
+ offset 0x00b8 ,
+ offset 0x00c0 */
+
+ /* Rate_data is the calculated rate data. It is a first derivative
+ * calculation. It is calculated at a frequency specified by the
+ * variable rate_divisor (pg. 12). The data on which the rate is
+ * calculated is specified by the variable rate_address (pg. 12).
+ */
+
+ struct force_array rate_data; /* offset 0x00c8 */
+
+ /* Minimum_data & maximum_data are the minimum and maximum (peak)
+ * data values. The JR3 DSP can monitor any 8 contiguous data items
+ * for minimums and maximums at full sensor bandwidth. This area is
+ * only updated at user request. This is done so that the user does
+ * not miss any peaks. To read the data, use either the read peaks
+ * command (pg. 40), or the read and reset peaks command (pg. 39).
+ * The address of the data to watch for peaks is stored in the
+ * variable peak_address (pg. 10). Peak data is lost when executing
+ * a coordinate transformation or a full scale change. Peak data is
+ * also lost when plugging in a new sensor.
+ */
+
+ struct force_array minimum_data; /* offset 0x00d0 */
+ struct force_array maximum_data; /* offset 0x00d8 */
+
+ /* Near_sat_value & sat_value contain the value used to determine if
+ * the raw sensor is saturated. Because of decoupling and offset
+ * removal, it is difficult to tell from the processed data if the
+ * sensor is saturated. These values, in conjunction with the error
+ * and warning words (pg. 14), provide this critical information.
+ * These two values may be set by the host processor. These values
+ * are positive signed values, since the saturation logic uses the
+ * absolute values of the raw data. The near_sat_value defaults to
+ * approximately 80% of the ADC's full scale, which is 26214, while
+ * sat_value defaults to the ADC's full scale:
+ *
+ * sat_value = 32768 - 2^(16 - ADC bits)
+ */
+
+ s32 near_sat_value; /* offset 0x00e0 */
+ s32 sat_value; /* offset 0x00e1 */
+
+ /* Rate_address, rate_divisor & rate_count contain the data used to
+ * control the calculations of the rates. Rate_address is the
+ * address of the data used for the rate calculation. The JR3 DSP
+ * will calculate rates for any 8 contiguous values (ex. to
+ * calculate rates for filter3 data set rate_address to 0x00a8).
+ * Rate_divisor is how often the rate is calculated. If rate_divisor
+ * is 1, the rates are calculated at full sensor bandwidth. If
+ * rate_divisor is 200, rates are calculated every 200 samples.
+ * Rate_divisor can be any value between 1 and 65536. Set
+ * rate_divisor to 0 to calculate rates every 65536 samples.
+ * Rate_count starts at zero and counts until it equals
+ * rate_divisor, at which point the rates are calculated, and
+ * rate_count is reset to 0. When setting a new rate divisor, it is
+ * a good idea to set rate_count to one less than rate divisor. This
+ * will minimize the time necessary to start the rate calculations.
+ */
+
+ s32 rate_address; /* offset 0x00e2 */
+ u32 rate_divisor; /* offset 0x00e3 */
+ u32 rate_count; /* offset 0x00e4 */
+
+ /* Command_word2 through command_word0 are the locations used to
+ * send commands to the JR3 DSP. Their usage varies with the command
+ * and is detailed later in the Command Definitions section (pg.
+ * 29). In general the user places values into various memory
+ * locations, and then places the command word into command_word0.
+ * The JR3 DSP will process the command and place a 0 into
+ * command_word0 to indicate successful completion. Alternatively
+ * the JR3 DSP will place a negative number into command_word0 to
+ * indicate an error condition. Please note the command locations
+ * are numbered backwards. (I.E. command_word2 comes before
+ * command_word1).
+ */
+
+ s32 command_word2; /* offset 0x00e5 */
+ s32 command_word1; /* offset 0x00e6 */
+ s32 command_word0; /* offset 0x00e7 */
+
+ /* Count1 through count6 are unsigned counters which are incremented
+ * every time the matching filters are calculated. Filter1 is
+ * calculated at the sensor data bandwidth. So this counter would
+ * increment at 8 kHz for a typical sensor. The rest of the counters
+ * are incremented at 1/4 the interval of the counter immediately
+ * preceding it, so they would count at 2 kHz, 500 Hz, 125 Hz etc.
+ * These counters can be used to wait for data. Each time the
+ * counter changes, the corresponding data set can be sampled, and
+ * this will insure that the user gets each sample, once, and only
+ * once.
+ */
+
+ u32 count1; /* offset 0x00e8 */
+ u32 count2; /* offset 0x00e9 */
+ u32 count3; /* offset 0x00ea */
+ u32 count4; /* offset 0x00eb */
+ u32 count5; /* offset 0x00ec */
+ u32 count6; /* offset 0x00ed */
+
+ /* Error_count is a running count of data reception errors. If this
+ * counter is changing rapidly, it probably indicates a bad sensor
+ * cable connection or other hardware problem. In most installations
+ * error_count should not change at all. But it is possible in an
+ * extremely noisy environment to experience occasional errors even
+ * without a hardware problem. If the sensor is well grounded, this
+ * is probably unavoidable in these environments. On the occasions
+ * where this counter counts a bad sample, that sample is ignored.
+ */
+
+ u32 error_count; /* offset 0x00ee */
+
+ /* Count_x is a counter which is incremented every time the JR3 DSP
+ * searches its job queues and finds nothing to do. It indicates the
+ * amount of idle time the JR3 DSP has available. It can also be
+ * used to determine if the JR3 DSP is alive. See the Performance
+ * Issues section on pg. 49 for more details.
+ */
+
+ u32 count_x; /* offset 0x00ef */
+
+ /* Warnings & errors contain the warning and error bits
+ * respectively. The format of these two words is discussed on page
+ * 21 under the headings warnings_bits and error_bits.
+ */
+
+ u32 warnings; /* offset 0x00f0 */
+ u32 errors; /* offset 0x00f1 */
+
+ /* Threshold_bits is a word containing the bits that are set by the
+ * load envelopes. See load_envelopes (pg. 17) and thresh_struct
+ * (pg. 23) for more details.
+ */
+
+ s32 threshold_bits; /* offset 0x00f2 */
+
+ /* Last_crc is the value that shows the actual calculated CRC. CRC
+ * is short for cyclic redundancy code. It should be zero. See the
+ * description for cal_crc_bad (pg. 21) for more information.
+ */
+
+ s32 last_CRC; /* offset 0x00f3 */
+
+ /* EEProm_ver_no contains the version number of the sensor EEProm.
+ * EEProm version numbers can vary between 0 and 255.
+ * Software_ver_no contains the software version number. Version
+ * 3.02 would be stored as 302.
+ */
+
+ s32 eeprom_ver_no; /* offset 0x00f4 */
+ s32 software_ver_no; /* offset 0x00f5 */
+
+ /* Software_day & software_year are the release date of the software
+ * the JR3 DSP is currently running. Day is the day of the year,
+ * with January 1 being 1, and December 31, being 365 for non leap
+ * years.
+ */
+
+ s32 software_day; /* offset 0x00f6 */
+ s32 software_year; /* offset 0x00f7 */
+
+ /* Serial_no & model_no are the two values which uniquely identify a
+ * sensor. This model number does not directly correspond to the JR3
+ * model number, but it will provide a unique identifier for
+ * different sensor configurations.
+ */
+
+ u32 serial_no; /* offset 0x00f8 */
+ u32 model_no; /* offset 0x00f9 */
+
+ /* Cal_day & cal_year are the sensor calibration date. Day is the
+ * day of the year, with January 1 being 1, and December 31, being
+ * 366 for leap years.
+ */
+
+ s32 cal_day; /* offset 0x00fa */
+ s32 cal_year; /* offset 0x00fb */
+
+ /* Units is an enumerated read only value defining the engineering
+ * units used in the sensor full scale. The meanings of particular
+ * values are discussed in the section detailing the force_units
+ * structure on page 22. The engineering units are setto customer
+ * specifications during sensor manufacture and cannot be changed by
+ * writing to Units.
+ *
+ * Bits contains the number of bits of resolution of the ADC
+ * currently in use.
+ *
+ * Channels is a bit field showing which channels the current sensor
+ * is capable of sending. If bit 0 is active, this sensor can send
+ * channel 0, if bit 13 is active, this sensor can send channel 13,
+ * etc. This bit can be active, even if the sensor is not currently
+ * sending this channel. Some sensors are configurable as to which
+ * channels to send, and this field only contains information on the
+ * channels available to send, not on the current configuration. To
+ * find which channels are currently being sent, monitor the
+ * Raw_time fields (pg. 19) in the raw_channels array (pg. 7). If
+ * the time is changing periodically, then that channel is being
+ * received.
+ */
+
+ u32 units; /* offset 0x00fc */
+ s32 bits; /* offset 0x00fd */
+ s32 channels; /* offset 0x00fe */
+
+ /* Thickness specifies the overall thickness of the sensor from
+ * flange to flange. The engineering units for this value are
+ * contained in units (pg. 16). The sensor calibration is relative
+ * to the center of the sensor. This value allows easy coordinate
+ * transformation from the center of the sensor to either flange.
+ */
+
+ s32 thickness; /* offset 0x00ff */
+
+ /* Load_envelopes is a table containing the load envelope
+ * descriptions. There are 16 possible load envelope slots in the
+ * table. The slots are on 16 word boundaries and are numbered 0-15.
+ * Each load envelope needs to start at the beginning of a slot but
+ * need not be fully contained in that slot. That is to say that a
+ * single load envelope can be larger than a single slot. The
+ * software has been tested and ran satisfactorily with 50
+ * thresholds active. A single load envelope this large would take
+ * up 5 of the 16 slots. The load envelope data is laid out in an
+ * order that is most efficient for the JR3 DSP. The structure is
+ * detailed later in the section showing the definition of the
+ * le_struct structure (pg. 23).
+ */
+
+ struct le_struct load_envelopes[0x10]; /* offset 0x0100 */
+
+ /* Transforms is a table containing the transform descriptions.
+ * There are 16 possible transform slots in the table. The slots are
+ * on 16 word boundaries and are numbered 0-15. Each transform needs
+ * to start at the beginning of a slot but need not be fully
+ * contained in that slot. That is to say that a single transform
+ * can be larger than a single slot. A transform is 2 * no of links
+ * + 1 words in length. So a single slot can contain a transform
+ * with 7 links. Two slots can contain a transform that is 15 links.
+ * The layout is detailed later in the section showing the
+ * definition of the transform structure (pg. 26).
+ */
+
+ struct intern_transform transforms[0x10]; /* offset 0x0200 */
+};
+
+struct jr3_t {
+ struct {
+ u32 program_low[0x4000]; /* 0x00000 - 0x10000 */
+ struct jr3_channel data; /* 0x10000 - 0x10c00 */
+ char pad2[0x30000 - 0x00c00]; /* 0x10c00 - 0x40000 */
+ u32 program_high[0x8000]; /* 0x40000 - 0x60000 */
+ u32 reset; /* 0x60000 - 0x60004 */
+ char pad3[0x20000 - 0x00004]; /* 0x60004 - 0x80000 */
+ } channel[4];
+};
diff --git a/drivers/staging/comedi/drivers/ke_counter.c b/drivers/staging/comedi/drivers/ke_counter.c
new file mode 100644
index 000000000000..79f6fe5646c5
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ke_counter.c
@@ -0,0 +1,254 @@
+/*
+ comedi/drivers/ke_counter.c
+ Comedi driver for Kolter-Electronic PCI Counter 1 Card
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: ke_counter
+Description: Driver for Kolter Electronic Counter Card
+Devices: [Kolter Electronic] PCI Counter Card (ke_counter)
+Author: Michael Hillmann
+Updated: Mon, 14 Apr 2008 15:42:42 +0100
+Status: tested
+
+Configuration Options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first supported
+ PCI device found will be used.
+
+This driver is a simple driver to read the counter values from
+Kolter Electronic PCI Counter Card.
+*/
+
+#include "../comedidev.h"
+
+#include "comedi_pci.h"
+
+#define CNT_DRIVER_NAME "ke_counter"
+#define PCI_VENDOR_ID_KOLTER 0x1001
+#define CNT_CARD_DEVICE_ID 0x0014
+
+/*-- function prototypes ----------------------------------------------------*/
+
+static int cnt_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int cnt_detach(struct comedi_device * dev);
+
+static DEFINE_PCI_DEVICE_TABLE(cnt_pci_table) = {
+ {PCI_VENDOR_ID_KOLTER, CNT_CARD_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, cnt_pci_table);
+
+/*-- board specification structure ------------------------------------------*/
+
+struct cnt_board_struct {
+
+ const char *name;
+ int device_id;
+ int cnt_channel_nbr;
+ int cnt_bits;
+};
+
+
+static const struct cnt_board_struct cnt_boards[] = {
+ {
+ name: CNT_DRIVER_NAME,
+ device_id:CNT_CARD_DEVICE_ID,
+ cnt_channel_nbr:3,
+ cnt_bits:24}
+};
+
+#define cnt_board_nbr (sizeof(cnt_boards)/sizeof(struct cnt_board_struct))
+
+/*-- device private structure -----------------------------------------------*/
+
+struct cnt_device_private {
+
+ struct pci_dev *pcidev;
+};
+
+
+#define devpriv ((struct cnt_device_private *)dev->private)
+
+static struct comedi_driver cnt_driver = {
+ driver_name:CNT_DRIVER_NAME,
+ module:THIS_MODULE,
+ attach:cnt_attach,
+ detach:cnt_detach,
+};
+
+COMEDI_PCI_INITCLEANUP(cnt_driver, cnt_pci_table);
+
+/*-- counter write ----------------------------------------------------------*/
+
+/* This should be used only for resetting the counters; maybe it is better
+ to make a special command 'reset'. */
+static int cnt_winsn(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+
+ outb((unsigned char)((data[0] >> 24) & 0xff),
+ dev->iobase + chan * 0x20 + 0x10);
+ outb((unsigned char)((data[0] >> 16) & 0xff),
+ dev->iobase + chan * 0x20 + 0x0c);
+ outb((unsigned char)((data[0] >> 8) & 0xff),
+ dev->iobase + chan * 0x20 + 0x08);
+ outb((unsigned char)((data[0] >> 0) & 0xff),
+ dev->iobase + chan * 0x20 + 0x04);
+
+ /* return the number of samples written */
+ return 1;
+}
+
+/*-- counter read -----------------------------------------------------------*/
+
+static int cnt_rinsn(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned char a0, a1, a2, a3, a4;
+ int chan = CR_CHAN(insn->chanspec);
+ int result;
+
+ a0 = inb(dev->iobase + chan * 0x20);
+ a1 = inb(dev->iobase + chan * 0x20 + 0x04);
+ a2 = inb(dev->iobase + chan * 0x20 + 0x08);
+ a3 = inb(dev->iobase + chan * 0x20 + 0x0c);
+ a4 = inb(dev->iobase + chan * 0x20 + 0x10);
+
+ result = (a1 + (a2 * 256) + (a3 * 65536));
+ if (a4 > 0)
+ result = result - s->maxdata;
+
+ *data = (unsigned int) result;
+
+ /* return the number of samples read */
+ return 1;
+}
+
+/*-- attach -----------------------------------------------------------------*/
+
+static int cnt_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *subdevice;
+ struct pci_dev *pci_device;
+ struct cnt_board_struct *board;
+ unsigned long io_base;
+ int error, i;
+
+ /* allocate device private structure */
+ if ((error = alloc_private(dev, sizeof(struct cnt_device_private))) < 0) {
+ return error;
+ }
+
+ /* Probe the device to determine what device in the series it is. */
+ for (pci_device = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ pci_device != NULL;
+ pci_device =
+ pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_device)) {
+ if (pci_device->vendor == PCI_VENDOR_ID_KOLTER) {
+ for (i = 0; i < cnt_board_nbr; i++) {
+ if (cnt_boards[i].device_id ==
+ pci_device->device) {
+ /* was a particular bus/slot requested? */
+ if ((it->options[0] != 0)
+ || (it->options[1] != 0)) {
+ /* are we on the wrong bus/slot? */
+ if (pci_device->bus->number !=
+ it->options[0]
+ || PCI_SLOT(pci_device->
+ devfn) !=
+ it->options[1]) {
+ continue;
+ }
+ }
+
+ dev->board_ptr = cnt_boards + i;
+ board = (struct cnt_board_struct *) dev->
+ board_ptr;
+ goto found;
+ }
+ }
+ }
+ }
+ printk("comedi%d: no supported board found! (req. bus/slot: %d/%d)\n",
+ dev->minor, it->options[0], it->options[1]);
+ return -EIO;
+
+ found:
+ printk("comedi%d: found %s at PCI bus %d, slot %d\n", dev->minor,
+ board->name, pci_device->bus->number,
+ PCI_SLOT(pci_device->devfn));
+ devpriv->pcidev = pci_device;
+ dev->board_name = board->name;
+
+ /* enable PCI device and request regions */
+ if ((error = comedi_pci_enable(pci_device, CNT_DRIVER_NAME)) < 0) {
+ printk("comedi%d: failed to enable PCI device and request regions!\n", dev->minor);
+ return error;
+ }
+
+ /* read register base address [PCI_BASE_ADDRESS #0] */
+ io_base = pci_resource_start(pci_device, 0);
+ dev->iobase = io_base;
+
+ /* allocate the subdevice structures */
+ if ((error = alloc_subdevices(dev, 1)) < 0) {
+ return error;
+ }
+
+ subdevice = dev->subdevices + 0;
+ dev->read_subdev = subdevice;
+
+ subdevice->type = COMEDI_SUBD_COUNTER;
+ subdevice->subdev_flags = SDF_READABLE /* | SDF_COMMON */ ;
+ subdevice->n_chan = board->cnt_channel_nbr;
+ subdevice->maxdata = (1 << board->cnt_bits) - 1;
+ subdevice->insn_read = cnt_rinsn;
+ subdevice->insn_write = cnt_winsn;
+
+ // select 20MHz clock
+ outb(3, dev->iobase + 248);
+
+ // reset all counters
+ outb(0, dev->iobase);
+ outb(0, dev->iobase + 0x20);
+ outb(0, dev->iobase + 0x40);
+
+ printk("comedi%d: " CNT_DRIVER_NAME " attached.\n", dev->minor);
+ return 0;
+}
+
+/*-- detach -----------------------------------------------------------------*/
+
+static int cnt_detach(struct comedi_device * dev)
+{
+ if (devpriv && devpriv->pcidev) {
+ if (dev->iobase) {
+ comedi_pci_disable(devpriv->pcidev);
+ }
+ pci_dev_put(devpriv->pcidev);
+ }
+ printk("comedi%d: " CNT_DRIVER_NAME " remove\n", dev->minor);
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/me4000.c b/drivers/staging/comedi/drivers/me4000.c
index b432aa7d7644..12481a032b9f 100644
--- a/drivers/staging/comedi/drivers/me4000.c
+++ b/drivers/staging/comedi/drivers/me4000.c
@@ -92,7 +92,7 @@ static DEFINE_PCI_DEVICE_TABLE(me4000_pci_table) = {
MODULE_DEVICE_TABLE(pci, me4000_pci_table);
-static const me4000_board_t me4000_boards[] = {
+static const struct me4000_board me4000_boards[] = {
{"ME-4650", 0x4650, {0, 0}, {16, 0, 0, 0}, {4}, {0}},
{"ME-4660", 0x4660, {0, 0}, {32, 0, 16, 0}, {4}, {3}},
@@ -113,108 +113,108 @@ static const me4000_board_t me4000_boards[] = {
{0},
};
-#define ME4000_BOARD_VERSIONS (sizeof(me4000_boards) / sizeof(me4000_board_t) - 1)
+#define ME4000_BOARD_VERSIONS (sizeof(me4000_boards) / sizeof(struct me4000_board) - 1)
/*-----------------------------------------------------------------------------
Comedi function prototypes
---------------------------------------------------------------------------*/
-static int me4000_attach(comedi_device * dev, comedi_devconfig * it);
-static int me4000_detach(comedi_device * dev);
-static comedi_driver driver_me4000 = {
+static int me4000_attach(struct comedi_device *dev, struct comedi_devconfig *it);
+static int me4000_detach(struct comedi_device *dev);
+static struct comedi_driver driver_me4000 = {
driver_name:"me4000",
- module:THIS_MODULE,
- attach:me4000_attach,
- detach:me4000_detach,
+ module : THIS_MODULE,
+ attach : me4000_attach,
+ detach : me4000_detach,
};
/*-----------------------------------------------------------------------------
Meilhaus function prototypes
---------------------------------------------------------------------------*/
-static int me4000_probe(comedi_device * dev, comedi_devconfig * it);
-static int get_registers(comedi_device * dev, struct pci_dev *pci_dev_p);
-static int init_board_info(comedi_device * dev, struct pci_dev *pci_dev_p);
-static int init_ao_context(comedi_device * dev);
-static int init_ai_context(comedi_device * dev);
-static int init_dio_context(comedi_device * dev);
-static int init_cnt_context(comedi_device * dev);
-static int xilinx_download(comedi_device * dev);
-static int reset_board(comedi_device * dev);
+static int me4000_probe(struct comedi_device *dev, struct comedi_devconfig *it);
+static int get_registers(struct comedi_device *dev, struct pci_dev *pci_dev_p);
+static int init_board_info(struct comedi_device *dev, struct pci_dev *pci_dev_p);
+static int init_ao_context(struct comedi_device *dev);
+static int init_ai_context(struct comedi_device *dev);
+static int init_dio_context(struct comedi_device *dev);
+static int init_cnt_context(struct comedi_device *dev);
+static int xilinx_download(struct comedi_device *dev);
+static int reset_board(struct comedi_device *dev);
-static int me4000_dio_insn_bits(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data);
+static int me4000_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data);
-static int me4000_dio_insn_config(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data);
+static int me4000_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data);
-static int cnt_reset(comedi_device * dev, unsigned int channel);
+static int cnt_reset(struct comedi_device *dev, unsigned int channel);
-static int cnt_config(comedi_device * dev,
+static int cnt_config(struct comedi_device *dev,
unsigned int channel, unsigned int mode);
-static int me4000_cnt_insn_config(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data);
+static int me4000_cnt_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data);
-static int me4000_cnt_insn_write(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data);
+static int me4000_cnt_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data);
-static int me4000_cnt_insn_read(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data);
+static int me4000_cnt_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data);
-static int me4000_ai_insn_read(comedi_device * dev,
- comedi_subdevice * subdevice, comedi_insn * insn, lsampl_t * data);
+static int me4000_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *subdevice, struct comedi_insn *insn, unsigned int *data);
-static int me4000_ai_cancel(comedi_device * dev, comedi_subdevice * s);
+static int me4000_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
-static int ai_check_chanlist(comedi_device * dev,
- comedi_subdevice * s, comedi_cmd * cmd);
+static int ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd);
-static int ai_round_cmd_args(comedi_device * dev,
- comedi_subdevice * s,
- comedi_cmd * cmd,
+static int ai_round_cmd_args(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd,
unsigned int *init_ticks,
unsigned int *scan_ticks, unsigned int *chan_ticks);
-static int ai_prepare(comedi_device * dev,
- comedi_subdevice * s,
- comedi_cmd * cmd,
+static int ai_prepare(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd,
unsigned int init_ticks,
unsigned int scan_ticks, unsigned int chan_ticks);
-static int ai_write_chanlist(comedi_device * dev,
- comedi_subdevice * s, comedi_cmd * cmd);
+static int ai_write_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd);
static irqreturn_t me4000_ai_isr(int irq, void *dev_id PT_REGS_ARG);
-static int me4000_ai_do_cmd_test(comedi_device * dev,
- comedi_subdevice * s, comedi_cmd * cmd);
+static int me4000_ai_do_cmd_test(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd);
-static int me4000_ai_do_cmd(comedi_device * dev, comedi_subdevice * s);
+static int me4000_ai_do_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
-static int me4000_ao_insn_write(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data);
+static int me4000_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data);
-static int me4000_ao_insn_read(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data);
+static int me4000_ao_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data);
/*-----------------------------------------------------------------------------
Meilhaus inline functions
---------------------------------------------------------------------------*/
-static inline void me4000_outb(comedi_device * dev, unsigned char value,
+static inline void me4000_outb(struct comedi_device *dev, unsigned char value,
unsigned long port)
{
PORT_PDEBUG("--> 0x%02X port 0x%04lX\n", value, port);
outb(value, port);
}
-static inline void me4000_outl(comedi_device * dev, unsigned long value,
+static inline void me4000_outl(struct comedi_device *dev, unsigned long value,
unsigned long port)
{
PORT_PDEBUG("--> 0x%08lX port 0x%04lX\n", value, port);
outl(value, port);
}
-static inline unsigned long me4000_inl(comedi_device * dev, unsigned long port)
+static inline unsigned long me4000_inl(struct comedi_device *dev, unsigned long port)
{
unsigned long value;
value = inl(port);
@@ -222,7 +222,7 @@ static inline unsigned long me4000_inl(comedi_device * dev, unsigned long port)
return value;
}
-static inline unsigned char me4000_inb(comedi_device * dev, unsigned long port)
+static inline unsigned char me4000_inb(struct comedi_device *dev, unsigned long port)
{
unsigned char value;
value = inb(port);
@@ -230,7 +230,7 @@ static inline unsigned char me4000_inb(comedi_device * dev, unsigned long port)
return value;
}
-static const comedi_lrange me4000_ai_range = {
+static const struct comedi_lrange me4000_ai_range = {
4,
{
UNI_RANGE(2.5),
@@ -240,16 +240,16 @@ static const comedi_lrange me4000_ai_range = {
}
};
-static const comedi_lrange me4000_ao_range = {
+static const struct comedi_lrange me4000_ao_range = {
1,
{
BIP_RANGE(10),
}
};
-static int me4000_attach(comedi_device * dev, comedi_devconfig * it)
+static int me4000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
- comedi_subdevice *s;
+ struct comedi_subdevice *s;
int result;
CALL_PDEBUG("In me4000_attach()\n");
@@ -277,7 +277,7 @@ static int me4000_attach(comedi_device * dev, comedi_devconfig * it)
s->subdev_flags =
SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
s->n_chan = thisboard->ai.count;
- s->maxdata = 0xFFFF; // 16 bit ADC
+ s->maxdata = 0xFFFF; /* 16 bit ADC */
s->len_chanlist = ME4000_AI_CHANNEL_LIST_COUNT;
s->range_table = &me4000_ai_range;
s->insn_read = me4000_ai_insn_read;
@@ -312,7 +312,7 @@ static int me4000_attach(comedi_device * dev, comedi_devconfig * it)
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITEABLE | SDF_COMMON | SDF_GROUND;
s->n_chan = thisboard->ao.count;
- s->maxdata = 0xFFFF; // 16 bit DAC
+ s->maxdata = 0xFFFF; /* 16 bit DAC */
s->range_table = &me4000_ao_range;
s->insn_write = me4000_ao_insn_write;
s->insn_read = me4000_ao_insn_read;
@@ -358,7 +358,7 @@ static int me4000_attach(comedi_device * dev, comedi_devconfig * it)
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = thisboard->cnt.count;
- s->maxdata = 0xFFFF; // 16 bit counters
+ s->maxdata = 0xFFFF; /* 16 bit counters */
s->insn_read = me4000_cnt_insn_read;
s->insn_write = me4000_cnt_insn_write;
s->insn_config = me4000_cnt_insn_config;
@@ -369,18 +369,18 @@ static int me4000_attach(comedi_device * dev, comedi_devconfig * it)
return 0;
}
-static int me4000_probe(comedi_device * dev, comedi_devconfig * it)
+static int me4000_probe(struct comedi_device *dev, struct comedi_devconfig *it)
{
struct pci_dev *pci_device;
int result, i;
- me4000_board_t *board;
+ struct me4000_board *board;
CALL_PDEBUG("In me4000_probe()\n");
/* Allocate private memory */
- if (alloc_private(dev, sizeof(me4000_info_t)) < 0) {
+ if (alloc_private(dev, sizeof(struct me4000_info)) < 0)
return -ENOMEM;
- }
+
/*
* Probe the device to determine what device in the series it is.
*/
@@ -405,7 +405,7 @@ static int me4000_probe(comedi_device * dev, comedi_devconfig * it)
}
}
dev->board_ptr = me4000_boards + i;
- board = (me4000_board_t *) dev->
+ board = (struct me4000_board *) dev->
board_ptr;
info->pci_dev_p = pci_device;
goto found;
@@ -512,7 +512,7 @@ static int me4000_probe(comedi_device * dev, comedi_devconfig * it)
return 0;
}
-static int get_registers(comedi_device * dev, struct pci_dev *pci_dev_p)
+static int get_registers(struct comedi_device *dev, struct pci_dev *pci_dev_p)
{
CALL_PDEBUG("In get_registers()\n");
@@ -564,27 +564,25 @@ static int get_registers(comedi_device * dev, struct pci_dev *pci_dev_p)
return 0;
}
-static int init_board_info(comedi_device * dev, struct pci_dev *pci_dev_p)
+static int init_board_info(struct comedi_device *dev, struct pci_dev *pci_dev_p)
{
int result;
CALL_PDEBUG("In init_board_info()\n");
/* Init spin locks */
- //spin_lock_init(&info->preload_lock);
- //spin_lock_init(&info->ai_ctrl_lock);
+ /* spin_lock_init(&info->preload_lock); */
+ /* spin_lock_init(&info->ai_ctrl_lock); */
/* Get the serial number */
result = pci_read_config_dword(pci_dev_p, 0x2C, &info->serial_no);
- if (result != PCIBIOS_SUCCESSFUL) {
+ if (result != PCIBIOS_SUCCESSFUL)
return result;
- }
/* Get the hardware revision */
result = pci_read_config_byte(pci_dev_p, 0x08, &info->hw_revision);
- if (result != PCIBIOS_SUCCESSFUL) {
+ if (result != PCIBIOS_SUCCESSFUL)
return result;
- }
/* Get the vendor id */
info->vendor_id = pci_dev_p->vendor;
@@ -598,14 +596,14 @@ static int init_board_info(comedi_device * dev, struct pci_dev *pci_dev_p)
return 0;
}
-static int init_ao_context(comedi_device * dev)
+static int init_ao_context(struct comedi_device *dev)
{
int i;
CALL_PDEBUG("In init_ao_context()\n");
for (i = 0; i < thisboard->ao.count; i++) {
- //spin_lock_init(&info->ao_context[i].use_lock);
+ /* spin_lock_init(&info->ao_context[i].use_lock); */
info->ao_context[i].irq = info->irq;
switch (i) {
@@ -681,7 +679,7 @@ static int init_ao_context(comedi_device * dev)
return 0;
}
-static int init_ai_context(comedi_device * dev)
+static int init_ai_context(struct comedi_device *dev)
{
CALL_PDEBUG("In init_ai_context()\n");
@@ -715,7 +713,7 @@ static int init_ai_context(comedi_device * dev)
return 0;
}
-static int init_dio_context(comedi_device * dev)
+static int init_dio_context(struct comedi_device *dev)
{
CALL_PDEBUG("In init_dio_context()\n");
@@ -734,7 +732,7 @@ static int init_dio_context(comedi_device * dev)
return 0;
}
-static int init_cnt_context(comedi_device * dev)
+static int init_cnt_context(struct comedi_device *dev)
{
CALL_PDEBUG("In init_cnt_context()\n");
@@ -755,7 +753,7 @@ static int init_cnt_context(comedi_device * dev)
extern unsigned char *xilinx_firm;
#endif
-static int xilinx_download(comedi_device * dev)
+static int xilinx_download(struct comedi_device *dev)
{
u32 value = 0;
wait_queue_head_t queue;
@@ -782,7 +780,7 @@ static int xilinx_download(comedi_device * dev)
/* Wait until /INIT pin is set */
udelay(20);
- if (!inl(info->plx_regbase + PLX_INTCSR) & 0x20) {
+ if (!(inl(info->plx_regbase + PLX_INTCSR) & 0x20)) {
printk(KERN_ERR
"comedi%d: me4000: xilinx_download(): Can't init Xilinx\n",
dev->minor);
@@ -837,7 +835,7 @@ static int xilinx_download(comedi_device * dev)
return 0;
}
-static int reset_board(comedi_device * dev)
+static int reset_board(struct comedi_device *dev)
{
unsigned long icr;
@@ -895,16 +893,15 @@ static int reset_board(comedi_device * dev)
return 0;
}
-static int me4000_detach(comedi_device * dev)
+static int me4000_detach(struct comedi_device *dev)
{
CALL_PDEBUG("In me4000_detach()\n");
if (info) {
if (info->pci_dev_p) {
reset_board(dev);
- if (info->plx_regbase) {
+ if (info->plx_regbase)
comedi_pci_disable(info->pci_dev_p);
- }
pci_dev_put(info->pci_dev_p);
}
}
@@ -916,8 +913,8 @@ static int me4000_detach(comedi_device * dev)
Analog input section
===========================================================================*/
-static int me4000_ai_insn_read(comedi_device * dev,
- comedi_subdevice * subdevice, comedi_insn * insn, lsampl_t * data)
+static int me4000_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *subdevice, struct comedi_insn *insn, unsigned int *data)
{
int chan = CR_CHAN(insn->chanspec);
@@ -1040,7 +1037,7 @@ static int me4000_ai_insn_read(comedi_device * dev,
return 1;
}
-static int me4000_ai_cancel(comedi_device * dev, comedi_subdevice * s)
+static int me4000_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
unsigned long tmp;
@@ -1057,8 +1054,8 @@ static int me4000_ai_cancel(comedi_device * dev, comedi_subdevice * s)
return 0;
}
-static int ai_check_chanlist(comedi_device * dev,
- comedi_subdevice * s, comedi_cmd * cmd)
+static int ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
int aref;
int i;
@@ -1138,9 +1135,9 @@ static int ai_check_chanlist(comedi_device * dev,
return 0;
}
-static int ai_round_cmd_args(comedi_device * dev,
- comedi_subdevice * s,
- comedi_cmd * cmd,
+static int ai_round_cmd_args(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd,
unsigned int *init_ticks,
unsigned int *scan_ticks, unsigned int *chan_ticks)
{
@@ -1163,9 +1160,8 @@ static int ai_round_cmd_args(comedi_device * dev,
rest = (cmd->start_arg * 33) % 1000;
if (cmd->flags & TRIG_ROUND_NEAREST) {
- if (rest > 33) {
+ if (rest > 33)
(*init_ticks)++;
- }
} else if (cmd->flags & TRIG_ROUND_UP) {
if (rest)
(*init_ticks)++;
@@ -1177,9 +1173,8 @@ static int ai_round_cmd_args(comedi_device * dev,
rest = (cmd->scan_begin_arg * 33) % 1000;
if (cmd->flags & TRIG_ROUND_NEAREST) {
- if (rest > 33) {
+ if (rest > 33)
(*scan_ticks)++;
- }
} else if (cmd->flags & TRIG_ROUND_UP) {
if (rest)
(*scan_ticks)++;
@@ -1191,9 +1186,8 @@ static int ai_round_cmd_args(comedi_device * dev,
rest = (cmd->convert_arg * 33) % 1000;
if (cmd->flags & TRIG_ROUND_NEAREST) {
- if (rest > 33) {
+ if (rest > 33)
(*chan_ticks)++;
- }
} else if (cmd->flags & TRIG_ROUND_UP) {
if (rest)
(*chan_ticks)++;
@@ -1207,7 +1201,7 @@ static int ai_round_cmd_args(comedi_device * dev,
return 0;
}
-static void ai_write_timer(comedi_device * dev,
+static void ai_write_timer(struct comedi_device *dev,
unsigned int init_ticks,
unsigned int scan_ticks, unsigned int chan_ticks)
{
@@ -1228,9 +1222,9 @@ static void ai_write_timer(comedi_device * dev,
me4000_outl(dev, chan_ticks - 1, info->ai_context.chan_timer_reg);
}
-static int ai_prepare(comedi_device * dev,
- comedi_subdevice * s,
- comedi_cmd * cmd,
+static int ai_prepare(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd,
unsigned int init_ticks,
unsigned int scan_ticks, unsigned int chan_ticks)
{
@@ -1297,8 +1291,8 @@ static int ai_prepare(comedi_device * dev,
return 0;
}
-static int ai_write_chanlist(comedi_device * dev,
- comedi_subdevice * s, comedi_cmd * cmd)
+static int ai_write_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
unsigned int entry;
unsigned int chan;
@@ -1337,13 +1331,13 @@ static int ai_write_chanlist(comedi_device * dev,
return 0;
}
-static int me4000_ai_do_cmd(comedi_device * dev, comedi_subdevice * s)
+static int me4000_ai_do_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
int err;
unsigned int init_ticks = 0;
unsigned int scan_ticks = 0;
unsigned int chan_ticks = 0;
- comedi_cmd *cmd = &s->async->cmd;
+ struct comedi_cmd *cmd = &s->async->cmd;
CALL_PDEBUG("In me4000_ai_do_cmd()\n");
@@ -1381,8 +1375,8 @@ static int me4000_ai_do_cmd(comedi_device * dev, comedi_subdevice * s)
* - invalid chanlist
* So I tried to adopt this scheme.
*/
-static int me4000_ai_do_cmd_test(comedi_device * dev,
- comedi_subdevice * s, comedi_cmd * cmd)
+static int me4000_ai_do_cmd_test(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
unsigned int init_ticks;
@@ -1503,9 +1497,8 @@ static int me4000_ai_do_cmd_test(comedi_device * dev,
cmd->stop_src = TRIG_NONE;
err++;
}
- if (err) {
+ if (err)
return 1;
- }
/*
* Stage 2. Check for trigger source conflicts.
@@ -1553,9 +1546,8 @@ static int me4000_ai_do_cmd_test(comedi_device * dev,
cmd->scan_end_src = TRIG_NONE;
err++;
}
- if (err) {
+ if (err)
return 2;
- }
/*
* Stage 3. Check if arguments are generally valid.
@@ -1588,9 +1580,9 @@ static int me4000_ai_do_cmd_test(comedi_device * dev,
cmd->convert_arg = 2000;
err++;
}
- if (err) {
+
+ if (err)
return 3;
- }
/*
* Stage 4. Check for argument conflicts.
@@ -1604,21 +1596,21 @@ static int me4000_ai_do_cmd_test(comedi_device * dev,
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n",
dev->minor);
- cmd->start_arg = 2000; // 66 ticks at least
+ cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
if (chan_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert arg\n",
dev->minor);
- cmd->convert_arg = 2000; // 66 ticks at least
+ cmd->convert_arg = 2000; /* 66 ticks at least */
err++;
}
if (scan_ticks <= cmd->chanlist_len * chan_ticks) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid scan end arg\n",
dev->minor);
- cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31; // At least one tick more
+ cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31; /* At least one tick more */
err++;
}
} else if (cmd->start_src == TRIG_NOW &&
@@ -1630,14 +1622,14 @@ static int me4000_ai_do_cmd_test(comedi_device * dev,
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n",
dev->minor);
- cmd->start_arg = 2000; // 66 ticks at least
+ cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
if (chan_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert arg\n",
dev->minor);
- cmd->convert_arg = 2000; // 66 ticks at least
+ cmd->convert_arg = 2000; /* 66 ticks at least */
err++;
}
} else if (cmd->start_src == TRIG_EXT &&
@@ -1649,21 +1641,21 @@ static int me4000_ai_do_cmd_test(comedi_device * dev,
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n",
dev->minor);
- cmd->start_arg = 2000; // 66 ticks at least
+ cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
if (chan_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert arg\n",
dev->minor);
- cmd->convert_arg = 2000; // 66 ticks at least
+ cmd->convert_arg = 2000; /* 66 ticks at least */
err++;
}
if (scan_ticks <= cmd->chanlist_len * chan_ticks) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid scan end arg\n",
dev->minor);
- cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31; // At least one tick more
+ cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31; /* At least one tick more */
err++;
}
} else if (cmd->start_src == TRIG_EXT &&
@@ -1675,14 +1667,14 @@ static int me4000_ai_do_cmd_test(comedi_device * dev,
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n",
dev->minor);
- cmd->start_arg = 2000; // 66 ticks at least
+ cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
if (chan_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert arg\n",
dev->minor);
- cmd->convert_arg = 2000; // 66 ticks at least
+ cmd->convert_arg = 2000; /* 66 ticks at least */
err++;
}
} else if (cmd->start_src == TRIG_EXT &&
@@ -1694,14 +1686,14 @@ static int me4000_ai_do_cmd_test(comedi_device * dev,
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n",
dev->minor);
- cmd->start_arg = 2000; // 66 ticks at least
+ cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
if (chan_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert arg\n",
dev->minor);
- cmd->convert_arg = 2000; // 66 ticks at least
+ cmd->convert_arg = 2000; /* 66 ticks at least */
err++;
}
} else if (cmd->start_src == TRIG_EXT &&
@@ -1713,7 +1705,7 @@ static int me4000_ai_do_cmd_test(comedi_device * dev,
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n",
dev->minor);
- cmd->start_arg = 2000; // 66 ticks at least
+ cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
}
@@ -1735,9 +1727,9 @@ static int me4000_ai_do_cmd_test(comedi_device * dev,
err++;
}
}
- if (err) {
+
+ if (err)
return 4;
- }
/*
* Stage 5. Check the channel list.
@@ -1751,9 +1743,9 @@ static int me4000_ai_do_cmd_test(comedi_device * dev,
static irqreturn_t me4000_ai_isr(int irq, void *dev_id PT_REGS_ARG)
{
unsigned int tmp;
- comedi_device *dev = dev_id;
- comedi_subdevice *s = dev->subdevices;
- me4000_ai_context_t *ai_context = &info->ai_context;
+ struct comedi_device *dev = dev_id;
+ struct comedi_subdevice *s = dev->subdevices;
+ struct me4000_ai_context *ai_context = &info->ai_context;
int i;
int c = 0;
long lval;
@@ -1911,8 +1903,8 @@ static irqreturn_t me4000_ai_isr(int irq, void *dev_id PT_REGS_ARG)
Analog output section
===========================================================================*/
-static int me4000_ao_insn_write(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+static int me4000_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
{
int chan = CR_CHAN(insn->chanspec);
@@ -1969,8 +1961,8 @@ static int me4000_ao_insn_write(comedi_device * dev,
return 1;
}
-static int me4000_ao_insn_read(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+static int me4000_ao_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
{
int chan = CR_CHAN(insn->chanspec);
@@ -1990,16 +1982,16 @@ static int me4000_ao_insn_read(comedi_device * dev,
Digital I/O section
===========================================================================*/
-static int me4000_dio_insn_bits(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+static int me4000_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
{
CALL_PDEBUG("In me4000_dio_insn_bits()\n");
/* Length of data must be 2 (mask and new data, see below) */
- if (insn->n == 0) {
+ if (insn->n == 0)
return 0;
- }
+
if (insn->n != 2) {
printk("comedi%d: me4000: me4000_dio_insn_bits(): Invalid instruction length\n", dev->minor);
return -EINVAL;
@@ -2041,8 +2033,8 @@ static int me4000_dio_insn_bits(comedi_device * dev,
return 2;
}
-static int me4000_dio_insn_config(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+static int me4000_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
{
unsigned long tmp;
int chan = CR_CHAN(insn->chanspec);
@@ -2135,7 +2127,7 @@ static int me4000_dio_insn_config(comedi_device * dev,
Counter section
===========================================================================*/
-static int cnt_reset(comedi_device * dev, unsigned int channel)
+static int cnt_reset(struct comedi_device *dev, unsigned int channel)
{
CALL_PDEBUG("In cnt_reset()\n");
@@ -2166,7 +2158,7 @@ static int cnt_reset(comedi_device * dev, unsigned int channel)
return 0;
}
-static int cnt_config(comedi_device * dev, unsigned int channel,
+static int cnt_config(struct comedi_device *dev, unsigned int channel,
unsigned int mode)
{
int tmp = 0;
@@ -2223,8 +2215,8 @@ static int cnt_config(comedi_device * dev, unsigned int channel,
return 0;
}
-static int me4000_cnt_insn_config(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+static int me4000_cnt_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
{
int err;
@@ -2266,17 +2258,17 @@ static int me4000_cnt_insn_config(comedi_device * dev,
return 2;
}
-static int me4000_cnt_insn_read(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+static int me4000_cnt_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
{
unsigned short tmp;
CALL_PDEBUG("In me4000_cnt_insn_read()\n");
- if (insn->n == 0) {
+ if (insn->n == 0)
return 0;
- }
+
if (insn->n > 1) {
printk(KERN_ERR
"comedi%d: me4000: me4000_cnt_insn_read(): Invalid instruction length %d\n",
@@ -2313,8 +2305,8 @@ static int me4000_cnt_insn_read(comedi_device * dev,
return 1;
}
-static int me4000_cnt_insn_write(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+static int me4000_cnt_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
{
unsigned short tmp;
diff --git a/drivers/staging/comedi/drivers/me4000.h b/drivers/staging/comedi/drivers/me4000.h
index f12b8873ec3c..733b19243c75 100644
--- a/drivers/staging/comedi/drivers/me4000.h
+++ b/drivers/staging/comedi/drivers/me4000.h
@@ -28,37 +28,37 @@
Debug section
===========================================================================*/
-#undef ME4000_CALL_DEBUG // Debug function entry and exit
-#undef ME4000_PORT_DEBUG // Debug port access
-#undef ME4000_ISR_DEBUG // Debug the interrupt service routine
-#undef ME4000_DEBUG // General purpose debug masseges
+#undef ME4000_CALL_DEBUG /* Debug function entry and exit */
+#undef ME4000_PORT_DEBUG /* Debug port access */
+#undef ME4000_ISR_DEBUG /* Debug the interrupt service routine */
+#undef ME4000_DEBUG /* General purpose debug masseges */
#ifdef ME4000_CALL_DEBUG
#undef CALL_PDEBUG
#define CALL_PDEBUG(fmt, args...) printk(KERN_DEBUG"comedi%d: me4000: " fmt, dev->minor, ##args)
#else
-# define CALL_PDEBUG(fmt, args...) // no debugging, do nothing
+# define CALL_PDEBUG(fmt, args...) /* no debugging, do nothing */
#endif
#ifdef ME4000_PORT_DEBUG
#undef PORT_PDEBUG
#define PORT_PDEBUG(fmt, args...) printk(KERN_DEBUG"comedi%d: me4000: " fmt, dev->minor, ##args)
#else
-#define PORT_PDEBUG(fmt, args...) // no debugging, do nothing
+#define PORT_PDEBUG(fmt, args...) /* no debugging, do nothing */
#endif
#ifdef ME4000_ISR_DEBUG
#undef ISR_PDEBUG
#define ISR_PDEBUG(fmt, args...) printk(KERN_DEBUG"comedi%d: me4000: " fmt, dev->minor, ##args)
#else
-#define ISR_PDEBUG(fmt, args...) // no debugging, do nothing
+#define ISR_PDEBUG(fmt, args...) /* no debugging, do nothing */
#endif
#ifdef ME4000_DEBUG
#undef PDEBUG
#define PDEBUG(fmt, args...) printk(KERN_DEBUG"comedi%d: me4000: " fmt, dev->minor, ##args)
#else
-#define PDEBUG(fmt, args...) // no debugging, do nothing
+#define PDEBUG(fmt, args...) /* no debugging, do nothing */
#endif
/*=============================================================================
@@ -67,78 +67,78 @@
#define PCI_VENDOR_ID_MEILHAUS 0x1402
-#define PCI_DEVICE_ID_MEILHAUS_ME4650 0x4650 // Low Cost version
+#define PCI_DEVICE_ID_MEILHAUS_ME4650 0x4650 /* Low Cost version */
-#define PCI_DEVICE_ID_MEILHAUS_ME4660 0x4660 // Standard version
-#define PCI_DEVICE_ID_MEILHAUS_ME4660I 0x4661 // Isolated version
-#define PCI_DEVICE_ID_MEILHAUS_ME4660S 0x4662 // Standard version with Sample and Hold
-#define PCI_DEVICE_ID_MEILHAUS_ME4660IS 0x4663 // Isolated version with Sample and Hold
+#define PCI_DEVICE_ID_MEILHAUS_ME4660 0x4660 /* Standard version */
+#define PCI_DEVICE_ID_MEILHAUS_ME4660I 0x4661 /* Isolated version */
+#define PCI_DEVICE_ID_MEILHAUS_ME4660S 0x4662 /* Standard version with Sample and Hold */
+#define PCI_DEVICE_ID_MEILHAUS_ME4660IS 0x4663 /* Isolated version with Sample and Hold */
-#define PCI_DEVICE_ID_MEILHAUS_ME4670 0x4670 // Standard version
-#define PCI_DEVICE_ID_MEILHAUS_ME4670I 0x4671 // Isolated version
-#define PCI_DEVICE_ID_MEILHAUS_ME4670S 0x4672 // Standard version with Sample and Hold
-#define PCI_DEVICE_ID_MEILHAUS_ME4670IS 0x4673 // Isolated version with Sample and Hold
+#define PCI_DEVICE_ID_MEILHAUS_ME4670 0x4670 /* Standard version */
+#define PCI_DEVICE_ID_MEILHAUS_ME4670I 0x4671 /* Isolated version */
+#define PCI_DEVICE_ID_MEILHAUS_ME4670S 0x4672 /* Standard version with Sample and Hold */
+#define PCI_DEVICE_ID_MEILHAUS_ME4670IS 0x4673 /* Isolated version with Sample and Hold */
-#define PCI_DEVICE_ID_MEILHAUS_ME4680 0x4680 // Standard version
-#define PCI_DEVICE_ID_MEILHAUS_ME4680I 0x4681 // Isolated version
-#define PCI_DEVICE_ID_MEILHAUS_ME4680S 0x4682 // Standard version with Sample and Hold
-#define PCI_DEVICE_ID_MEILHAUS_ME4680IS 0x4683 // Isolated version with Sample and Hold
+#define PCI_DEVICE_ID_MEILHAUS_ME4680 0x4680 /* Standard version */
+#define PCI_DEVICE_ID_MEILHAUS_ME4680I 0x4681 /* Isolated version */
+#define PCI_DEVICE_ID_MEILHAUS_ME4680S 0x4682 /* Standard version with Sample and Hold */
+#define PCI_DEVICE_ID_MEILHAUS_ME4680IS 0x4683 /* Isolated version with Sample and Hold */
/*=============================================================================
ME-4000 base register offsets
===========================================================================*/
-#define ME4000_AO_00_CTRL_REG 0x00 // R/W
-#define ME4000_AO_00_STATUS_REG 0x04 // R/_
-#define ME4000_AO_00_FIFO_REG 0x08 // _/W
-#define ME4000_AO_00_SINGLE_REG 0x0C // R/W
-#define ME4000_AO_00_TIMER_REG 0x10 // _/W
-
-#define ME4000_AO_01_CTRL_REG 0x18 // R/W
-#define ME4000_AO_01_STATUS_REG 0x1C // R/_
-#define ME4000_AO_01_FIFO_REG 0x20 // _/W
-#define ME4000_AO_01_SINGLE_REG 0x24 // R/W
-#define ME4000_AO_01_TIMER_REG 0x28 // _/W
-
-#define ME4000_AO_02_CTRL_REG 0x30 // R/W
-#define ME4000_AO_02_STATUS_REG 0x34 // R/_
-#define ME4000_AO_02_FIFO_REG 0x38 // _/W
-#define ME4000_AO_02_SINGLE_REG 0x3C // R/W
-#define ME4000_AO_02_TIMER_REG 0x40 // _/W
-
-#define ME4000_AO_03_CTRL_REG 0x48 // R/W
-#define ME4000_AO_03_STATUS_REG 0x4C // R/_
-#define ME4000_AO_03_FIFO_REG 0x50 // _/W
-#define ME4000_AO_03_SINGLE_REG 0x54 // R/W
-#define ME4000_AO_03_TIMER_REG 0x58 // _/W
-
-#define ME4000_AI_CTRL_REG 0x74 // _/W
-#define ME4000_AI_STATUS_REG 0x74 // R/_
-#define ME4000_AI_CHANNEL_LIST_REG 0x78 // _/W
-#define ME4000_AI_DATA_REG 0x7C // R/_
-#define ME4000_AI_CHAN_TIMER_REG 0x80 // _/W
-#define ME4000_AI_CHAN_PRE_TIMER_REG 0x84 // _/W
-#define ME4000_AI_SCAN_TIMER_LOW_REG 0x88 // _/W
-#define ME4000_AI_SCAN_TIMER_HIGH_REG 0x8C // _/W
-#define ME4000_AI_SCAN_PRE_TIMER_LOW_REG 0x90 // _/W
-#define ME4000_AI_SCAN_PRE_TIMER_HIGH_REG 0x94 // _/W
-#define ME4000_AI_START_REG 0x98 // R/_
-
-#define ME4000_IRQ_STATUS_REG 0x9C // R/_
-
-#define ME4000_DIO_PORT_0_REG 0xA0 // R/W
-#define ME4000_DIO_PORT_1_REG 0xA4 // R/W
-#define ME4000_DIO_PORT_2_REG 0xA8 // R/W
-#define ME4000_DIO_PORT_3_REG 0xAC // R/W
-#define ME4000_DIO_DIR_REG 0xB0 // R/W
-
-#define ME4000_AO_LOADSETREG_XX 0xB4 // R/W
-
-#define ME4000_DIO_CTRL_REG 0xB8 // R/W
-
-#define ME4000_AO_DEMUX_ADJUST_REG 0xBC // -/W
-
-#define ME4000_AI_SAMPLE_COUNTER_REG 0xC0 // _/W
+#define ME4000_AO_00_CTRL_REG 0x00 /* R/W */
+#define ME4000_AO_00_STATUS_REG 0x04 /* R/_ */
+#define ME4000_AO_00_FIFO_REG 0x08 /* _/W */
+#define ME4000_AO_00_SINGLE_REG 0x0C /* R/W */
+#define ME4000_AO_00_TIMER_REG 0x10 /* _/W */
+
+#define ME4000_AO_01_CTRL_REG 0x18 /* R/W */
+#define ME4000_AO_01_STATUS_REG 0x1C /* R/_ */
+#define ME4000_AO_01_FIFO_REG 0x20 /* _/W */
+#define ME4000_AO_01_SINGLE_REG 0x24 /* R/W */
+#define ME4000_AO_01_TIMER_REG 0x28 /* _/W */
+
+#define ME4000_AO_02_CTRL_REG 0x30 /* R/W */
+#define ME4000_AO_02_STATUS_REG 0x34 /* R/_ */
+#define ME4000_AO_02_FIFO_REG 0x38 /* _/W */
+#define ME4000_AO_02_SINGLE_REG 0x3C /* R/W */
+#define ME4000_AO_02_TIMER_REG 0x40 /* _/W */
+
+#define ME4000_AO_03_CTRL_REG 0x48 /* R/W */
+#define ME4000_AO_03_STATUS_REG 0x4C /* R/_ */
+#define ME4000_AO_03_FIFO_REG 0x50 /* _/W */
+#define ME4000_AO_03_SINGLE_REG 0x54 /* R/W */
+#define ME4000_AO_03_TIMER_REG 0x58 /* _/W */
+
+#define ME4000_AI_CTRL_REG 0x74 /* _/W */
+#define ME4000_AI_STATUS_REG 0x74 /* R/_ */
+#define ME4000_AI_CHANNEL_LIST_REG 0x78 /* _/W */
+#define ME4000_AI_DATA_REG 0x7C /* R/_ */
+#define ME4000_AI_CHAN_TIMER_REG 0x80 /* _/W */
+#define ME4000_AI_CHAN_PRE_TIMER_REG 0x84 /* _/W */
+#define ME4000_AI_SCAN_TIMER_LOW_REG 0x88 /* _/W */
+#define ME4000_AI_SCAN_TIMER_HIGH_REG 0x8C /* _/W */
+#define ME4000_AI_SCAN_PRE_TIMER_LOW_REG 0x90 /* _/W */
+#define ME4000_AI_SCAN_PRE_TIMER_HIGH_REG 0x94 /* _/W */
+#define ME4000_AI_START_REG 0x98 /* R/_ */
+
+#define ME4000_IRQ_STATUS_REG 0x9C /* R/_ */
+
+#define ME4000_DIO_PORT_0_REG 0xA0 /* R/W */
+#define ME4000_DIO_PORT_1_REG 0xA4 /* R/W */
+#define ME4000_DIO_PORT_2_REG 0xA8 /* R/W */
+#define ME4000_DIO_PORT_3_REG 0xAC /* R/W */
+#define ME4000_DIO_DIR_REG 0xB0 /* R/W */
+
+#define ME4000_AO_LOADSETREG_XX 0xB4 /* R/W */
+
+#define ME4000_DIO_CTRL_REG 0xB8 /* R/W */
+
+#define ME4000_AO_DEMUX_ADJUST_REG 0xBC /* -/W */
+
+#define ME4000_AI_SAMPLE_COUNTER_REG 0xC0 /* _/W */
/*=============================================================================
Value to adjust Demux
@@ -159,21 +159,21 @@
PLX base register offsets
===========================================================================*/
-#define PLX_INTCSR 0x4C // Interrupt control and status register
-#define PLX_ICR 0x50 // Initialization control register
+#define PLX_INTCSR 0x4C /* Interrupt control and status register */
+#define PLX_ICR 0x50 /* Initialization control register */
/*=============================================================================
Bits for the PLX_ICSR register
===========================================================================*/
-#define PLX_INTCSR_LOCAL_INT1_EN 0x01 // If set, local interrupt 1 is enabled (r/w)
-#define PLX_INTCSR_LOCAL_INT1_POL 0x02 // If set, local interrupt 1 polarity is active high (r/w)
-#define PLX_INTCSR_LOCAL_INT1_STATE 0x04 // If set, local interrupt 1 is active (r/_)
-#define PLX_INTCSR_LOCAL_INT2_EN 0x08 // If set, local interrupt 2 is enabled (r/w)
-#define PLX_INTCSR_LOCAL_INT2_POL 0x10 // If set, local interrupt 2 polarity is active high (r/w)
-#define PLX_INTCSR_LOCAL_INT2_STATE 0x20 // If set, local interrupt 2 is active (r/_)
-#define PLX_INTCSR_PCI_INT_EN 0x40 // If set, PCI interrupt is enabled (r/w)
-#define PLX_INTCSR_SOFT_INT 0x80 // If set, a software interrupt is generated (r/w)
+#define PLX_INTCSR_LOCAL_INT1_EN 0x01 /* If set, local interrupt 1 is enabled (r/w) */
+#define PLX_INTCSR_LOCAL_INT1_POL 0x02 /* If set, local interrupt 1 polarity is active high (r/w) */
+#define PLX_INTCSR_LOCAL_INT1_STATE 0x04 /* If set, local interrupt 1 is active (r/_) */
+#define PLX_INTCSR_LOCAL_INT2_EN 0x08 /* If set, local interrupt 2 is enabled (r/w) */
+#define PLX_INTCSR_LOCAL_INT2_POL 0x10 /* If set, local interrupt 2 polarity is active high (r/w) */
+#define PLX_INTCSR_LOCAL_INT2_STATE 0x20 /* If set, local interrupt 2 is active (r/_) */
+#define PLX_INTCSR_PCI_INT_EN 0x40 /* If set, PCI interrupt is enabled (r/w) */
+#define PLX_INTCSR_SOFT_INT 0x80 /* If set, a software interrupt is generated (r/w) */
/*=============================================================================
Bits for the PLX_ICR register
@@ -293,45 +293,45 @@
Information about the hardware capabilities
===========================================================================*/
-typedef struct me4000_ao_info {
+struct me4000_ao_info {
int count;
int fifo_count;
-} me4000_ao_info_t;
+};
-typedef struct me4000_ai_info {
+struct me4000_ai_info {
int count;
int sh_count;
int diff_count;
int ex_trig_analog;
-} me4000_ai_info_t;
+};
-typedef struct me4000_dio_info {
+struct me4000_dio_info {
int count;
-} me4000_dio_info_t;
+};
-typedef struct me4000_cnt_info {
+struct me4000_cnt_info {
int count;
-} me4000_cnt_info_t;
+};
-typedef struct me4000_board {
+struct me4000_board {
const char *name;
unsigned short device_id;
- me4000_ao_info_t ao;
- me4000_ai_info_t ai;
- me4000_dio_info_t dio;
- me4000_cnt_info_t cnt;
-} me4000_board_t;
+ struct me4000_ao_info ao;
+ struct me4000_ai_info ai;
+ struct me4000_dio_info dio;
+ struct me4000_cnt_info cnt;
+};
-#define thisboard ((const me4000_board_t *)dev->board_ptr)
+#define thisboard ((const struct me4000_board *)dev->board_ptr)
/*=============================================================================
Global board and subdevice information structures
===========================================================================*/
-typedef struct me4000_ao_context {
+struct me4000_ao_context {
int irq;
- unsigned long mirror; // Store the last written value
+ unsigned long mirror; /* Store the last written value */
unsigned long ctrl_reg;
unsigned long status_reg;
@@ -340,9 +340,9 @@ typedef struct me4000_ao_context {
unsigned long timer_reg;
unsigned long irq_status_reg;
unsigned long preload_reg;
-} me4000_ao_context_t;
+};
-typedef struct me4000_ai_context {
+struct me4000_ai_context {
int irq;
unsigned long ctrl_reg;
@@ -358,51 +358,51 @@ typedef struct me4000_ai_context {
unsigned long start_reg;
unsigned long irq_status_reg;
unsigned long sample_counter_reg;
-} me4000_ai_context_t;
+};
-typedef struct me4000_dio_context {
+struct me4000_dio_context {
unsigned long dir_reg;
unsigned long ctrl_reg;
unsigned long port_0_reg;
unsigned long port_1_reg;
unsigned long port_2_reg;
unsigned long port_3_reg;
-} me4000_dio_context_t;
+};
-typedef struct me4000_cnt_context {
+struct me4000_cnt_context {
unsigned long ctrl_reg;
unsigned long counter_0_reg;
unsigned long counter_1_reg;
unsigned long counter_2_reg;
-} me4000_cnt_context_t;
+};
-typedef struct me4000_info {
- unsigned long plx_regbase; // PLX configuration space base address
- unsigned long me4000_regbase; // Base address of the ME4000
- unsigned long timer_regbase; // Base address of the timer circuit
- unsigned long program_regbase; // Base address to set the program pin for the xilinx
+struct me4000_info {
+ unsigned long plx_regbase; /* PLX configuration space base address */
+ unsigned long me4000_regbase; /* Base address of the ME4000 */
+ unsigned long timer_regbase; /* Base address of the timer circuit */
+ unsigned long program_regbase; /* Base address to set the program pin for the xilinx */
- unsigned long plx_regbase_size; // PLX register set space
- unsigned long me4000_regbase_size; // ME4000 register set space
- unsigned long timer_regbase_size; // Timer circuit register set space
- unsigned long program_regbase_size; // Size of program base address of the ME4000
+ unsigned long plx_regbase_size; /* PLX register set space */
+ unsigned long me4000_regbase_size; /* ME4000 register set space */
+ unsigned long timer_regbase_size; /* Timer circuit register set space */
+ unsigned long program_regbase_size; /* Size of program base address of the ME4000 */
- unsigned int serial_no; // Serial number of the board
- unsigned char hw_revision; // Hardware revision of the board
- unsigned short vendor_id; // Meilhaus vendor id
- unsigned short device_id; // Device id
+ unsigned int serial_no; /* Serial number of the board */
+ unsigned char hw_revision; /* Hardware revision of the board */
+ unsigned short vendor_id; /* Meilhaus vendor id */
+ unsigned short device_id; /* Device id */
- struct pci_dev *pci_dev_p; // General PCI information
+ struct pci_dev *pci_dev_p; /* General PCI information */
- unsigned int irq; // IRQ assigned from the PCI BIOS
+ unsigned int irq; /* IRQ assigned from the PCI BIOS */
- struct me4000_ai_context ai_context; // Analog input specific context
- struct me4000_ao_context ao_context[4]; // Vector with analog output specific context
- struct me4000_dio_context dio_context; // Digital I/O specific context
- struct me4000_cnt_context cnt_context; // Counter specific context
-} me4000_info_t;
+ struct me4000_ai_context ai_context; /* Analog input specific context */
+ struct me4000_ao_context ao_context[4]; /* Vector with analog output specific context */
+ struct me4000_dio_context dio_context; /* Digital I/O specific context */
+ struct me4000_cnt_context cnt_context; /* Counter specific context */
+};
-#define info ((me4000_info_t *)dev->private)
+#define info ((struct me4000_info *)dev->private)
/*-----------------------------------------------------------------------------
Defines for analog input
@@ -412,7 +412,7 @@ typedef struct me4000_info {
#define ME4000_AI_FIFO_COUNT 2048
#define ME4000_AI_MIN_TICKS 66
-#define ME4000_AI_MIN_SAMPLE_TIME 2000 // Minimum sample time [ns]
+#define ME4000_AI_MIN_SAMPLE_TIME 2000 /* Minimum sample time [ns] */
#define ME4000_AI_BASE_FREQUENCY (unsigned int) 33E6
/* Channel list defines and masks */
@@ -436,11 +436,11 @@ typedef struct me4000_info {
#define ME4000_CNT_COUNTER_1 0x40
#define ME4000_CNT_COUNTER_2 0x80
-#define ME4000_CNT_MODE_0 0x00 // Change state if zero crossing
-#define ME4000_CNT_MODE_1 0x02 // Retriggerable One-Shot
-#define ME4000_CNT_MODE_2 0x04 // Asymmetrical divider
-#define ME4000_CNT_MODE_3 0x06 // Symmetrical divider
-#define ME4000_CNT_MODE_4 0x08 // Counter start by software trigger
-#define ME4000_CNT_MODE_5 0x0A // Counter start by hardware trigger
+#define ME4000_CNT_MODE_0 0x00 /* Change state if zero crossing */
+#define ME4000_CNT_MODE_1 0x02 /* Retriggerable One-Shot */
+#define ME4000_CNT_MODE_2 0x04 /* Asymmetrical divider */
+#define ME4000_CNT_MODE_3 0x06 /* Symmetrical divider */
+#define ME4000_CNT_MODE_4 0x08 /* Counter start by software trigger */
+#define ME4000_CNT_MODE_5 0x0A /* Counter start by hardware trigger */
#endif
diff --git a/drivers/staging/comedi/drivers/me_daq.c b/drivers/staging/comedi/drivers/me_daq.c
index 6accec20a0f9..0f023d009e61 100644
--- a/drivers/staging/comedi/drivers/me_daq.c
+++ b/drivers/staging/comedi/drivers/me_daq.c
@@ -144,10 +144,10 @@ from http://www.comedi.org
#define ME_COUNTER_VALUE_B 0x0022 /* R | - */
/* Function prototypes */
-static int me_attach(comedi_device *dev, comedi_devconfig *it);
-static int me_detach(comedi_device *dev);
+static int me_attach(struct comedi_device *dev, struct comedi_devconfig *it);
+static int me_detach(struct comedi_device *dev);
-static const comedi_lrange me2000_ai_range = {
+static const struct comedi_lrange me2000_ai_range = {
8,
{
BIP_RANGE(10),
@@ -161,7 +161,7 @@ static const comedi_lrange me2000_ai_range = {
}
};
-static const comedi_lrange me2600_ai_range = {
+static const struct comedi_lrange me2600_ai_range = {
8,
{
BIP_RANGE(10),
@@ -175,7 +175,7 @@ static const comedi_lrange me2600_ai_range = {
}
};
-static const comedi_lrange me2600_ao_range = {
+static const struct comedi_lrange me2600_ao_range = {
3,
{
BIP_RANGE(10),
@@ -201,11 +201,11 @@ struct me_board {
int ao_channel_nbr; /* DA config */
int ao_resolution;
int ao_resolution_mask;
- const comedi_lrange *ao_range_list;
+ const struct comedi_lrange *ao_range_list;
int ai_channel_nbr; /* AD config */
int ai_resolution;
int ai_resolution_mask;
- const comedi_lrange *ai_range_list;
+ const struct comedi_lrange *ai_range_list;
int dio_channel_nbr; /* DIO config */
};
@@ -246,7 +246,8 @@ static const struct me_board me_boards[] = {
#define me_board_nbr (sizeof(me_boards)/sizeof(struct me_board))
-static comedi_driver me_driver = {
+
+static struct comedi_driver me_driver = {
.driver_name = ME_DRIVER_NAME,
.module = THIS_MODULE,
.attach = me_attach,
@@ -290,8 +291,8 @@ static inline void sleep(unsigned sec)
*
* ------------------------------------------------------------------
*/
-static int me_dio_insn_config(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int me_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int bits;
int mask = 1 << CR_CHAN(insn->chanspec);
@@ -326,8 +327,8 @@ static int me_dio_insn_config(comedi_device *dev, comedi_subdevice *s,
}
/* Digital instant input/outputs */
-static int me_dio_insn_bits(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int me_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
unsigned int mask = data[0];
s->state &= ~mask;
@@ -362,8 +363,8 @@ static int me_dio_insn_bits(comedi_device *dev, comedi_subdevice *s,
*/
/* Analog instant input */
-static int me_ai_insn_read(comedi_device *dev, comedi_subdevice *subdevice,
- comedi_insn *insn, lsampl_t *data)
+static int me_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *subdevice,
+ struct comedi_insn *insn, unsigned int *data)
{
unsigned short value;
int chan = CR_CHAN((&insn->chanspec)[0]);
@@ -436,7 +437,7 @@ static int me_ai_insn_read(comedi_device *dev, comedi_subdevice *subdevice,
*/
/* Cancel analog input autoscan */
-static int me_ai_cancel(comedi_device *dev, comedi_subdevice *s)
+static int me_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
/* disable interrupts */
@@ -448,14 +449,14 @@ static int me_ai_cancel(comedi_device *dev, comedi_subdevice *s)
}
/* Test analog input command */
-static int me_ai_do_cmd_test(comedi_device *dev, comedi_subdevice *s,
- comedi_cmd *cmd)
+static int me_ai_do_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
{
return 0;
}
/* Analog input command */
-static int me_ai_do_cmd(comedi_device *dev, comedi_subdevice *subdevice)
+static int me_ai_do_cmd(struct comedi_device *dev, struct comedi_subdevice *subdevice)
{
return 0;
}
@@ -469,8 +470,8 @@ static int me_ai_do_cmd(comedi_device *dev, comedi_subdevice *subdevice)
*/
/* Analog instant output */
-static int me_ao_insn_write(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int me_ao_insn_write(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int chan;
int rang;
@@ -519,8 +520,8 @@ static int me_ao_insn_write(comedi_device *dev, comedi_subdevice *s,
}
/* Analog output readback */
-static int me_ao_insn_read(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int me_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int i;
@@ -541,7 +542,7 @@ static int me_ao_insn_read(comedi_device *dev, comedi_subdevice *s,
*/
/* Xilinx firmware download for card: ME-2600i */
-static int me2600_xilinx_download(comedi_device *dev,
+static int me2600_xilinx_download(struct comedi_device *dev,
unsigned char *me2600_firmware,
unsigned int length)
{
@@ -609,7 +610,7 @@ static int me2600_xilinx_download(comedi_device *dev,
}
/* Reset device */
-static int me_reset(comedi_device *dev)
+static int me_reset(struct comedi_device *dev)
{
/* Reset board */
writew(0x00, dev_private->me_regbase + ME_CONTROL_1);
@@ -631,10 +632,10 @@ static int me_reset(comedi_device *dev)
* - Register PCI device
* - Declare device driver capability
*/
-static int me_attach(comedi_device *dev, comedi_devconfig *it)
+static int me_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
struct pci_dev *pci_device;
- comedi_subdevice *subdevice;
+ struct comedi_subdevice *subdevice;
struct me_board *board;
resource_size_t plx_regbase_tmp;
unsigned long plx_regbase_size_tmp;
@@ -825,7 +826,7 @@ found:
}
/* Detach */
-static int me_detach(comedi_device *dev)
+static int me_detach(struct comedi_device *dev)
{
if (dev_private) {
if (dev_private->me_regbase) {
diff --git a/drivers/staging/comedi/drivers/mite.c b/drivers/staging/comedi/drivers/mite.c
index 9cc527424d04..ae9017744537 100644
--- a/drivers/staging/comedi/drivers/mite.c
+++ b/drivers/staging/comedi/drivers/mite.c
@@ -47,7 +47,7 @@
*/
-//#define USE_KMALLOC
+/* #define USE_KMALLOC */
#include "mite.h"
@@ -63,7 +63,7 @@
MODULE_LICENSE("GPL");
-struct mite_struct *mite_devices = NULL;
+struct mite_struct *mite_devices;
#define TOP_OF_PAGE(x) ((x)|(~(PAGE_MASK)))
@@ -103,7 +103,7 @@ static void dump_chip_signature(u32 csigr_bits)
printk("mite: num channels = %i, write post fifo depth = %i, wins = %i, iowins = %i\n", mite_csigr_dmac(csigr_bits), mite_csigr_wpdep(csigr_bits), mite_csigr_wins(csigr_bits), mite_csigr_iowins(csigr_bits));
}
-unsigned mite_fifo_size(struct mite_struct * mite, unsigned channel)
+unsigned mite_fifo_size(struct mite_struct *mite, unsigned channel)
{
unsigned fcr_bits = readl(mite->mite_io_addr +
MITE_FCR(channel));
@@ -139,7 +139,7 @@ int mite_setup2(struct mite_struct *mite, unsigned use_iodwbsr_1)
addr = pci_resource_start(mite->pcidev, 1);
mite->daq_phys_addr = addr;
length = pci_resource_len(mite->pcidev, 1);
- // In case of a 660x board, DAQ size is 8k instead of 4k (see as shown by lspci output)
+ /* In case of a 660x board, DAQ size is 8k instead of 4k (see as shown by lspci output) */
mite->daq_io_addr = ioremap(mite->daq_phys_addr, length);
if (!mite->daq_io_addr) {
printk("failed to remap daq io memory address\n");
@@ -212,7 +212,7 @@ void mite_cleanup(void)
void mite_unsetup(struct mite_struct *mite)
{
- //unsigned long offset, start, length;
+ /* unsigned long offset, start, length; */
if (!mite)
return;
@@ -257,7 +257,7 @@ struct mite_channel *mite_request_channel_in_range(struct mite_struct *mite,
unsigned long flags;
struct mite_channel *channel = NULL;
- // spin lock so mite_release_channel can be called safely from interrupts
+ /* spin lock so mite_release_channel can be called safely from interrupts */
comedi_spin_lock_irqsave(&mite->lock, flags);
for (i = min_channel; i <= max_channel; ++i) {
if (mite->channel_allocated[i] == 0) {
@@ -276,7 +276,7 @@ void mite_release_channel(struct mite_channel *mite_chan)
struct mite_struct *mite = mite_chan->mite;
unsigned long flags;
- // spin lock to prevent races with mite_request_channel
+ /* spin lock to prevent races with mite_request_channel */
comedi_spin_lock_irqsave(&mite->lock, flags);
if (mite->channel_allocated[mite_chan->channel]) {
mite_dma_disarm(mite_chan);
@@ -312,12 +312,12 @@ void mite_dma_arm(struct mite_channel *mite_chan)
writel(chor, mite->mite_io_addr + MITE_CHOR(mite_chan->channel));
mmiowb();
comedi_spin_unlock_irqrestore(&mite->lock, flags);
-// mite_dma_tcr(mite, channel);
+/* mite_dma_tcr(mite, channel); */
}
/**************************************/
-int mite_buf_change(struct mite_dma_descriptor_ring *ring, comedi_async * async)
+int mite_buf_change(struct mite_dma_descriptor_ring *ring, struct comedi_async * async)
{
unsigned int n_links;
int i;
@@ -331,9 +331,9 @@ int mite_buf_change(struct mite_dma_descriptor_ring *ring, comedi_async * async)
ring->descriptors_dma_addr = 0;
ring->n_links = 0;
- if (async->prealloc_bufsz == 0) {
+ if (async->prealloc_bufsz == 0)
return 0;
- }
+
n_links = async->prealloc_bufsz >> PAGE_SHIFT;
MDPRINTK("ring->hw_dev=%p, n_links=0x%04x\n", ring->hw_dev, n_links);
@@ -395,9 +395,9 @@ void mite_prep_dma(struct mite_channel *mite_chan,
on e-series boards. */
chcr |= CHCR_BYTE_SWAP_DEVICE | CHCR_BYTE_SWAP_MEMORY;
}
- if (mite_chan->dir == COMEDI_INPUT) {
+ if (mite_chan->dir == COMEDI_INPUT)
chcr |= CHCR_DEV_TO_MEM;
- }
+
writel(chcr, mite->mite_io_addr + MITE_CHCR(mite_chan->channel));
/* to/from memory */
@@ -459,15 +459,15 @@ u32 mite_device_bytes_transferred(struct mite_channel *mite_chan)
return readl(mite->mite_io_addr + MITE_DAR(mite_chan->channel));
}
-u32 mite_bytes_in_transit(struct mite_channel * mite_chan)
+u32 mite_bytes_in_transit(struct mite_channel *mite_chan)
{
struct mite_struct *mite = mite_chan->mite;
return readl(mite->mite_io_addr +
MITE_FCR(mite_chan->channel)) & 0x000000FF;
}
-// returns lower bound for number of bytes transferred from device to memory
-u32 mite_bytes_written_to_memory_lb(struct mite_channel * mite_chan)
+/* returns lower bound for number of bytes transferred from device to memory */
+u32 mite_bytes_written_to_memory_lb(struct mite_channel *mite_chan)
{
u32 device_byte_count;
@@ -475,8 +475,8 @@ u32 mite_bytes_written_to_memory_lb(struct mite_channel * mite_chan)
return device_byte_count - mite_bytes_in_transit(mite_chan);
}
-// returns upper bound for number of bytes transferred from device to memory
-u32 mite_bytes_written_to_memory_ub(struct mite_channel * mite_chan)
+/* returns upper bound for number of bytes transferred from device to memory */
+u32 mite_bytes_written_to_memory_ub(struct mite_channel *mite_chan)
{
u32 in_transit_count;
@@ -484,8 +484,8 @@ u32 mite_bytes_written_to_memory_ub(struct mite_channel * mite_chan)
return mite_device_bytes_transferred(mite_chan) - in_transit_count;
}
-// returns lower bound for number of bytes read from memory for transfer to device
-u32 mite_bytes_read_from_memory_lb(struct mite_channel * mite_chan)
+/* returns lower bound for number of bytes read from memory for transfer to device */
+u32 mite_bytes_read_from_memory_lb(struct mite_channel *mite_chan)
{
u32 device_byte_count;
@@ -493,8 +493,8 @@ u32 mite_bytes_read_from_memory_lb(struct mite_channel * mite_chan)
return device_byte_count + mite_bytes_in_transit(mite_chan);
}
-// returns upper bound for number of bytes read from memory for transfer to device
-u32 mite_bytes_read_from_memory_ub(struct mite_channel * mite_chan)
+/* returns upper bound for number of bytes read from memory for transfer to device */
+u32 mite_bytes_read_from_memory_ub(struct mite_channel *mite_chan)
{
u32 in_transit_count;
@@ -526,14 +526,14 @@ void mite_dma_disarm(struct mite_channel *mite_chan)
writel(chor, mite->mite_io_addr + MITE_CHOR(mite_chan->channel));
}
-int mite_sync_input_dma(struct mite_channel *mite_chan, comedi_async * async)
+int mite_sync_input_dma(struct mite_channel *mite_chan, struct comedi_async * async)
{
int count;
unsigned int nbytes, old_alloc_count;
const unsigned bytes_per_scan = cfc_bytes_per_scan(async->subdevice);
old_alloc_count = async->buf_write_alloc_count;
- // write alloc as much as we can
+ /* write alloc as much as we can */
comedi_buf_write_alloc(async, async->prealloc_bufsz);
nbytes = mite_bytes_written_to_memory_lb(mite_chan);
@@ -547,9 +547,9 @@ int mite_sync_input_dma(struct mite_channel *mite_chan, comedi_async * async)
count = nbytes - async->buf_write_count;
/* it's possible count will be negative due to
* conservative value returned by mite_bytes_written_to_memory_lb */
- if (count <= 0) {
+ if (count <= 0)
return 0;
- }
+
comedi_buf_write_free(async, count);
async->scan_progress += count;
@@ -561,7 +561,7 @@ int mite_sync_input_dma(struct mite_channel *mite_chan, comedi_async * async)
return 0;
}
-int mite_sync_output_dma(struct mite_channel *mite_chan, comedi_async * async)
+int mite_sync_output_dma(struct mite_channel *mite_chan, struct comedi_async * async)
{
int count;
u32 nbytes_ub, nbytes_lb;
@@ -570,7 +570,7 @@ int mite_sync_output_dma(struct mite_channel *mite_chan, comedi_async * async)
async->cmd.stop_arg * cfc_bytes_per_scan(async->subdevice);
old_alloc_count = async->buf_read_alloc_count;
- // read alloc as much as we can
+ /* read alloc as much as we can */
comedi_buf_read_alloc(async, async->prealloc_bufsz);
nbytes_lb = mite_bytes_read_from_memory_lb(mite_chan);
if (async->cmd.stop_src == TRIG_COUNT &&
@@ -586,9 +586,9 @@ int mite_sync_output_dma(struct mite_channel *mite_chan, comedi_async * async)
return -1;
}
count = nbytes_lb - async->buf_read_count;
- if (count <= 0) {
+ if (count <= 0)
return 0;
- }
+
if (count) {
comedi_buf_read_free(async, count);
async->events |= COMEDI_CB_BLOCK;
@@ -753,9 +753,8 @@ static void mite_decode(char **bit_str, unsigned int bits)
int i;
for (i = 31; i >= 0; i--) {
- if (bits & (1 << i)) {
+ if (bits & (1 << i))
printk(" %s", bit_str[i]);
- }
}
printk("\n");
}
diff --git a/drivers/staging/comedi/drivers/mite.h b/drivers/staging/comedi/drivers/mite.h
index b84eafa6ff2b..cdaf8a31688a 100644
--- a/drivers/staging/comedi/drivers/mite.h
+++ b/drivers/staging/comedi/drivers/mite.h
@@ -29,13 +29,13 @@
#define PCI_VENDOR_ID_NATINST 0x1093
-// #define DEBUG_MITE
+/* #define DEBUG_MITE */
#define PCIMIO_COMPAT
#ifdef DEBUG_MITE
-#define MDPRINTK(format,args...) printk(format , ## args )
+#define MDPRINTK(format, args...) printk(format , ## args)
#else
-#define MDPRINTK(format,args...)
+#define MDPRINTK(format, args...)
#endif
#define MAX_MITE_DMA_CHANNELS 8
@@ -142,8 +142,8 @@ void mite_release_channel(struct mite_channel *mite_chan);
unsigned mite_dma_tcr(struct mite_channel *mite_chan);
void mite_dma_arm(struct mite_channel *mite_chan);
void mite_dma_disarm(struct mite_channel *mite_chan);
-int mite_sync_input_dma(struct mite_channel *mite_chan, comedi_async * async);
-int mite_sync_output_dma(struct mite_channel *mite_chan, comedi_async * async);
+int mite_sync_input_dma(struct mite_channel *mite_chan, struct comedi_async * async);
+int mite_sync_output_dma(struct mite_channel *mite_chan, struct comedi_async * async);
u32 mite_bytes_written_to_memory_lb(struct mite_channel *mite_chan);
u32 mite_bytes_written_to_memory_ub(struct mite_channel *mite_chan);
u32 mite_bytes_read_from_memory_lb(struct mite_channel *mite_chan);
@@ -153,7 +153,7 @@ unsigned mite_get_status(struct mite_channel *mite_chan);
int mite_done(struct mite_channel *mite_chan);
#if 0
-unsigned long mite_ll_from_kvmem(struct mite_struct *mite, comedi_async * async,
+unsigned long mite_ll_from_kvmem(struct mite_struct *mite, struct comedi_async * async,
int len);
void mite_setregs(struct mite_struct *mite, unsigned long ll_start, int chan,
int dir);
@@ -162,7 +162,7 @@ void mite_setregs(struct mite_struct *mite, unsigned long ll_start, int chan,
void mite_prep_dma(struct mite_channel *mite_chan,
unsigned int num_device_bits, unsigned int num_memory_bits);
int mite_buf_change(struct mite_dma_descriptor_ring *ring,
- comedi_async * async);
+ struct comedi_async *async);
#ifdef DEBUG_MITE
void mite_print_chsr(unsigned int chsr);
@@ -179,83 +179,83 @@ enum mite_registers {
written and read back. The bits 0x1f always read as 1.
The rest always read as zero. */
MITE_UNKNOWN_DMA_BURST_REG = 0x28,
- MITE_IODWBSR = 0xc0, //IO Device Window Base Size Register
- MITE_IODWBSR_1 = 0xc4, // IO Device Window Base Size Register 1
+ MITE_IODWBSR = 0xc0, /* IO Device Window Base Size Register */
+ MITE_IODWBSR_1 = 0xc4, /* IO Device Window Base Size Register 1 */
MITE_IODWCR_1 = 0xf4,
MITE_PCI_CONFIG_OFFSET = 0x300,
- MITE_CSIGR = 0x460 //chip signature
+ MITE_CSIGR = 0x460 /* chip signature */
};
-static inline int MITE_CHOR(int channel) // channel operation
+static inline int MITE_CHOR(int channel) /* channel operation */
{
return CHAN_OFFSET(channel) + 0x0;
};
-static inline int MITE_CHCR(int channel) // channel control
+static inline int MITE_CHCR(int channel) /* channel control */
{
return CHAN_OFFSET(channel) + 0x4;
};
-static inline int MITE_TCR(int channel) // transfer count
+static inline int MITE_TCR(int channel) /* transfer count */
{
return CHAN_OFFSET(channel) + 0x8;
};
-static inline int MITE_MCR(int channel) // memory configuration
+static inline int MITE_MCR(int channel) /* memory configuration */
{
return CHAN_OFFSET(channel) + 0xc;
};
-static inline int MITE_MAR(int channel) // memory address
+static inline int MITE_MAR(int channel) /* memory address */
{
return CHAN_OFFSET(channel) + 0x10;
};
-static inline int MITE_DCR(int channel) // device configuration
+static inline int MITE_DCR(int channel) /* device configuration */
{
return CHAN_OFFSET(channel) + 0x14;
};
-static inline int MITE_DAR(int channel) // device address
+static inline int MITE_DAR(int channel) /* device address */
{
return CHAN_OFFSET(channel) + 0x18;
};
-static inline int MITE_LKCR(int channel) // link configuration
+static inline int MITE_LKCR(int channel) /* link configuration */
{
return CHAN_OFFSET(channel) + 0x1c;
};
-static inline int MITE_LKAR(int channel) // link address
+static inline int MITE_LKAR(int channel) /* link address */
{
return CHAN_OFFSET(channel) + 0x20;
};
-static inline int MITE_LLKAR(int channel) // see mite section of tnt5002 manual
+static inline int MITE_LLKAR(int channel) /* see mite section of tnt5002 manual */
{
return CHAN_OFFSET(channel) + 0x24;
};
-static inline int MITE_BAR(int channel) // base address
+static inline int MITE_BAR(int channel) /* base address */
{
return CHAN_OFFSET(channel) + 0x28;
};
-static inline int MITE_BCR(int channel) // base count
+static inline int MITE_BCR(int channel) /* base count */
{
return CHAN_OFFSET(channel) + 0x2c;
};
-static inline int MITE_SAR(int channel) // ? address
+static inline int MITE_SAR(int channel) /* ? address */
{
return CHAN_OFFSET(channel) + 0x30;
};
-static inline int MITE_WSCR(int channel) // ?
+static inline int MITE_WSCR(int channel) /* ? */
{
return CHAN_OFFSET(channel) + 0x34;
};
-static inline int MITE_WSER(int channel) // ?
+static inline int MITE_WSER(int channel) /* ? */
{
return CHAN_OFFSET(channel) + 0x38;
};
-static inline int MITE_CHSR(int channel) // channel status
+static inline int MITE_CHSR(int channel) /* channel status */
{
return CHAN_OFFSET(channel) + 0x3c;
};
-static inline int MITE_FCR(int channel) // fifo count
+static inline int MITE_FCR(int channel) /* fifo count */
{
return CHAN_OFFSET(channel) + 0x40;
};
enum MITE_IODWBSR_bits {
- WENAB = 0x80, // window enable
+ WENAB = 0x80, /* window enable */
};
static inline unsigned MITE_IODWBSR_1_WSIZE_bits(unsigned size)
@@ -276,23 +276,23 @@ static inline int mite_csigr_version(u32 csigr_bits)
return csigr_bits & 0xf;
};
static inline int mite_csigr_type(u32 csigr_bits)
-{ // original mite = 0, minimite = 1
+{ /* original mite = 0, minimite = 1 */
return (csigr_bits >> 4) & 0xf;
};
static inline int mite_csigr_mmode(u32 csigr_bits)
-{ // mite mode, minimite = 1
+{ /* mite mode, minimite = 1 */
return (csigr_bits >> 8) & 0x3;
};
static inline int mite_csigr_imode(u32 csigr_bits)
-{ // cpu port interface mode, pci = 0x3
+{ /* cpu port interface mode, pci = 0x3 */
return (csigr_bits >> 12) & 0x3;
};
static inline int mite_csigr_dmac(u32 csigr_bits)
-{ // number of dma channels
+{ /* number of dma channels */
return (csigr_bits >> 16) & 0xf;
};
static inline int mite_csigr_wpdep(u32 csigr_bits)
-{ // write post fifo depth
+{ /* write post fifo depth */
unsigned int wpdep_bits = (csigr_bits >> 20) & 0x7;
if (wpdep_bits == 0)
return 0;
@@ -304,7 +304,7 @@ static inline int mite_csigr_wins(u32 csigr_bits)
return (csigr_bits >> 24) & 0x1f;
};
static inline int mite_csigr_iowins(u32 csigr_bits)
-{ // number of io windows
+{ /* number of io windows */
return (csigr_bits >> 29) & 0x7;
};
diff --git a/drivers/staging/comedi/drivers/mpc624.c b/drivers/staging/comedi/drivers/mpc624.c
new file mode 100644
index 000000000000..a151602929d9
--- /dev/null
+++ b/drivers/staging/comedi/drivers/mpc624.c
@@ -0,0 +1,386 @@
+/*
+ comedi/drivers/mpc624.c
+ Hardware driver for a Micro/sys inc. MPC-624 PC/104 board
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: mpc624
+Description: Micro/sys MPC-624 PC/104 board
+Devices: [Micro/sys] MPC-624 (mpc624)
+Author: Stanislaw Raczynski <sraczynski@op.pl>
+Updated: Thu, 15 Sep 2005 12:01:18 +0200
+Status: working
+
+ The Micro/sys MPC-624 board is based on the LTC2440 24-bit sigma-delta
+ ADC chip.
+
+ Subdevices supported by the driver:
+ - Analog In: supported
+ - Digital I/O: not supported
+ - LEDs: not supported
+ - EEPROM: not supported
+
+Configuration Options:
+ [0] - I/O base address
+ [1] - convertion rate
+ Convertion rate RMS noise Effective Number Of Bits
+ 0 3.52kHz 23uV 17
+ 1 1.76kHz 3.5uV 20
+ 2 880Hz 2uV 21.3
+ 3 440Hz 1.4uV 21.8
+ 4 220Hz 1uV 22.4
+ 5 110Hz 750uV 22.9
+ 6 55Hz 510nV 23.4
+ 7 27.5Hz 375nV 24
+ 8 13.75Hz 250nV 24.4
+ 9 6.875Hz 200nV 24.6
+ [2] - voltage range
+ 0 -1.01V .. +1.01V
+ 1 -10.1V .. +10.1V
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+
+// Consecutive I/O port addresses
+#define MPC624_SIZE 16
+
+// Offsets of different ports
+#define MPC624_MASTER_CONTROL 0 // not used
+#define MPC624_GNMUXCH 1 // Gain, Mux, Channel of ADC
+#define MPC624_ADC 2 // read/write to/from ADC
+#define MPC624_EE 3 // read/write to/from serial EEPROM via I2C
+#define MPC624_LEDS 4 // write to LEDs
+#define MPC624_DIO 5 // read/write to/from digital I/O ports
+#define MPC624_IRQ_MASK 6 // IRQ masking enable/disable
+
+// Register bits' names
+#define MPC624_ADBUSY (1<<5)
+#define MPC624_ADSDO (1<<4)
+#define MPC624_ADFO (1<<3)
+#define MPC624_ADCS (1<<2)
+#define MPC624_ADSCK (1<<1)
+#define MPC624_ADSDI (1<<0)
+
+// SDI Speed/Resolution Programming bits
+#define MPC624_OSR4 (1<<31)
+#define MPC624_OSR3 (1<<30)
+#define MPC624_OSR2 (1<<29)
+#define MPC624_OSR1 (1<<28)
+#define MPC624_OSR0 (1<<27)
+
+// 32-bit output value bits' names
+#define MPC624_EOC_BIT (1<<31)
+#define MPC624_DMY_BIT (1<<30)
+#define MPC624_SGN_BIT (1<<29)
+
+// Convertion speeds
+/* OSR4 OSR3 OSR2 OSR1 OSR0 Convertion rate RMS noise ENOB^
+ * X 0 0 0 1 3.52kHz 23uV 17
+ * X 0 0 1 0 1.76kHz 3.5uV 20
+ * X 0 0 1 1 880Hz 2uV 21.3
+ * X 0 1 0 0 440Hz 1.4uV 21.8
+ * X 0 1 0 1 220Hz 1uV 22.4
+ * X 0 1 1 0 110Hz 750uV 22.9
+ * X 0 1 1 1 55Hz 510nV 23.4
+ * X 1 0 0 0 27.5Hz 375nV 24
+ * X 1 0 0 1 13.75Hz 250nV 24.4
+ * X 1 1 1 1 6.875Hz 200nV 24.6
+ *
+ * ^ - Effective Number Of Bits
+ */
+
+#define MPC624_SPEED_3_52_kHz (MPC624_OSR4 | MPC624_OSR0)
+#define MPC624_SPEED_1_76_kHz (MPC624_OSR4 | MPC624_OSR1 )
+#define MPC624_SPEED_880_Hz (MPC624_OSR4 | MPC624_OSR1 | MPC624_OSR0)
+#define MPC624_SPEED_440_Hz (MPC624_OSR4 | MPC624_OSR2 )
+#define MPC624_SPEED_220_Hz (MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR0)
+#define MPC624_SPEED_110_Hz (MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR1 )
+#define MPC624_SPEED_55_Hz (MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR1 | MPC624_OSR0)
+#define MPC624_SPEED_27_5_Hz (MPC624_OSR4 | MPC624_OSR3 )
+#define MPC624_SPEED_13_75_Hz (MPC624_OSR4 | MPC624_OSR3 | MPC624_OSR0)
+#define MPC624_SPEED_6_875_Hz (MPC624_OSR4 | MPC624_OSR3 | MPC624_OSR2 | MPC624_OSR1 | MPC624_OSR0)
+//----------------------------------------------------------------------------
+struct skel_private {
+
+ unsigned long int ulConvertionRate; // set by mpc624_attach() from driver's parameters
+};
+
+
+#define devpriv ((struct skel_private *)dev->private)
+//----------------------------------------------------------------------------
+static const struct comedi_lrange range_mpc624_bipolar1 = {
+ 1,
+ {
+// BIP_RANGE(1.01) // this is correct,
+ // but my MPC-624 actually seems to have a range of 2.02
+ BIP_RANGE(2.02)
+ }
+};
+static const struct comedi_lrange range_mpc624_bipolar10 = {
+ 1,
+ {
+// BIP_RANGE(10.1) // this is correct,
+ // but my MPC-624 actually seems to have a range of 20.2
+ BIP_RANGE(20.2)
+ }
+};
+
+//----------------------------------------------------------------------------
+static int mpc624_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int mpc624_detach(struct comedi_device * dev);
+//----------------------------------------------------------------------------
+static struct comedi_driver driver_mpc624 = {
+ driver_name:"mpc624",
+ module:THIS_MODULE,
+ attach:mpc624_attach,
+ detach:mpc624_detach
+};
+
+//----------------------------------------------------------------------------
+static int mpc624_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+//----------------------------------------------------------------------------
+static int mpc624_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+
+ iobase = it->options[0];
+ rt_printk("comedi%d: mpc624 [0x%04lx, ", dev->minor, iobase);
+ if (request_region(iobase, MPC624_SIZE, "mpc624") == NULL) {
+ rt_printk("I/O port(s) in use\n");
+ return -EIO;
+ }
+
+ dev->iobase = iobase;
+ dev->board_name = "mpc624";
+
+ // Private structure initialization
+ if (alloc_private(dev, sizeof(struct skel_private)) < 0)
+ return -ENOMEM;
+
+ switch (it->options[1]) {
+ case 0:
+ devpriv->ulConvertionRate = MPC624_SPEED_3_52_kHz;
+ rt_printk("3.52 kHz, ");
+ break;
+ case 1:
+ devpriv->ulConvertionRate = MPC624_SPEED_1_76_kHz;
+ rt_printk("1.76 kHz, ");
+ break;
+ case 2:
+ devpriv->ulConvertionRate = MPC624_SPEED_880_Hz;
+ rt_printk("880 Hz, ");
+ break;
+ case 3:
+ devpriv->ulConvertionRate = MPC624_SPEED_440_Hz;
+ rt_printk("440 Hz, ");
+ break;
+ case 4:
+ devpriv->ulConvertionRate = MPC624_SPEED_220_Hz;
+ rt_printk("220 Hz, ");
+ break;
+ case 5:
+ devpriv->ulConvertionRate = MPC624_SPEED_110_Hz;
+ rt_printk("110 Hz, ");
+ break;
+ case 6:
+ devpriv->ulConvertionRate = MPC624_SPEED_55_Hz;
+ rt_printk("55 Hz, ");
+ break;
+ case 7:
+ devpriv->ulConvertionRate = MPC624_SPEED_27_5_Hz;
+ rt_printk("27.5 Hz, ");
+ break;
+ case 8:
+ devpriv->ulConvertionRate = MPC624_SPEED_13_75_Hz;
+ rt_printk("13.75 Hz, ");
+ break;
+ case 9:
+ devpriv->ulConvertionRate = MPC624_SPEED_6_875_Hz;
+ rt_printk("6.875 Hz, ");
+ break;
+ default:
+ rt_printk
+ ("illegal convertion rate setting! Valid numbers are 0..9. Using 9 => 6.875 Hz, ");
+ devpriv->ulConvertionRate = MPC624_SPEED_3_52_kHz;
+ }
+
+ // Subdevices structures
+ if (alloc_subdevices(dev, 1) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_DIFF;
+ s->n_chan = 8;
+ switch (it->options[1]) {
+ default:
+ s->maxdata = 0x3FFFFFFF;
+ rt_printk("30 bit, ");
+ }
+
+ switch (it->options[1]) {
+ case 0:
+ s->range_table = &range_mpc624_bipolar1;
+ rt_printk("1.01V]: ");
+ break;
+ default:
+ s->range_table = &range_mpc624_bipolar10;
+ rt_printk("10.1V]: ");
+ }
+ s->len_chanlist = 1;
+ s->insn_read = mpc624_ai_rinsn;
+
+ rt_printk("attached\n");
+
+ return 1;
+}
+
+static int mpc624_detach(struct comedi_device * dev)
+{
+ rt_printk("comedi%d: mpc624: remove\n", dev->minor);
+
+ if (dev->iobase)
+ release_region(dev->iobase, MPC624_SIZE);
+
+ return 0;
+}
+
+// Timeout 200ms
+#define TIMEOUT 200
+
+static int mpc624_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, i;
+ unsigned long int data_in, data_out;
+ unsigned char ucPort;
+
+ // WARNING: We always write 0 to GNSWA bit, so the channel range is +-/10.1Vdc
+ outb(insn->chanspec, dev->iobase + MPC624_GNMUXCH);
+// rt_printk("Channel %d: \n", insn->chanspec);
+ if (!insn->n) {
+ rt_printk("MPC624: Warning, no data to aquire\n");
+ return 0;
+ }
+
+ for (n = 0; n < insn->n; n++) {
+ // Trigger the convertion
+ outb(MPC624_ADSCK, dev->iobase + MPC624_ADC);
+ comedi_udelay(1);
+ outb(MPC624_ADCS | MPC624_ADSCK, dev->iobase + MPC624_ADC);
+ comedi_udelay(1);
+ outb(0, dev->iobase + MPC624_ADC);
+ comedi_udelay(1);
+
+ // Wait for the convertion to end
+ for (i = 0; i < TIMEOUT; i++) {
+ ucPort = inb(dev->iobase + MPC624_ADC);
+ if (ucPort & MPC624_ADBUSY)
+ comedi_udelay(1000);
+ else
+ break;
+ }
+ if (i == TIMEOUT) {
+ rt_printk("MPC624: timeout (%dms)\n", TIMEOUT);
+ data[n] = 0;
+ return -ETIMEDOUT;
+ }
+ // Start reading data
+ data_in = 0;
+ data_out = devpriv->ulConvertionRate;
+ comedi_udelay(1);
+ for (i = 0; i < 32; i++) {
+ // Set the clock low
+ outb(0, dev->iobase + MPC624_ADC);
+ comedi_udelay(1);
+
+ if (data_out & (1 << 31)) // the next bit is a 1
+ {
+ // Set the ADSDI line (send to MPC624)
+ outb(MPC624_ADSDI, dev->iobase + MPC624_ADC);
+ comedi_udelay(1);
+ // Set the clock high
+ outb(MPC624_ADSCK | MPC624_ADSDI,
+ dev->iobase + MPC624_ADC);
+ } else // the next bit is a 0
+ {
+ // Set the ADSDI line (send to MPC624)
+ outb(0, dev->iobase + MPC624_ADC);
+ comedi_udelay(1);
+ // Set the clock high
+ outb(MPC624_ADSCK, dev->iobase + MPC624_ADC);
+ }
+ // Read ADSDO on high clock (receive from MPC624)
+ comedi_udelay(1);
+ data_in <<= 1;
+ data_in |=
+ (inb(dev->iobase +
+ MPC624_ADC) & MPC624_ADSDO) >> 4;
+ comedi_udelay(1);
+
+ data_out <<= 1;
+ }
+
+ // Received 32-bit long value consist of:
+ // 31: EOC (End Of Transmission) bit - should be 0
+ // 30: DMY (Dummy) bit - should be 0
+ // 29: SIG (Sign) bit - 1 if the voltage is positive, 0 if negative
+ // 28: MSB (Most Significant Bit) - the first bit of convertion result
+ // ....
+ // 05: LSB (Least Significant Bit) - the last bit of convertion result
+ // 04: sub-LSB - sub-LSBs are basically noise, but when
+ // 03: sub-LSB averaged properly, they can increase convertion
+ // 02: sub-LSB precision up to 29 bits; they can be discarded
+ // 01: sub-LSB without loss of resolution.
+ // 00: sub-LSB
+
+ if (data_in & MPC624_EOC_BIT)
+ rt_printk("MPC624: EOC bit is set (data_in=%lu)!",
+ data_in);
+ if (data_in & MPC624_DMY_BIT)
+ rt_printk("MPC624: DMY bit is set (data_in=%lu)!",
+ data_in);
+ if (data_in & MPC624_SGN_BIT) // check the sign bit
+ { // The voltage is positive
+ data_in &= 0x3FFFFFFF; // EOC and DMY should be 0, but we will mask them out just to be sure
+ data[n] = data_in; // comedi operates on unsigned numbers, so we don't clear the SGN bit
+ // SGN bit is still set! It's correct, since we're converting to unsigned.
+ } else { // The voltage is negative
+ // data_in contains a number in 30-bit two's complement code and we must deal with it
+ data_in |= MPC624_SGN_BIT;
+ data_in = ~data_in;
+ data_in += 1;
+ data_in &= ~(MPC624_EOC_BIT | MPC624_DMY_BIT);
+ // clear EOC and DMY bits
+ data_in = 0x20000000 - data_in;
+ data[n] = data_in;
+ }
+ }
+
+ // Return the number of samples read/written
+ return n;
+}
+
+COMEDI_INITCLEANUP(driver_mpc624);
diff --git a/drivers/staging/comedi/drivers/mpc8260cpm.c b/drivers/staging/comedi/drivers/mpc8260cpm.c
new file mode 100644
index 000000000000..bac0a7bb9cbd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/mpc8260cpm.c
@@ -0,0 +1,169 @@
+/*
+ comedi/drivers/mpc8260.c
+ driver for digital I/O pins on the MPC 8260 CPM module
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000,2001 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: mpc8260cpm
+Description: MPC8260 CPM module generic digital I/O lines
+Devices: [Motorola] MPC8260 CPM (mpc8260cpm)
+Author: ds
+Status: experimental
+Updated: Sat, 16 Mar 2002 17:34:48 -0800
+
+This driver is specific to the Motorola MPC8260 processor, allowing
+you to access the processor's generic digital I/O lines.
+
+It is apparently missing some code.
+*/
+
+#include "../comedidev.h"
+
+extern unsigned long mpc8260_dio_reserved[4];
+
+struct mpc8260cpm_private {
+
+ int data;
+
+};
+
+#define devpriv ((struct mpc8260cpm_private *)dev->private)
+
+static int mpc8260cpm_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int mpc8260cpm_detach(struct comedi_device * dev);
+static struct comedi_driver driver_mpc8260cpm = {
+ driver_name:"mpc8260cpm",
+ module:THIS_MODULE,
+ attach:mpc8260cpm_attach,
+ detach:mpc8260cpm_detach,
+};
+
+COMEDI_INITCLEANUP(driver_mpc8260cpm);
+
+static int mpc8260cpm_dio_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int mpc8260cpm_dio_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int mpc8260cpm_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int i;
+
+ printk("comedi%d: mpc8260cpm: ", dev->minor);
+
+ dev->board_ptr = mpc8260cpm_boards + dev->board;
+
+ dev->board_name = thisboard->name;
+
+ if (alloc_private(dev, sizeof(struct mpc8260cpm_private)) < 0)
+ return -ENOMEM;
+
+ if (alloc_subdevices(dev, 4) < 0)
+ return -ENOMEM;
+
+ for (i = 0; i < 4; i++) {
+ s = dev->subdevices + i;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 32;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_config = mpc8260cpm_dio_config;
+ s->insn_bits = mpc8260cpm_dio_bits;
+ }
+
+ return 1;
+}
+
+static int mpc8260cpm_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: mpc8260cpm: remove\n", dev->minor);
+
+ return 0;
+}
+
+static unsigned long *cpm_pdat(int port)
+{
+ switch (port) {
+ case 0:
+ return &io->iop_pdata;
+ case 1:
+ return &io->iop_pdatb;
+ case 2:
+ return &io->iop_pdatc;
+ case 3:
+ return &io->iop_pdatd;
+ }
+}
+
+static int mpc8260cpm_dio_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ unsigned int d;
+ unsigned int mask;
+ int port;
+
+ port = (int)s->private;
+ mask = 1 << CR_CHAN(insn->chanspec);
+ if (mask & cpm_reserved_bits[port]) {
+ return -EINVAL;
+ }
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= mask;
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~mask;
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (port) {
+ case 0:
+ return &io->iop_pdira;
+ case 1:
+ return &io->iop_pdirb;
+ case 2:
+ return &io->iop_pdirc;
+ case 3:
+ return &io->iop_pdird;
+ }
+
+ return 1;
+}
+
+static int mpc8260cpm_dio_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int port;
+ unsigned long *p;
+
+ p = cpm_pdat((int)s->private);
+
+ return 2;
+}
diff --git a/drivers/staging/comedi/drivers/multiq3.c b/drivers/staging/comedi/drivers/multiq3.c
new file mode 100644
index 000000000000..9e47574171de
--- /dev/null
+++ b/drivers/staging/comedi/drivers/multiq3.c
@@ -0,0 +1,333 @@
+/*
+ comedi/drivers/multiq3.c
+ Hardware driver for Quanser Consulting MultiQ-3 board
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+/*
+Driver: multiq3
+Description: Quanser Consulting MultiQ-3
+Author: Anders Blomdell <anders.blomdell@control.lth.se>
+Status: works
+Devices: [Quanser Consulting] MultiQ-3 (multiq3)
+
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+#define MULTIQ3_SIZE 16
+
+/*
+ * MULTIQ-3 port offsets
+ */
+#define MULTIQ3_DIGIN_PORT 0
+#define MULTIQ3_DIGOUT_PORT 0
+#define MULTIQ3_DAC_DATA 2
+#define MULTIQ3_AD_DATA 4
+#define MULTIQ3_AD_CS 4
+#define MULTIQ3_STATUS 6
+#define MULTIQ3_CONTROL 6
+#define MULTIQ3_CLK_DATA 8
+#define MULTIQ3_ENC_DATA 12
+#define MULTIQ3_ENC_CONTROL 14
+
+/*
+ * flags for CONTROL register
+ */
+#define MULTIQ3_AD_MUX_EN 0x0040
+#define MULTIQ3_AD_AUTOZ 0x0080
+#define MULTIQ3_AD_AUTOCAL 0x0100
+#define MULTIQ3_AD_SH 0x0200
+#define MULTIQ3_AD_CLOCK_4M 0x0400
+#define MULTIQ3_DA_LOAD 0x1800
+
+#define MULTIQ3_CONTROL_MUST 0x0600
+
+/*
+ * flags for STATUS register
+ */
+#define MULTIQ3_STATUS_EOC 0x008
+#define MULTIQ3_STATUS_EOC_I 0x010
+
+/*
+ * flags for encoder control
+ */
+#define MULTIQ3_CLOCK_DATA 0x00
+#define MULTIQ3_CLOCK_SETUP 0x18
+#define MULTIQ3_INPUT_SETUP 0x41
+#define MULTIQ3_QUAD_X4 0x38
+#define MULTIQ3_BP_RESET 0x01
+#define MULTIQ3_CNTR_RESET 0x02
+#define MULTIQ3_TRSFRPR_CTR 0x08
+#define MULTIQ3_TRSFRCNTR_OL 0x10
+#define MULTIQ3_EFLAG_RESET 0x06
+
+#define MULTIQ3_TIMEOUT 30
+
+static int multiq3_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int multiq3_detach(struct comedi_device * dev);
+static struct comedi_driver driver_multiq3 = {
+ driver_name:"multiq3",
+ module:THIS_MODULE,
+ attach:multiq3_attach,
+ detach:multiq3_detach,
+};
+
+COMEDI_INITCLEANUP(driver_multiq3);
+
+struct multiq3_private {
+ unsigned int ao_readback[2];
+};
+#define devpriv ((struct multiq3_private *)dev->private)
+
+static int multiq3_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i, n;
+ int chan;
+ unsigned int hi, lo;
+
+ chan = CR_CHAN(insn->chanspec);
+ outw(MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3),
+ dev->iobase + MULTIQ3_CONTROL);
+
+ for (i = 0; i < MULTIQ3_TIMEOUT; i++) {
+ if (inw(dev->iobase + MULTIQ3_STATUS) & MULTIQ3_STATUS_EOC)
+ break;
+ }
+ if (i == MULTIQ3_TIMEOUT)
+ return -ETIMEDOUT;
+
+ for (n = 0; n < insn->n; n++) {
+ outw(0, dev->iobase + MULTIQ3_AD_CS);
+ for (i = 0; i < MULTIQ3_TIMEOUT; i++) {
+ if (inw(dev->iobase +
+ MULTIQ3_STATUS) & MULTIQ3_STATUS_EOC_I)
+ break;
+ }
+ if (i == MULTIQ3_TIMEOUT)
+ return -ETIMEDOUT;
+
+ hi = inb(dev->iobase + MULTIQ3_AD_CS);
+ lo = inb(dev->iobase + MULTIQ3_AD_CS);
+ data[n] = (((hi << 8) | lo) + 0x1000) & 0x1fff;
+ }
+
+ return n;
+}
+
+static int multiq3_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++) {
+ data[i] = devpriv->ao_readback[chan];
+ }
+
+ return i;
+}
+
+static int multiq3_ao_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++) {
+ outw(MULTIQ3_CONTROL_MUST | MULTIQ3_DA_LOAD | chan,
+ dev->iobase + MULTIQ3_CONTROL);
+ outw(data[i], dev->iobase + MULTIQ3_DAC_DATA);
+ outw(MULTIQ3_CONTROL_MUST, dev->iobase + MULTIQ3_CONTROL);
+
+ devpriv->ao_readback[chan] = data[i];
+ }
+
+ return i;
+}
+
+static int multiq3_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ data[1] = inw(dev->iobase + MULTIQ3_DIGIN_PORT);
+
+ return 2;
+}
+
+static int multiq3_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ outw(s->state, dev->iobase + MULTIQ3_DIGOUT_PORT);
+
+ data[1] = s->state;
+
+ return 2;
+}
+
+static int multiq3_encoder_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int chan = CR_CHAN(insn->chanspec);
+ int control = MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3);
+
+ for (n = 0; n < insn->n; n++) {
+ int value;
+ outw(control, dev->iobase + MULTIQ3_CONTROL);
+ outb(MULTIQ3_BP_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
+ outb(MULTIQ3_TRSFRCNTR_OL, dev->iobase + MULTIQ3_ENC_CONTROL);
+ value = inb(dev->iobase + MULTIQ3_ENC_DATA);
+ value |= (inb(dev->iobase + MULTIQ3_ENC_DATA) << 8);
+ value |= (inb(dev->iobase + MULTIQ3_ENC_DATA) << 16);
+ data[n] = (value + 0x800000) & 0xffffff;
+ }
+
+ return n;
+}
+
+static void encoder_reset(struct 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(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int result = 0;
+ unsigned long iobase;
+ unsigned int irq;
+ struct comedi_subdevice *s;
+
+ iobase = it->options[0];
+ printk("comedi%d: multiq3: 0x%04lx ", dev->minor, iobase);
+ if (!request_region(iobase, MULTIQ3_SIZE, "multiq3")) {
+ printk("comedi%d: I/O port conflict\n", dev->minor);
+ return -EIO;
+ }
+
+ dev->iobase = iobase;
+
+ irq = it->options[1];
+ if (irq) {
+ printk("comedi%d: irq = %u ignored\n", dev->minor, irq);
+ } else {
+ printk("comedi%d: no irq\n", dev->minor);
+ }
+ dev->board_name = "multiq3";
+ result = alloc_subdevices(dev, 5);
+ if (result < 0)
+ return result;
+
+ result = alloc_private(dev, sizeof(struct multiq3_private));
+ if (result < 0)
+ return result;
+
+ s = dev->subdevices + 0;
+ /* ai subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 8;
+ s->insn_read = multiq3_ai_insn_read;
+ s->maxdata = 0x1fff;
+ s->range_table = &range_bipolar5;
+
+ s = dev->subdevices + 1;
+ /* ao subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 8;
+ s->insn_read = multiq3_ao_insn_read;
+ s->insn_write = multiq3_ao_insn_write;
+ s->maxdata = 0xfff;
+ s->range_table = &range_bipolar5;
+
+ s = dev->subdevices + 2;
+ /* di subdevice */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->insn_bits = multiq3_di_insn_bits;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+
+ s = dev->subdevices + 3;
+ /* do subdevice */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->insn_bits = multiq3_do_insn_bits;
+ s->maxdata = 1;
+ s->range_table = &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->insn_read = multiq3_encoder_insn_read;
+ s->maxdata = 0xffffff;
+ s->range_table = &range_unknown;
+
+ encoder_reset(dev);
+
+ return 0;
+}
+
+static int multiq3_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: multiq3: remove\n", dev->minor);
+
+ if (dev->iobase) {
+ release_region(dev->iobase, MULTIQ3_SIZE);
+ }
+ if (dev->irq) {
+ free_irq(dev->irq, dev);
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/ni_6527.c b/drivers/staging/comedi/drivers/ni_6527.c
new file mode 100644
index 000000000000..e01ecb6760f9
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_6527.c
@@ -0,0 +1,489 @@
+/*
+ comedi/drivers/ni_6527.c
+ driver for National Instruments PCI-6527
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999,2002,2003 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: ni_6527
+Description: National Instruments 6527
+Author: ds
+Status: works
+Devices: [National Instruments] PCI-6527 (ni6527), PXI-6527
+Updated: Sat, 25 Jan 2003 13:24:40 -0800
+
+
+*/
+
+/*
+ Manuals (available from ftp://ftp.natinst.com/support/manuals)
+
+ 370106b.pdf 6527 Register Level Programmer Manual
+
+ */
+
+#define DEBUG 1
+#define DEBUG_FLAGS
+
+#include "../comedidev.h"
+
+#include "mite.h"
+
+#define NI6527_DIO_SIZE 4096
+#define NI6527_MITE_SIZE 4096
+
+#define Port_Register(x) (0x00+(x))
+#define ID_Register 0x06
+
+#define Clear_Register 0x07
+#define ClrEdge 0x08
+#define ClrOverflow 0x04
+#define ClrFilter 0x02
+#define ClrInterval 0x01
+
+#define Filter_Interval(x) (0x08+(x))
+#define Filter_Enable(x) (0x0c+(x))
+
+#define Change_Status 0x14
+#define MasterInterruptStatus 0x04
+#define Overflow 0x02
+#define EdgeStatus 0x01
+
+#define Master_Interrupt_Control 0x15
+#define FallingEdgeIntEnable 0x10
+#define RisingEdgeIntEnable 0x08
+#define MasterInterruptEnable 0x04
+#define OverflowIntEnable 0x02
+#define EdgeIntEnable 0x01
+
+#define Rising_Edge_Detection_Enable(x) (0x018+(x))
+#define Falling_Edge_Detection_Enable(x) (0x020+(x))
+
+static int ni6527_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int ni6527_detach(struct comedi_device * dev);
+static struct comedi_driver driver_ni6527 = {
+ driver_name:"ni6527",
+ module:THIS_MODULE,
+ attach:ni6527_attach,
+ detach:ni6527_detach,
+};
+
+struct ni6527_board {
+
+ int dev_id;
+ const char *name;
+};
+
+static const struct ni6527_board ni6527_boards[] = {
+ {
+ dev_id: 0x2b20,
+ name: "pci-6527",
+ },
+ {
+ dev_id: 0x2b10,
+ name: "pxi-6527",
+ },
+};
+
+#define n_ni6527_boards (sizeof(ni6527_boards)/sizeof(ni6527_boards[0]))
+#define this_board ((const struct ni6527_board *)dev->board_ptr)
+
+static DEFINE_PCI_DEVICE_TABLE(ni6527_pci_table) = {
+ {PCI_VENDOR_ID_NATINST, 0x2b10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x2b20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, ni6527_pci_table);
+
+struct ni6527_private {
+ struct mite_struct *mite;
+ unsigned int filter_interval;
+ unsigned int filter_enable;
+};
+
+#define devpriv ((struct ni6527_private *)dev->private)
+
+static int ni6527_find_device(struct comedi_device * dev, int bus, int slot);
+
+static int ni6527_di_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+ unsigned int interval;
+
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (data[0] != INSN_CONFIG_FILTER)
+ return -EINVAL;
+
+ if (data[1]) {
+ interval = (data[1] + 100) / 200;
+ data[1] = interval * 200;
+
+ if (interval != devpriv->filter_interval) {
+ writeb(interval & 0xff,
+ devpriv->mite->daq_io_addr +
+ Filter_Interval(0));
+ writeb((interval >> 8) & 0xff,
+ devpriv->mite->daq_io_addr +
+ Filter_Interval(1));
+ writeb((interval >> 16) & 0x0f,
+ devpriv->mite->daq_io_addr +
+ Filter_Interval(2));
+
+ writeb(ClrInterval,
+ devpriv->mite->daq_io_addr + Clear_Register);
+
+ devpriv->filter_interval = interval;
+ }
+
+ devpriv->filter_enable |= 1 << chan;
+ } else {
+ devpriv->filter_enable &= ~(1 << chan);
+ }
+
+ writeb(devpriv->filter_enable,
+ devpriv->mite->daq_io_addr + Filter_Enable(0));
+ writeb(devpriv->filter_enable >> 8,
+ devpriv->mite->daq_io_addr + Filter_Enable(1));
+ writeb(devpriv->filter_enable >> 16,
+ devpriv->mite->daq_io_addr + Filter_Enable(2));
+
+ return 2;
+}
+
+static int ni6527_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ data[1] = readb(devpriv->mite->daq_io_addr + Port_Register(0));
+ data[1] |= readb(devpriv->mite->daq_io_addr + Port_Register(1)) << 8;
+ data[1] |= readb(devpriv->mite->daq_io_addr + Port_Register(2)) << 16;
+
+ return 2;
+}
+
+static int ni6527_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+
+ /* The open relay state on the board cooresponds to 1,
+ * but in Comedi, it is represented by 0. */
+ if (data[0] & 0x0000ff) {
+ writeb((s->state ^ 0xff),
+ devpriv->mite->daq_io_addr + Port_Register(3));
+ }
+ if (data[0] & 0x00ff00) {
+ writeb((s->state >> 8) ^ 0xff,
+ devpriv->mite->daq_io_addr + Port_Register(4));
+ }
+ if (data[0] & 0xff0000) {
+ writeb((s->state >> 16) ^ 0xff,
+ devpriv->mite->daq_io_addr + Port_Register(5));
+ }
+ }
+ data[1] = s->state;
+
+ return 2;
+}
+
+static irqreturn_t ni6527_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 2;
+ unsigned int status;
+
+ status = readb(devpriv->mite->daq_io_addr + Change_Status);
+ if ((status & MasterInterruptStatus) == 0)
+ return IRQ_NONE;
+ if ((status & EdgeStatus) == 0)
+ return IRQ_NONE;
+
+ writeb(ClrEdge | ClrOverflow,
+ devpriv->mite->daq_io_addr + Clear_Register);
+
+ comedi_buf_put(s->async, 0);
+ s->async->events |= COMEDI_CB_EOS;
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+}
+
+static int ni6527_intr_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_OTHER;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_FOLLOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_end_arg != 1) {
+ cmd->scan_end_arg = 1;
+ err++;
+ }
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int ni6527_intr_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ //struct comedi_cmd *cmd = &s->async->cmd;
+
+ writeb(ClrEdge | ClrOverflow,
+ devpriv->mite->daq_io_addr + Clear_Register);
+ writeb(FallingEdgeIntEnable | RisingEdgeIntEnable |
+ MasterInterruptEnable | EdgeIntEnable,
+ devpriv->mite->daq_io_addr + Master_Interrupt_Control);
+
+ return 0;
+}
+
+static int ni6527_intr_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ writeb(0x00, devpriv->mite->daq_io_addr + Master_Interrupt_Control);
+
+ return 0;
+}
+
+static int ni6527_intr_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n < 1)
+ return -EINVAL;
+
+ data[1] = 0;
+ return 2;
+}
+
+static int ni6527_intr_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n < 1)
+ return -EINVAL;
+ if (data[0] != INSN_CONFIG_CHANGE_NOTIFY)
+ return -EINVAL;
+
+ writeb(data[1],
+ devpriv->mite->daq_io_addr + Rising_Edge_Detection_Enable(0));
+ writeb(data[1] >> 8,
+ devpriv->mite->daq_io_addr + Rising_Edge_Detection_Enable(1));
+ writeb(data[1] >> 16,
+ devpriv->mite->daq_io_addr + Rising_Edge_Detection_Enable(2));
+
+ writeb(data[2],
+ devpriv->mite->daq_io_addr + Falling_Edge_Detection_Enable(0));
+ writeb(data[2] >> 8,
+ devpriv->mite->daq_io_addr + Falling_Edge_Detection_Enable(1));
+ writeb(data[2] >> 16,
+ devpriv->mite->daq_io_addr + Falling_Edge_Detection_Enable(2));
+
+ return 2;
+}
+
+static int ni6527_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int ret;
+
+ printk("comedi%d: ni6527:", dev->minor);
+
+ if ((ret = alloc_private(dev, sizeof(struct ni6527_private))) < 0)
+ return ret;
+
+ ret = ni6527_find_device(dev, it->options[0], it->options[1]);
+ if (ret < 0)
+ return ret;
+
+ ret = mite_setup(devpriv->mite);
+ if (ret < 0) {
+ printk("error setting up mite\n");
+ return ret;
+ }
+
+ dev->board_name = this_board->name;
+ printk(" %s", dev->board_name);
+
+ printk(" ID=0x%02x", readb(devpriv->mite->daq_io_addr + ID_Register));
+
+ if ((ret = alloc_subdevices(dev, 3)) < 0)
+ return ret;
+
+ s = dev->subdevices + 0;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 24;
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+ s->insn_config = ni6527_di_insn_config;
+ s->insn_bits = ni6527_di_insn_bits;
+
+ s = dev->subdevices + 1;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 24;
+ s->range_table = &range_unknown; /* FIXME: actually conductance */
+ s->maxdata = 1;
+ s->insn_bits = ni6527_do_insn_bits;
+
+ s = dev->subdevices + 2;
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+ s->n_chan = 1;
+ s->range_table = &range_unknown;
+ s->maxdata = 1;
+ s->do_cmdtest = ni6527_intr_cmdtest;
+ s->do_cmd = ni6527_intr_cmd;
+ s->cancel = ni6527_intr_cancel;
+ s->insn_bits = ni6527_intr_insn_bits;
+ s->insn_config = ni6527_intr_insn_config;
+
+ writeb(0x00, devpriv->mite->daq_io_addr + Filter_Enable(0));
+ writeb(0x00, devpriv->mite->daq_io_addr + Filter_Enable(1));
+ writeb(0x00, devpriv->mite->daq_io_addr + Filter_Enable(2));
+
+ writeb(ClrEdge | ClrOverflow | ClrFilter | ClrInterval,
+ devpriv->mite->daq_io_addr + Clear_Register);
+ writeb(0x00, devpriv->mite->daq_io_addr + Master_Interrupt_Control);
+
+ ret = comedi_request_irq(mite_irq(devpriv->mite), ni6527_interrupt,
+ IRQF_SHARED, "ni6527", dev);
+ if (ret < 0) {
+ printk(" irq not available");
+ } else
+ dev->irq = mite_irq(devpriv->mite);
+
+ printk("\n");
+
+ return 0;
+}
+
+static int ni6527_detach(struct comedi_device * dev)
+{
+ if (devpriv && devpriv->mite && devpriv->mite->daq_io_addr) {
+ writeb(0x00,
+ devpriv->mite->daq_io_addr + Master_Interrupt_Control);
+ }
+
+ if (dev->irq) {
+ comedi_free_irq(dev->irq, dev);
+ }
+
+ if (devpriv && devpriv->mite) {
+ mite_unsetup(devpriv->mite);
+ }
+
+ return 0;
+}
+
+static int ni6527_find_device(struct comedi_device * dev, int bus, int slot)
+{
+ struct mite_struct *mite;
+ int i;
+
+ for (mite = mite_devices; mite; mite = mite->next) {
+ if (mite->used)
+ continue;
+ if (bus || slot) {
+ if (bus != mite->pcidev->bus->number ||
+ slot != PCI_SLOT(mite->pcidev->devfn))
+ continue;
+ }
+ for (i = 0; i < n_ni6527_boards; i++) {
+ if (mite_device_id(mite) == ni6527_boards[i].dev_id) {
+ dev->board_ptr = ni6527_boards + i;
+ devpriv->mite = mite;
+ return 0;
+ }
+ }
+ }
+ printk("no device found\n");
+ mite_list_devices();
+ return -EIO;
+}
+
+COMEDI_PCI_INITCLEANUP(driver_ni6527, ni6527_pci_table);
diff --git a/drivers/staging/comedi/drivers/ni_65xx.c b/drivers/staging/comedi/drivers/ni_65xx.c
new file mode 100644
index 000000000000..6e85da16a241
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_65xx.c
@@ -0,0 +1,809 @@
+/*
+ comedi/drivers/ni_6514.c
+ driver for National Instruments PCI-6514
+
+ Copyright (C) 2006 Jon Grierson <jd@renko.co.uk>
+ Copyright (C) 2006 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999,2002,2003 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: ni_65xx
+Description: National Instruments 65xx static dio boards
+Author: Jon Grierson <jd@renko.co.uk>, Frank Mori Hess <fmhess@users.sourceforge.net>
+Status: testing
+Devices: [National Instruments] PCI-6509 (ni_65xx), PXI-6509, PCI-6510, PCI-6511,
+ PXI-6511, PCI-6512, PXI-6512, PCI-6513, PXI-6513, PCI-6514, PXI-6514, PCI-6515,
+ PXI-6515, PCI-6516, PCI-6517, PCI-6518, PCI-6519, PCI-6520, PCI-6521, PXI-6521,
+ PCI-6528, PXI-6528
+Updated: Wed Oct 18 08:59:11 EDT 2006
+
+Based on the PCI-6527 driver by ds.
+The interrupt subdevice (subdevice 3) is probably broken for all boards
+except maybe the 6514.
+
+*/
+
+/*
+ Manuals (available from ftp://ftp.natinst.com/support/manuals)
+
+ 370106b.pdf 6514 Register Level Programmer Manual
+
+ */
+
+#define _GNU_SOURCE
+#define DEBUG 1
+#define DEBUG_FLAGS
+#include "../comedidev.h"
+
+#include "mite.h"
+
+#define NI6514_DIO_SIZE 4096
+#define NI6514_MITE_SIZE 4096
+
+#define NI_65XX_MAX_NUM_PORTS 12
+static const unsigned ni_65xx_channels_per_port = 8;
+static const unsigned ni_65xx_port_offset = 0x10;
+
+static inline unsigned Port_Data(unsigned port)
+{
+ return 0x40 + port * ni_65xx_port_offset;
+}
+static inline unsigned Port_Select(unsigned port)
+{
+ return 0x41 + port * ni_65xx_port_offset;
+}
+static inline unsigned Rising_Edge_Detection_Enable(unsigned port)
+{
+ return 0x42 + port * ni_65xx_port_offset;
+}
+static inline unsigned Falling_Edge_Detection_Enable(unsigned port)
+{
+ return 0x43 + port * ni_65xx_port_offset;
+}
+static inline unsigned Filter_Enable(unsigned port)
+{
+ return 0x44 + port * ni_65xx_port_offset;
+}
+
+#define ID_Register 0x00
+
+#define Clear_Register 0x01
+#define ClrEdge 0x08
+#define ClrOverflow 0x04
+
+#define Filter_Interval 0x08
+
+#define Change_Status 0x02
+#define MasterInterruptStatus 0x04
+#define Overflow 0x02
+#define EdgeStatus 0x01
+
+#define Master_Interrupt_Control 0x03
+#define FallingEdgeIntEnable 0x10
+#define RisingEdgeIntEnable 0x08
+#define MasterInterruptEnable 0x04
+#define OverflowIntEnable 0x02
+#define EdgeIntEnable 0x01
+
+static int ni_65xx_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int ni_65xx_detach(struct comedi_device * dev);
+static struct comedi_driver driver_ni_65xx = {
+ driver_name:"ni_65xx",
+ module:THIS_MODULE,
+ attach:ni_65xx_attach,
+ detach:ni_65xx_detach,
+};
+
+struct ni_65xx_board {
+
+ int dev_id;
+ const char *name;
+ unsigned num_dio_ports;
+ unsigned num_di_ports;
+ unsigned num_do_ports;
+ unsigned invert_outputs:1;
+};
+
+static const struct ni_65xx_board ni_65xx_boards[] = {
+ {
+ dev_id: 0x7085,
+ name: "pci-6509",
+ num_dio_ports:12,
+ invert_outputs:0},
+ {
+ dev_id: 0x1710,
+ name: "pxi-6509",
+ num_dio_ports:12,
+ invert_outputs:0},
+ {
+ dev_id: 0x7124,
+ name: "pci-6510",
+ num_di_ports:4},
+ {
+ dev_id: 0x70c3,
+ name: "pci-6511",
+ num_di_ports:8},
+ {
+ dev_id: 0x70d3,
+ name: "pxi-6511",
+ num_di_ports:8},
+ {
+ dev_id: 0x70cc,
+ name: "pci-6512",
+ num_do_ports:8},
+ {
+ dev_id: 0x70d2,
+ name: "pxi-6512",
+ num_do_ports:8},
+ {
+ dev_id: 0x70c8,
+ name: "pci-6513",
+ num_do_ports:8,
+ invert_outputs:1},
+ {
+ dev_id: 0x70d1,
+ name: "pxi-6513",
+ num_do_ports:8,
+ invert_outputs:1},
+ {
+ dev_id: 0x7088,
+ name: "pci-6514",
+ num_di_ports:4,
+ num_do_ports:4,
+ invert_outputs:1},
+ {
+ dev_id: 0x70CD,
+ name: "pxi-6514",
+ num_di_ports:4,
+ num_do_ports:4,
+ invert_outputs:1},
+ {
+ dev_id: 0x7087,
+ name: "pci-6515",
+ num_di_ports:4,
+ num_do_ports:4,
+ invert_outputs:1},
+ {
+ dev_id: 0x70c9,
+ name: "pxi-6515",
+ num_di_ports:4,
+ num_do_ports:4,
+ invert_outputs:1},
+ {
+ dev_id: 0x7125,
+ name: "pci-6516",
+ num_do_ports:4,
+ invert_outputs:1},
+ {
+ dev_id: 0x7126,
+ name: "pci-6517",
+ num_do_ports:4,
+ invert_outputs:1},
+ {
+ dev_id: 0x7127,
+ name: "pci-6518",
+ num_di_ports:2,
+ num_do_ports:2,
+ invert_outputs:1},
+ {
+ dev_id: 0x7128,
+ name: "pci-6519",
+ num_di_ports:2,
+ num_do_ports:2,
+ invert_outputs:1},
+ {
+ dev_id: 0x71c5,
+ name: "pci-6520",
+ num_di_ports:1,
+ num_do_ports:1,
+ },
+ {
+ dev_id: 0x718b,
+ name: "pci-6521",
+ num_di_ports:1,
+ num_do_ports:1,
+ },
+ {
+ dev_id: 0x718c,
+ name: "pxi-6521",
+ num_di_ports:1,
+ num_do_ports:1,
+ },
+ {
+ dev_id: 0x70a9,
+ name: "pci-6528",
+ num_di_ports:3,
+ num_do_ports:3,
+ },
+ {
+ dev_id: 0x7086,
+ name: "pxi-6528",
+ num_di_ports:3,
+ num_do_ports:3,
+ },
+};
+
+#define n_ni_65xx_boards (sizeof(ni_65xx_boards)/sizeof(ni_65xx_boards[0]))
+static inline const struct ni_65xx_board *board(struct comedi_device * dev)
+{
+ return dev->board_ptr;
+}
+static inline unsigned ni_65xx_port_by_channel(unsigned channel)
+{
+ return channel / ni_65xx_channels_per_port;
+}
+static inline unsigned ni_65xx_total_num_ports(const struct ni_65xx_board * board)
+{
+ return board->num_dio_ports + board->num_di_ports + board->num_do_ports;
+}
+
+static DEFINE_PCI_DEVICE_TABLE(ni_65xx_pci_table) = {
+ {PCI_VENDOR_ID_NATINST, 0x1710, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x7085, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x7086, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x7087, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x7088, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70a9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70c3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70c8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70c9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70cc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70CD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70d1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70d2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70d3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x7124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x7125, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x7126, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x7127, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x7128, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x718b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x718c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x71c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, ni_65xx_pci_table);
+
+struct ni_65xx_private {
+ struct mite_struct *mite;
+ unsigned int filter_interval;
+ unsigned short filter_enable[NI_65XX_MAX_NUM_PORTS];
+ unsigned short output_bits[NI_65XX_MAX_NUM_PORTS];
+ unsigned short dio_direction[NI_65XX_MAX_NUM_PORTS];
+};
+
+static inline struct ni_65xx_private *private(struct comedi_device * dev)
+{
+ return dev->private;
+}
+
+struct ni_65xx_subdevice_private {
+ unsigned base_port;
+};
+
+static inline struct ni_65xx_subdevice_private *sprivate(struct comedi_subdevice * subdev)
+{
+ return subdev->private;
+}
+static struct ni_65xx_subdevice_private *ni_65xx_alloc_subdevice_private(void)
+{
+ struct ni_65xx_subdevice_private *subdev_private =
+ kzalloc(sizeof(struct ni_65xx_subdevice_private), GFP_KERNEL);
+ if (subdev_private == NULL)
+ return NULL;
+ return subdev_private;
+}
+
+static int ni_65xx_find_device(struct comedi_device * dev, int bus, int slot);
+
+static int ni_65xx_config_filter(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ const unsigned chan = CR_CHAN(insn->chanspec);
+ const unsigned port =
+ sprivate(s)->base_port + ni_65xx_port_by_channel(chan);
+
+ if (data[0] != INSN_CONFIG_FILTER)
+ return -EINVAL;
+ if (data[1]) {
+ static const unsigned filter_resolution_ns = 200;
+ static const unsigned max_filter_interval = 0xfffff;
+ unsigned interval =
+ (data[1] +
+ (filter_resolution_ns / 2)) / filter_resolution_ns;
+ if (interval > max_filter_interval)
+ interval = max_filter_interval;
+ data[1] = interval * filter_resolution_ns;
+
+ if (interval != private(dev)->filter_interval) {
+ writeb(interval,
+ private(dev)->mite->daq_io_addr +
+ Filter_Interval);
+ private(dev)->filter_interval = interval;
+ }
+
+ private(dev)->filter_enable[port] |=
+ 1 << (chan % ni_65xx_channels_per_port);
+ } else {
+ private(dev)->filter_enable[port] &=
+ ~(1 << (chan % ni_65xx_channels_per_port));
+ }
+
+ writeb(private(dev)->filter_enable[port],
+ private(dev)->mite->daq_io_addr + Filter_Enable(port));
+
+ return 2;
+}
+
+static int ni_65xx_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned port;
+
+ if (insn->n < 1)
+ return -EINVAL;
+ port = sprivate(s)->base_port +
+ ni_65xx_port_by_channel(CR_CHAN(insn->chanspec));
+ switch (data[0]) {
+ case INSN_CONFIG_FILTER:
+ return ni_65xx_config_filter(dev, s, insn, data);
+ break;
+ case INSN_CONFIG_DIO_OUTPUT:
+ if (s->type != COMEDI_SUBD_DIO)
+ return -EINVAL;
+ private(dev)->dio_direction[port] = COMEDI_OUTPUT;
+ writeb(0, private(dev)->mite->daq_io_addr + Port_Select(port));
+ return 1;
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ if (s->type != COMEDI_SUBD_DIO)
+ return -EINVAL;
+ private(dev)->dio_direction[port] = COMEDI_INPUT;
+ writeb(1, private(dev)->mite->daq_io_addr + Port_Select(port));
+ return 1;
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ if (s->type != COMEDI_SUBD_DIO)
+ return -EINVAL;
+ data[1] = private(dev)->dio_direction[port];
+ return insn->n;
+ break;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int ni_65xx_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned base_bitfield_channel;
+ const unsigned max_ports_per_bitfield = 5;
+ unsigned read_bits = 0;
+ unsigned j;
+ if (insn->n != 2)
+ return -EINVAL;
+ base_bitfield_channel = CR_CHAN(insn->chanspec);
+ for (j = 0; j < max_ports_per_bitfield; ++j) {
+ const unsigned port =
+ sprivate(s)->base_port +
+ ni_65xx_port_by_channel(base_bitfield_channel) + j;
+ unsigned base_port_channel;
+ unsigned port_mask, port_data, port_read_bits;
+ int bitshift;
+ if (port >= ni_65xx_total_num_ports(board(dev)))
+ break;
+ base_port_channel = port * ni_65xx_channels_per_port;
+ port_mask = data[0];
+ port_data = data[1];
+ bitshift = base_port_channel - base_bitfield_channel;
+ if (bitshift >= 32 || bitshift <= -32)
+ break;
+ if (bitshift > 0) {
+ port_mask >>= bitshift;
+ port_data >>= bitshift;
+ } else {
+ port_mask <<= -bitshift;
+ port_data <<= -bitshift;
+ }
+ port_mask &= 0xff;
+ port_data &= 0xff;
+ if (port_mask) {
+ unsigned bits;
+ private(dev)->output_bits[port] &= ~port_mask;
+ private(dev)->output_bits[port] |=
+ port_data & port_mask;
+ bits = private(dev)->output_bits[port];
+ if (board(dev)->invert_outputs)
+ bits = ~bits;
+ writeb(bits,
+ private(dev)->mite->daq_io_addr +
+ Port_Data(port));
+// rt_printk("wrote 0x%x to port %i\n", bits, port);
+ }
+ port_read_bits =
+ readb(private(dev)->mite->daq_io_addr +
+ Port_Data(port));
+// rt_printk("read 0x%x from port %i\n", port_read_bits, port);
+ if (bitshift > 0) {
+ port_read_bits <<= bitshift;
+ } else {
+ port_read_bits >>= -bitshift;
+ }
+ read_bits |= port_read_bits;
+ }
+ data[1] = read_bits;
+ return insn->n;
+}
+
+static irqreturn_t ni_65xx_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 2;
+ unsigned int status;
+
+ status = readb(private(dev)->mite->daq_io_addr + Change_Status);
+ if ((status & MasterInterruptStatus) == 0)
+ return IRQ_NONE;
+ if ((status & EdgeStatus) == 0)
+ return IRQ_NONE;
+
+ writeb(ClrEdge | ClrOverflow,
+ private(dev)->mite->daq_io_addr + Clear_Register);
+
+ comedi_buf_put(s->async, 0);
+ s->async->events |= COMEDI_CB_EOS;
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+}
+
+static int ni_65xx_intr_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_OTHER;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_FOLLOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_end_arg != 1) {
+ cmd->scan_end_arg = 1;
+ err++;
+ }
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int ni_65xx_intr_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ //struct comedi_cmd *cmd = &s->async->cmd;
+
+ writeb(ClrEdge | ClrOverflow,
+ private(dev)->mite->daq_io_addr + Clear_Register);
+ writeb(FallingEdgeIntEnable | RisingEdgeIntEnable |
+ MasterInterruptEnable | EdgeIntEnable,
+ private(dev)->mite->daq_io_addr + Master_Interrupt_Control);
+
+ return 0;
+}
+
+static int ni_65xx_intr_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ writeb(0x00,
+ private(dev)->mite->daq_io_addr + Master_Interrupt_Control);
+
+ return 0;
+}
+
+static int ni_65xx_intr_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n < 1)
+ return -EINVAL;
+
+ data[1] = 0;
+ return 2;
+}
+
+static int ni_65xx_intr_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n < 1)
+ return -EINVAL;
+ if (data[0] != INSN_CONFIG_CHANGE_NOTIFY)
+ return -EINVAL;
+
+ writeb(data[1],
+ private(dev)->mite->daq_io_addr +
+ Rising_Edge_Detection_Enable(0));
+ writeb(data[1] >> 8,
+ private(dev)->mite->daq_io_addr +
+ Rising_Edge_Detection_Enable(0x10));
+ writeb(data[1] >> 16,
+ private(dev)->mite->daq_io_addr +
+ Rising_Edge_Detection_Enable(0x20));
+ writeb(data[1] >> 24,
+ private(dev)->mite->daq_io_addr +
+ Rising_Edge_Detection_Enable(0x30));
+
+ writeb(data[2],
+ private(dev)->mite->daq_io_addr +
+ Falling_Edge_Detection_Enable(0));
+ writeb(data[2] >> 8,
+ private(dev)->mite->daq_io_addr +
+ Falling_Edge_Detection_Enable(0x10));
+ writeb(data[2] >> 16,
+ private(dev)->mite->daq_io_addr +
+ Falling_Edge_Detection_Enable(0x20));
+ writeb(data[2] >> 24,
+ private(dev)->mite->daq_io_addr +
+ Falling_Edge_Detection_Enable(0x30));
+
+ return 2;
+}
+
+static int ni_65xx_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned i;
+ int ret;
+
+ printk("comedi%d: ni_65xx:", dev->minor);
+
+ if ((ret = alloc_private(dev, sizeof(struct ni_65xx_private))) < 0)
+ return ret;
+
+ ret = ni_65xx_find_device(dev, it->options[0], it->options[1]);
+ if (ret < 0)
+ return ret;
+
+ ret = mite_setup(private(dev)->mite);
+ if (ret < 0) {
+ printk("error setting up mite\n");
+ return ret;
+ }
+
+ dev->board_name = board(dev)->name;
+ dev->irq = mite_irq(private(dev)->mite);
+ printk(" %s", dev->board_name);
+
+ printk(" ID=0x%02x",
+ readb(private(dev)->mite->daq_io_addr + ID_Register));
+
+ if ((ret = alloc_subdevices(dev, 4)) < 0)
+ return ret;
+
+ s = dev->subdevices + 0;
+ if (board(dev)->num_di_ports) {
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan =
+ board(dev)->num_di_ports * ni_65xx_channels_per_port;
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+ s->insn_config = ni_65xx_dio_insn_config;
+ s->insn_bits = ni_65xx_dio_insn_bits;
+ s->private = ni_65xx_alloc_subdevice_private();
+ if (s->private == NULL)
+ return -ENOMEM;
+ sprivate(s)->base_port = 0;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = dev->subdevices + 1;
+ if (board(dev)->num_do_ports) {
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan =
+ board(dev)->num_do_ports * ni_65xx_channels_per_port;
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+ s->insn_bits = ni_65xx_dio_insn_bits;
+ s->private = ni_65xx_alloc_subdevice_private();
+ if (s->private == NULL)
+ return -ENOMEM;
+ sprivate(s)->base_port = board(dev)->num_di_ports;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = dev->subdevices + 2;
+ if (board(dev)->num_dio_ports) {
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan =
+ board(dev)->num_dio_ports * ni_65xx_channels_per_port;
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+ s->insn_config = ni_65xx_dio_insn_config;
+ s->insn_bits = ni_65xx_dio_insn_bits;
+ s->private = ni_65xx_alloc_subdevice_private();
+ if (s->private == NULL)
+ return -ENOMEM;
+ sprivate(s)->base_port = 0;
+ for (i = 0; i < board(dev)->num_dio_ports; ++i) {
+ // configure all ports for input
+ writeb(0x1,
+ private(dev)->mite->daq_io_addr +
+ Port_Select(i));
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = dev->subdevices + 3;
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+ s->n_chan = 1;
+ s->range_table = &range_unknown;
+ s->maxdata = 1;
+ s->do_cmdtest = ni_65xx_intr_cmdtest;
+ s->do_cmd = ni_65xx_intr_cmd;
+ s->cancel = ni_65xx_intr_cancel;
+ s->insn_bits = ni_65xx_intr_insn_bits;
+ s->insn_config = ni_65xx_intr_insn_config;
+
+ for (i = 0; i < ni_65xx_total_num_ports(board(dev)); ++i) {
+ writeb(0x00,
+ private(dev)->mite->daq_io_addr + Filter_Enable(i));
+ if (board(dev)->invert_outputs)
+ writeb(0x01,
+ private(dev)->mite->daq_io_addr + Port_Data(i));
+ else
+ writeb(0x00,
+ private(dev)->mite->daq_io_addr + Port_Data(i));
+ }
+ writeb(ClrEdge | ClrOverflow,
+ private(dev)->mite->daq_io_addr + Clear_Register);
+ writeb(0x00,
+ private(dev)->mite->daq_io_addr + Master_Interrupt_Control);
+
+ /* Set filter interval to 0 (32bit reg) */
+ writeb(0x00000000, private(dev)->mite->daq_io_addr + Filter_Interval);
+
+ ret = comedi_request_irq(dev->irq, ni_65xx_interrupt, IRQF_SHARED,
+ "ni_65xx", dev);
+ if (ret < 0) {
+ dev->irq = 0;
+ printk(" irq not available");
+ }
+
+ printk("\n");
+
+ return 0;
+}
+
+static int ni_65xx_detach(struct comedi_device * dev)
+{
+ if (private(dev) && private(dev)->mite
+ && private(dev)->mite->daq_io_addr) {
+ writeb(0x00,
+ private(dev)->mite->daq_io_addr +
+ Master_Interrupt_Control);
+ }
+
+ if (dev->irq) {
+ comedi_free_irq(dev->irq, dev);
+ }
+
+ if (private(dev)) {
+ unsigned i;
+ for (i = 0; i < dev->n_subdevices; ++i) {
+ if (dev->subdevices[i].private) {
+ kfree(dev->subdevices[i].private);
+ dev->subdevices[i].private = NULL;
+ }
+ }
+ if (private(dev)->mite) {
+ mite_unsetup(private(dev)->mite);
+ }
+ }
+ return 0;
+}
+
+static int ni_65xx_find_device(struct comedi_device * dev, int bus, int slot)
+{
+ struct mite_struct *mite;
+ int i;
+
+ for (mite = mite_devices; mite; mite = mite->next) {
+ if (mite->used)
+ continue;
+ if (bus || slot) {
+ if (bus != mite->pcidev->bus->number ||
+ slot != PCI_SLOT(mite->pcidev->devfn))
+ continue;
+ }
+ for (i = 0; i < n_ni_65xx_boards; i++) {
+ if (mite_device_id(mite) == ni_65xx_boards[i].dev_id) {
+ dev->board_ptr = ni_65xx_boards + i;
+ private(dev)->mite = mite;
+ return 0;
+ }
+ }
+ }
+ printk("no device found\n");
+ mite_list_devices();
+ return -EIO;
+}
+
+COMEDI_PCI_INITCLEANUP(driver_ni_65xx, ni_65xx_pci_table);
diff --git a/drivers/staging/comedi/drivers/ni_660x.c b/drivers/staging/comedi/drivers/ni_660x.c
new file mode 100644
index 000000000000..14e35ba721e9
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_660x.c
@@ -0,0 +1,1324 @@
+/*
+ comedi/drivers/ni_660x.c
+ Hardware driver for NI 660x devices
+
+ 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.
+*/
+
+/*
+Driver: ni_660x
+Description: National Instruments 660x counter/timer boards
+Devices:
+[National Instruments] PCI-6601 (ni_660x), PCI-6602, PXI-6602,
+ PXI-6608
+Author: J.P. Mellor <jpmellor@rose-hulman.edu>,
+ Herman.Bruyninckx@mech.kuleuven.ac.be,
+ Wim.Meeussen@mech.kuleuven.ac.be,
+ Klaas.Gadeyne@mech.kuleuven.ac.be,
+ Frank Mori Hess <fmhess@users.sourceforge.net>
+Updated: Thu Oct 18 12:56:06 EDT 2007
+Status: experimental
+
+Encoders work. PulseGeneration (both single pulse and pulse train)
+works. Buffered commands work for input but not output.
+
+References:
+DAQ 660x Register-Level Programmer Manual (NI 370505A-01)
+DAQ 6601/6602 User Manual (NI 322137B-01)
+
+*/
+
+#include "../comedidev.h"
+#include "mite.h"
+#include "ni_tio.h"
+
+enum ni_660x_constants {
+ min_counter_pfi_chan = 8,
+ max_dio_pfi_chan = 31,
+ counters_per_chip = 4
+};
+
+#define NUM_PFI_CHANNELS 40
+// really there are only up to 3 dma channels, but the register layout allows for 4
+#define MAX_DMA_CHANNEL 4
+
+/* See Register-Level Programmer Manual page 3.1 */
+enum NI_660x_Register {
+ G0InterruptAcknowledge,
+ G0StatusRegister,
+ G1InterruptAcknowledge,
+ G1StatusRegister,
+ G01StatusRegister,
+ G0CommandRegister,
+ STCDIOParallelInput,
+ G1CommandRegister,
+ G0HWSaveRegister,
+ G1HWSaveRegister,
+ STCDIOOutput,
+ STCDIOControl,
+ G0SWSaveRegister,
+ G1SWSaveRegister,
+ G0ModeRegister,
+ G01JointStatus1Register,
+ G1ModeRegister,
+ STCDIOSerialInput,
+ G0LoadARegister,
+ G01JointStatus2Register,
+ G0LoadBRegister,
+ G1LoadARegister,
+ G1LoadBRegister,
+ G0InputSelectRegister,
+ G1InputSelectRegister,
+ G0AutoincrementRegister,
+ G1AutoincrementRegister,
+ G01JointResetRegister,
+ G0InterruptEnable,
+ G1InterruptEnable,
+ G0CountingModeRegister,
+ G1CountingModeRegister,
+ G0SecondGateRegister,
+ G1SecondGateRegister,
+ G0DMAConfigRegister,
+ G0DMAStatusRegister,
+ G1DMAConfigRegister,
+ G1DMAStatusRegister,
+ G2InterruptAcknowledge,
+ G2StatusRegister,
+ G3InterruptAcknowledge,
+ G3StatusRegister,
+ G23StatusRegister,
+ G2CommandRegister,
+ G3CommandRegister,
+ G2HWSaveRegister,
+ G3HWSaveRegister,
+ G2SWSaveRegister,
+ G3SWSaveRegister,
+ G2ModeRegister,
+ G23JointStatus1Register,
+ G3ModeRegister,
+ G2LoadARegister,
+ G23JointStatus2Register,
+ G2LoadBRegister,
+ G3LoadARegister,
+ G3LoadBRegister,
+ G2InputSelectRegister,
+ G3InputSelectRegister,
+ G2AutoincrementRegister,
+ G3AutoincrementRegister,
+ G23JointResetRegister,
+ G2InterruptEnable,
+ G3InterruptEnable,
+ G2CountingModeRegister,
+ G3CountingModeRegister,
+ G3SecondGateRegister,
+ G2SecondGateRegister,
+ G2DMAConfigRegister,
+ G2DMAStatusRegister,
+ G3DMAConfigRegister,
+ G3DMAStatusRegister,
+ DIO32Input,
+ DIO32Output,
+ ClockConfigRegister,
+ GlobalInterruptStatusRegister,
+ DMAConfigRegister,
+ GlobalInterruptConfigRegister,
+ IOConfigReg0_1,
+ IOConfigReg2_3,
+ IOConfigReg4_5,
+ IOConfigReg6_7,
+ IOConfigReg8_9,
+ IOConfigReg10_11,
+ IOConfigReg12_13,
+ IOConfigReg14_15,
+ IOConfigReg16_17,
+ IOConfigReg18_19,
+ IOConfigReg20_21,
+ IOConfigReg22_23,
+ IOConfigReg24_25,
+ IOConfigReg26_27,
+ IOConfigReg28_29,
+ IOConfigReg30_31,
+ IOConfigReg32_33,
+ IOConfigReg34_35,
+ IOConfigReg36_37,
+ IOConfigReg38_39,
+ NumRegisters,
+};
+
+static inline unsigned IOConfigReg(unsigned pfi_channel)
+{
+ unsigned reg = IOConfigReg0_1 + pfi_channel / 2;
+ BUG_ON(reg > IOConfigReg38_39);
+ return reg;
+}
+
+enum ni_660x_register_width {
+ DATA_1B,
+ DATA_2B,
+ DATA_4B
+};
+
+enum ni_660x_register_direction {
+ NI_660x_READ,
+ NI_660x_WRITE,
+ NI_660x_READ_WRITE
+};
+
+enum ni_660x_pfi_output_select {
+ pfi_output_select_high_Z = 0,
+ pfi_output_select_counter = 1,
+ pfi_output_select_do = 2,
+ num_pfi_output_selects
+};
+
+enum ni_660x_subdevices {
+ NI_660X_DIO_SUBDEV = 1,
+ NI_660X_GPCT_SUBDEV_0 = 2
+};
+static inline unsigned NI_660X_GPCT_SUBDEV(unsigned index)
+{
+ return NI_660X_GPCT_SUBDEV_0 + index;
+}
+
+struct NI_660xRegisterData {
+
+ const char *name; // Register Name
+ int offset; // Offset from base address from GPCT chip
+ enum ni_660x_register_direction direction;
+ enum ni_660x_register_width size; // 1 byte, 2 bytes, or 4 bytes
+};
+
+
+static const struct NI_660xRegisterData registerData[NumRegisters] = {
+ {"G0 Interrupt Acknowledge", 0x004, NI_660x_WRITE, DATA_2B},
+ {"G0 Status Register", 0x004, NI_660x_READ, DATA_2B},
+ {"G1 Interrupt Acknowledge", 0x006, NI_660x_WRITE, DATA_2B},
+ {"G1 Status Register", 0x006, NI_660x_READ, DATA_2B},
+ {"G01 Status Register ", 0x008, NI_660x_READ, DATA_2B},
+ {"G0 Command Register", 0x00C, NI_660x_WRITE, DATA_2B},
+ {"STC DIO Parallel Input", 0x00E, NI_660x_READ, DATA_2B},
+ {"G1 Command Register", 0x00E, NI_660x_WRITE, DATA_2B},
+ {"G0 HW Save Register", 0x010, NI_660x_READ, DATA_4B},
+ {"G1 HW Save Register", 0x014, NI_660x_READ, DATA_4B},
+ {"STC DIO Output", 0x014, NI_660x_WRITE, DATA_2B},
+ {"STC DIO Control", 0x016, NI_660x_WRITE, DATA_2B},
+ {"G0 SW Save Register", 0x018, NI_660x_READ, DATA_4B},
+ {"G1 SW Save Register", 0x01C, NI_660x_READ, DATA_4B},
+ {"G0 Mode Register", 0x034, NI_660x_WRITE, DATA_2B},
+ {"G01 Joint Status 1 Register", 0x036, NI_660x_READ, DATA_2B},
+ {"G1 Mode Register", 0x036, NI_660x_WRITE, DATA_2B},
+ {"STC DIO Serial Input", 0x038, NI_660x_READ, DATA_2B},
+ {"G0 Load A Register", 0x038, NI_660x_WRITE, DATA_4B},
+ {"G01 Joint Status 2 Register", 0x03A, NI_660x_READ, DATA_2B},
+ {"G0 Load B Register", 0x03C, NI_660x_WRITE, DATA_4B},
+ {"G1 Load A Register", 0x040, NI_660x_WRITE, DATA_4B},
+ {"G1 Load B Register", 0x044, NI_660x_WRITE, DATA_4B},
+ {"G0 Input Select Register", 0x048, NI_660x_WRITE, DATA_2B},
+ {"G1 Input Select Register", 0x04A, NI_660x_WRITE, DATA_2B},
+ {"G0 Autoincrement Register", 0x088, NI_660x_WRITE, DATA_2B},
+ {"G1 Autoincrement Register", 0x08A, NI_660x_WRITE, DATA_2B},
+ {"G01 Joint Reset Register", 0x090, NI_660x_WRITE, DATA_2B},
+ {"G0 Interrupt Enable", 0x092, NI_660x_WRITE, DATA_2B},
+ {"G1 Interrupt Enable", 0x096, NI_660x_WRITE, DATA_2B},
+ {"G0 Counting Mode Register", 0x0B0, NI_660x_WRITE, DATA_2B},
+ {"G1 Counting Mode Register", 0x0B2, NI_660x_WRITE, DATA_2B},
+ {"G0 Second Gate Register", 0x0B4, NI_660x_WRITE, DATA_2B},
+ {"G1 Second Gate Register", 0x0B6, NI_660x_WRITE, DATA_2B},
+ {"G0 DMA Config Register", 0x0B8, NI_660x_WRITE, DATA_2B},
+ {"G0 DMA Status Register", 0x0B8, NI_660x_READ, DATA_2B},
+ {"G1 DMA Config Register", 0x0BA, NI_660x_WRITE, DATA_2B},
+ {"G1 DMA Status Register", 0x0BA, NI_660x_READ, DATA_2B},
+ {"G2 Interrupt Acknowledge", 0x104, NI_660x_WRITE, DATA_2B},
+ {"G2 Status Register", 0x104, NI_660x_READ, DATA_2B},
+ {"G3 Interrupt Acknowledge", 0x106, NI_660x_WRITE, DATA_2B},
+ {"G3 Status Register", 0x106, NI_660x_READ, DATA_2B},
+ {"G23 Status Register", 0x108, NI_660x_READ, DATA_2B},
+ {"G2 Command Register", 0x10C, NI_660x_WRITE, DATA_2B},
+ {"G3 Command Register", 0x10E, NI_660x_WRITE, DATA_2B},
+ {"G2 HW Save Register", 0x110, NI_660x_READ, DATA_4B},
+ {"G3 HW Save Register", 0x114, NI_660x_READ, DATA_4B},
+ {"G2 SW Save Register", 0x118, NI_660x_READ, DATA_4B},
+ {"G3 SW Save Register", 0x11C, NI_660x_READ, DATA_4B},
+ {"G2 Mode Register", 0x134, NI_660x_WRITE, DATA_2B},
+ {"G23 Joint Status 1 Register", 0x136, NI_660x_READ, DATA_2B},
+ {"G3 Mode Register", 0x136, NI_660x_WRITE, DATA_2B},
+ {"G2 Load A Register", 0x138, NI_660x_WRITE, DATA_4B},
+ {"G23 Joint Status 2 Register", 0x13A, NI_660x_READ, DATA_2B},
+ {"G2 Load B Register", 0x13C, NI_660x_WRITE, DATA_4B},
+ {"G3 Load A Register", 0x140, NI_660x_WRITE, DATA_4B},
+ {"G3 Load B Register", 0x144, NI_660x_WRITE, DATA_4B},
+ {"G2 Input Select Register", 0x148, NI_660x_WRITE, DATA_2B},
+ {"G3 Input Select Register", 0x14A, NI_660x_WRITE, DATA_2B},
+ {"G2 Autoincrement Register", 0x188, NI_660x_WRITE, DATA_2B},
+ {"G3 Autoincrement Register", 0x18A, NI_660x_WRITE, DATA_2B},
+ {"G23 Joint Reset Register", 0x190, NI_660x_WRITE, DATA_2B},
+ {"G2 Interrupt Enable", 0x192, NI_660x_WRITE, DATA_2B},
+ {"G3 Interrupt Enable", 0x196, NI_660x_WRITE, DATA_2B},
+ {"G2 Counting Mode Register", 0x1B0, NI_660x_WRITE, DATA_2B},
+ {"G3 Counting Mode Register", 0x1B2, NI_660x_WRITE, DATA_2B},
+ {"G3 Second Gate Register", 0x1B6, NI_660x_WRITE, DATA_2B},
+ {"G2 Second Gate Register", 0x1B4, NI_660x_WRITE, DATA_2B},
+ {"G2 DMA Config Register", 0x1B8, NI_660x_WRITE, DATA_2B},
+ {"G2 DMA Status Register", 0x1B8, NI_660x_READ, DATA_2B},
+ {"G3 DMA Config Register", 0x1BA, NI_660x_WRITE, DATA_2B},
+ {"G3 DMA Status Register", 0x1BA, NI_660x_READ, DATA_2B},
+ {"32 bit Digital Input", 0x414, NI_660x_READ, DATA_4B},
+ {"32 bit Digital Output", 0x510, NI_660x_WRITE, DATA_4B},
+ {"Clock Config Register", 0x73C, NI_660x_WRITE, DATA_4B},
+ {"Global Interrupt Status Register", 0x754, NI_660x_READ, DATA_4B},
+ {"DMA Configuration Register", 0x76C, NI_660x_WRITE, DATA_4B},
+ {"Global Interrupt Config Register", 0x770, NI_660x_WRITE, DATA_4B},
+ {"IO Config Register 0-1", 0x77C, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 2-3", 0x77E, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 4-5", 0x780, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 6-7", 0x782, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 8-9", 0x784, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 10-11", 0x786, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 12-13", 0x788, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 14-15", 0x78A, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 16-17", 0x78C, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 18-19", 0x78E, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 20-21", 0x790, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 22-23", 0x792, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 24-25", 0x794, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 26-27", 0x796, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 28-29", 0x798, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 30-31", 0x79A, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 32-33", 0x79C, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 34-35", 0x79E, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 36-37", 0x7A0, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 38-39", 0x7A2, NI_660x_READ_WRITE, DATA_2B}
+};
+
+// kind of ENABLE for the second counter
+enum clock_config_register_bits {
+ CounterSwap = 0x1 << 21
+};
+
+// ioconfigreg
+static inline unsigned ioconfig_bitshift(unsigned pfi_channel)
+{
+ if (pfi_channel % 2)
+ return 0;
+ else
+ return 8;
+}
+static inline unsigned pfi_output_select_mask(unsigned pfi_channel)
+{
+ return 0x3 << ioconfig_bitshift(pfi_channel);
+}
+static inline unsigned pfi_output_select_bits(unsigned pfi_channel,
+ unsigned output_select)
+{
+ return (output_select & 0x3) << ioconfig_bitshift(pfi_channel);
+}
+static inline unsigned pfi_input_select_mask(unsigned pfi_channel)
+{
+ return 0x7 << (4 + ioconfig_bitshift(pfi_channel));
+}
+static inline unsigned pfi_input_select_bits(unsigned pfi_channel,
+ unsigned input_select)
+{
+ return (input_select & 0x7) << (4 + ioconfig_bitshift(pfi_channel));
+}
+
+// dma configuration register bits
+static inline unsigned dma_select_mask(unsigned dma_channel)
+{
+ BUG_ON(dma_channel >= MAX_DMA_CHANNEL);
+ return 0x1f << (8 * dma_channel);
+}
+enum dma_selection {
+ dma_selection_none = 0x1f,
+};
+static inline unsigned dma_selection_counter(unsigned counter_index)
+{
+ BUG_ON(counter_index >= counters_per_chip);
+ return counter_index;
+}
+static inline unsigned dma_select_bits(unsigned dma_channel, unsigned selection)
+{
+ BUG_ON(dma_channel >= MAX_DMA_CHANNEL);
+ return (selection << (8 * dma_channel)) & dma_select_mask(dma_channel);
+}
+static inline unsigned dma_reset_bit(unsigned dma_channel)
+{
+ BUG_ON(dma_channel >= MAX_DMA_CHANNEL);
+ return 0x80 << (8 * dma_channel);
+}
+
+enum global_interrupt_status_register_bits {
+ Counter_0_Int_Bit = 0x100,
+ Counter_1_Int_Bit = 0x200,
+ Counter_2_Int_Bit = 0x400,
+ Counter_3_Int_Bit = 0x800,
+ Cascade_Int_Bit = 0x20000000,
+ Global_Int_Bit = 0x80000000
+};
+
+enum global_interrupt_config_register_bits {
+ Cascade_Int_Enable_Bit = 0x20000000,
+ Global_Int_Polarity_Bit = 0x40000000,
+ Global_Int_Enable_Bit = 0x80000000
+};
+
+// Offset of the GPCT chips from the base-adress of the card
+static const unsigned GPCT_OFFSET[2] = { 0x0, 0x800 }; /* First chip is at base-address +
+ 0x00, etc. */
+
+/* Board description*/
+struct ni_660x_board {
+ unsigned short dev_id; /* `lspci` will show you this */
+ const char *name;
+ unsigned n_chips; /* total number of TIO chips */
+};
+
+static const struct ni_660x_board ni_660x_boards[] = {
+ {
+ dev_id: 0x2c60,
+ name: "PCI-6601",
+ n_chips: 1,
+ },
+ {
+ dev_id: 0x1310,
+ name: "PCI-6602",
+ n_chips: 2,
+ },
+ {
+ dev_id: 0x1360,
+ name: "PXI-6602",
+ n_chips: 2,
+ },
+ {
+ dev_id: 0x2cc0,
+ name: "PXI-6608",
+ n_chips: 2,
+ },
+};
+
+#define NI_660X_MAX_NUM_CHIPS 2
+#define NI_660X_MAX_NUM_COUNTERS (NI_660X_MAX_NUM_CHIPS * counters_per_chip)
+
+static DEFINE_PCI_DEVICE_TABLE(ni_660x_pci_table) = {
+ {PCI_VENDOR_ID_NATINST, 0x2c60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1310, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x2cc0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, ni_660x_pci_table);
+
+struct ni_660x_private {
+ struct mite_struct *mite;
+ struct ni_gpct_device *counter_dev;
+ uint64_t pfi_direction_bits;
+ struct mite_dma_descriptor_ring
+ *mite_rings[NI_660X_MAX_NUM_CHIPS][counters_per_chip];
+ spinlock_t mite_channel_lock;
+ unsigned dma_configuration_soft_copies[NI_660X_MAX_NUM_CHIPS];
+ spinlock_t soft_reg_copy_lock;
+ unsigned short pfi_output_selects[NUM_PFI_CHANNELS];
+};
+
+static inline struct ni_660x_private *private(struct comedi_device * dev)
+{
+ return dev->private;
+}
+
+/* initialized in ni_660x_find_device() */
+static inline const struct ni_660x_board *board(struct comedi_device * dev)
+{
+ return dev->board_ptr;
+}
+
+#define n_ni_660x_boards (sizeof(ni_660x_boards)/sizeof(ni_660x_boards[0]))
+
+static int ni_660x_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int ni_660x_detach(struct comedi_device * dev);
+static void init_tio_chip(struct comedi_device * dev, int chipset);
+static void ni_660x_select_pfi_output(struct comedi_device * dev, unsigned pfi_channel,
+ unsigned output_select);
+
+static struct comedi_driver driver_ni_660x = {
+ driver_name:"ni_660x",
+ module:THIS_MODULE,
+ attach:ni_660x_attach,
+ detach:ni_660x_detach,
+};
+
+COMEDI_PCI_INITCLEANUP(driver_ni_660x, ni_660x_pci_table);
+
+static int ni_660x_find_device(struct comedi_device * dev, int bus, int slot);
+static int ni_660x_set_pfi_routing(struct comedi_device * dev, unsigned chan,
+ unsigned source);
+
+/* Possible instructions for a GPCT */
+static int ni_660x_GPCT_rinsn(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data);
+static int ni_660x_GPCT_insn_config(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data);
+static int ni_660x_GPCT_winsn(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data);
+
+/* Possible instructions for Digital IO */
+static int ni_660x_dio_insn_config(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data);
+static int ni_660x_dio_insn_bits(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data);
+
+static inline unsigned ni_660x_num_counters(struct comedi_device * dev)
+{
+ return board(dev)->n_chips * counters_per_chip;
+}
+
+static enum NI_660x_Register ni_gpct_to_660x_register(enum ni_gpct_register reg)
+{
+ enum NI_660x_Register ni_660x_register;
+ switch (reg) {
+ case NITIO_G0_Autoincrement_Reg:
+ ni_660x_register = G0AutoincrementRegister;
+ break;
+ case NITIO_G1_Autoincrement_Reg:
+ ni_660x_register = G1AutoincrementRegister;
+ break;
+ case NITIO_G2_Autoincrement_Reg:
+ ni_660x_register = G2AutoincrementRegister;
+ break;
+ case NITIO_G3_Autoincrement_Reg:
+ ni_660x_register = G3AutoincrementRegister;
+ break;
+ case NITIO_G0_Command_Reg:
+ ni_660x_register = G0CommandRegister;
+ break;
+ case NITIO_G1_Command_Reg:
+ ni_660x_register = G1CommandRegister;
+ break;
+ case NITIO_G2_Command_Reg:
+ ni_660x_register = G2CommandRegister;
+ break;
+ case NITIO_G3_Command_Reg:
+ ni_660x_register = G3CommandRegister;
+ break;
+ case NITIO_G0_HW_Save_Reg:
+ ni_660x_register = G0HWSaveRegister;
+ break;
+ case NITIO_G1_HW_Save_Reg:
+ ni_660x_register = G1HWSaveRegister;
+ break;
+ case NITIO_G2_HW_Save_Reg:
+ ni_660x_register = G2HWSaveRegister;
+ break;
+ case NITIO_G3_HW_Save_Reg:
+ ni_660x_register = G3HWSaveRegister;
+ break;
+ case NITIO_G0_SW_Save_Reg:
+ ni_660x_register = G0SWSaveRegister;
+ break;
+ case NITIO_G1_SW_Save_Reg:
+ ni_660x_register = G1SWSaveRegister;
+ break;
+ case NITIO_G2_SW_Save_Reg:
+ ni_660x_register = G2SWSaveRegister;
+ break;
+ case NITIO_G3_SW_Save_Reg:
+ ni_660x_register = G3SWSaveRegister;
+ break;
+ case NITIO_G0_Mode_Reg:
+ ni_660x_register = G0ModeRegister;
+ break;
+ case NITIO_G1_Mode_Reg:
+ ni_660x_register = G1ModeRegister;
+ break;
+ case NITIO_G2_Mode_Reg:
+ ni_660x_register = G2ModeRegister;
+ break;
+ case NITIO_G3_Mode_Reg:
+ ni_660x_register = G3ModeRegister;
+ break;
+ case NITIO_G0_LoadA_Reg:
+ ni_660x_register = G0LoadARegister;
+ break;
+ case NITIO_G1_LoadA_Reg:
+ ni_660x_register = G1LoadARegister;
+ break;
+ case NITIO_G2_LoadA_Reg:
+ ni_660x_register = G2LoadARegister;
+ break;
+ case NITIO_G3_LoadA_Reg:
+ ni_660x_register = G3LoadARegister;
+ break;
+ case NITIO_G0_LoadB_Reg:
+ ni_660x_register = G0LoadBRegister;
+ break;
+ case NITIO_G1_LoadB_Reg:
+ ni_660x_register = G1LoadBRegister;
+ break;
+ case NITIO_G2_LoadB_Reg:
+ ni_660x_register = G2LoadBRegister;
+ break;
+ case NITIO_G3_LoadB_Reg:
+ ni_660x_register = G3LoadBRegister;
+ break;
+ case NITIO_G0_Input_Select_Reg:
+ ni_660x_register = G0InputSelectRegister;
+ break;
+ case NITIO_G1_Input_Select_Reg:
+ ni_660x_register = G1InputSelectRegister;
+ break;
+ case NITIO_G2_Input_Select_Reg:
+ ni_660x_register = G2InputSelectRegister;
+ break;
+ case NITIO_G3_Input_Select_Reg:
+ ni_660x_register = G3InputSelectRegister;
+ break;
+ case NITIO_G01_Status_Reg:
+ ni_660x_register = G01StatusRegister;
+ break;
+ case NITIO_G23_Status_Reg:
+ ni_660x_register = G23StatusRegister;
+ break;
+ case NITIO_G01_Joint_Reset_Reg:
+ ni_660x_register = G01JointResetRegister;
+ break;
+ case NITIO_G23_Joint_Reset_Reg:
+ ni_660x_register = G23JointResetRegister;
+ break;
+ case NITIO_G01_Joint_Status1_Reg:
+ ni_660x_register = G01JointStatus1Register;
+ break;
+ case NITIO_G23_Joint_Status1_Reg:
+ ni_660x_register = G23JointStatus1Register;
+ break;
+ case NITIO_G01_Joint_Status2_Reg:
+ ni_660x_register = G01JointStatus2Register;
+ break;
+ case NITIO_G23_Joint_Status2_Reg:
+ ni_660x_register = G23JointStatus2Register;
+ break;
+ case NITIO_G0_Counting_Mode_Reg:
+ ni_660x_register = G0CountingModeRegister;
+ break;
+ case NITIO_G1_Counting_Mode_Reg:
+ ni_660x_register = G1CountingModeRegister;
+ break;
+ case NITIO_G2_Counting_Mode_Reg:
+ ni_660x_register = G2CountingModeRegister;
+ break;
+ case NITIO_G3_Counting_Mode_Reg:
+ ni_660x_register = G3CountingModeRegister;
+ break;
+ case NITIO_G0_Second_Gate_Reg:
+ ni_660x_register = G0SecondGateRegister;
+ break;
+ case NITIO_G1_Second_Gate_Reg:
+ ni_660x_register = G1SecondGateRegister;
+ break;
+ case NITIO_G2_Second_Gate_Reg:
+ ni_660x_register = G2SecondGateRegister;
+ break;
+ case NITIO_G3_Second_Gate_Reg:
+ ni_660x_register = G3SecondGateRegister;
+ break;
+ case NITIO_G0_DMA_Config_Reg:
+ ni_660x_register = G0DMAConfigRegister;
+ break;
+ case NITIO_G0_DMA_Status_Reg:
+ ni_660x_register = G0DMAStatusRegister;
+ break;
+ case NITIO_G1_DMA_Config_Reg:
+ ni_660x_register = G1DMAConfigRegister;
+ break;
+ case NITIO_G1_DMA_Status_Reg:
+ ni_660x_register = G1DMAStatusRegister;
+ break;
+ case NITIO_G2_DMA_Config_Reg:
+ ni_660x_register = G2DMAConfigRegister;
+ break;
+ case NITIO_G2_DMA_Status_Reg:
+ ni_660x_register = G2DMAStatusRegister;
+ break;
+ case NITIO_G3_DMA_Config_Reg:
+ ni_660x_register = G3DMAConfigRegister;
+ break;
+ case NITIO_G3_DMA_Status_Reg:
+ ni_660x_register = G3DMAStatusRegister;
+ break;
+ case NITIO_G0_Interrupt_Acknowledge_Reg:
+ ni_660x_register = G0InterruptAcknowledge;
+ break;
+ case NITIO_G1_Interrupt_Acknowledge_Reg:
+ ni_660x_register = G1InterruptAcknowledge;
+ break;
+ case NITIO_G2_Interrupt_Acknowledge_Reg:
+ ni_660x_register = G2InterruptAcknowledge;
+ break;
+ case NITIO_G3_Interrupt_Acknowledge_Reg:
+ ni_660x_register = G3InterruptAcknowledge;
+ break;
+ case NITIO_G0_Status_Reg:
+ ni_660x_register = G0StatusRegister;
+ break;
+ case NITIO_G1_Status_Reg:
+ ni_660x_register = G0StatusRegister;
+ break;
+ case NITIO_G2_Status_Reg:
+ ni_660x_register = G0StatusRegister;
+ break;
+ case NITIO_G3_Status_Reg:
+ ni_660x_register = G0StatusRegister;
+ break;
+ case NITIO_G0_Interrupt_Enable_Reg:
+ ni_660x_register = G0InterruptEnable;
+ break;
+ case NITIO_G1_Interrupt_Enable_Reg:
+ ni_660x_register = G1InterruptEnable;
+ break;
+ case NITIO_G2_Interrupt_Enable_Reg:
+ ni_660x_register = G2InterruptEnable;
+ break;
+ case NITIO_G3_Interrupt_Enable_Reg:
+ ni_660x_register = G3InterruptEnable;
+ break;
+ default:
+ rt_printk("%s: unhandled register 0x%x in switch.\n",
+ __FUNCTION__, reg);
+ BUG();
+ return 0;
+ break;
+ }
+ return ni_660x_register;
+}
+
+static inline void ni_660x_write_register(struct comedi_device * dev,
+ unsigned chip_index, unsigned bits, enum NI_660x_Register reg)
+{
+ void *const write_address =
+ private(dev)->mite->daq_io_addr + GPCT_OFFSET[chip_index] +
+ registerData[reg].offset;
+
+ switch (registerData[reg].size) {
+ case DATA_2B:
+ writew(bits, write_address);
+ break;
+ case DATA_4B:
+ writel(bits, write_address);
+ break;
+ default:
+ rt_printk("%s: %s: bug! unhandled case (reg=0x%x) in switch.\n",
+ __FILE__, __FUNCTION__, reg);
+ BUG();
+ break;
+ }
+}
+
+static inline unsigned ni_660x_read_register(struct comedi_device * dev,
+ unsigned chip_index, enum NI_660x_Register reg)
+{
+ void *const read_address =
+ private(dev)->mite->daq_io_addr + GPCT_OFFSET[chip_index] +
+ registerData[reg].offset;
+
+ switch (registerData[reg].size) {
+ case DATA_2B:
+ return readw(read_address);
+ break;
+ case DATA_4B:
+ return readl(read_address);
+ break;
+ default:
+ rt_printk("%s: %s: bug! unhandled case (reg=0x%x) in switch.\n",
+ __FILE__, __FUNCTION__, reg);
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static void ni_gpct_write_register(struct ni_gpct *counter, unsigned bits,
+ enum ni_gpct_register reg)
+{
+ struct comedi_device *dev = counter->counter_dev->dev;
+ enum NI_660x_Register ni_660x_register = ni_gpct_to_660x_register(reg);
+ ni_660x_write_register(dev, counter->chip_index, bits,
+ ni_660x_register);
+}
+
+static unsigned ni_gpct_read_register(struct ni_gpct *counter,
+ enum ni_gpct_register reg)
+{
+ struct comedi_device *dev = counter->counter_dev->dev;
+ enum NI_660x_Register ni_660x_register = ni_gpct_to_660x_register(reg);
+ return ni_660x_read_register(dev, counter->chip_index,
+ ni_660x_register);
+}
+
+static inline struct mite_dma_descriptor_ring *mite_ring(struct ni_660x_private * priv,
+ struct ni_gpct *counter)
+{
+ return priv->mite_rings[counter->chip_index][counter->counter_index];
+}
+
+static inline void ni_660x_set_dma_channel(struct comedi_device * dev,
+ unsigned mite_channel, struct ni_gpct *counter)
+{
+ unsigned long flags;
+ comedi_spin_lock_irqsave(&private(dev)->soft_reg_copy_lock, flags);
+ private(dev)->dma_configuration_soft_copies[counter->chip_index] &=
+ ~dma_select_mask(mite_channel);
+ private(dev)->dma_configuration_soft_copies[counter->chip_index] |=
+ dma_select_bits(mite_channel,
+ dma_selection_counter(counter->counter_index));
+ ni_660x_write_register(dev, counter->chip_index,
+ private(dev)->dma_configuration_soft_copies[counter->
+ chip_index] | dma_reset_bit(mite_channel),
+ DMAConfigRegister);
+ mmiowb();
+ comedi_spin_unlock_irqrestore(&private(dev)->soft_reg_copy_lock, flags);
+}
+
+static inline void ni_660x_unset_dma_channel(struct comedi_device * dev,
+ unsigned mite_channel, struct ni_gpct *counter)
+{
+ unsigned long flags;
+ comedi_spin_lock_irqsave(&private(dev)->soft_reg_copy_lock, flags);
+ private(dev)->dma_configuration_soft_copies[counter->chip_index] &=
+ ~dma_select_mask(mite_channel);
+ private(dev)->dma_configuration_soft_copies[counter->chip_index] |=
+ dma_select_bits(mite_channel, dma_selection_none);
+ ni_660x_write_register(dev, counter->chip_index,
+ private(dev)->dma_configuration_soft_copies[counter->
+ chip_index], DMAConfigRegister);
+ mmiowb();
+ comedi_spin_unlock_irqrestore(&private(dev)->soft_reg_copy_lock, flags);
+}
+
+static int ni_660x_request_mite_channel(struct comedi_device * dev,
+ struct ni_gpct *counter, enum comedi_io_direction direction)
+{
+ unsigned long flags;
+ struct mite_channel *mite_chan;
+
+ comedi_spin_lock_irqsave(&private(dev)->mite_channel_lock, flags);
+ BUG_ON(counter->mite_chan);
+ mite_chan =
+ mite_request_channel(private(dev)->mite, mite_ring(private(dev),
+ counter));
+ if (mite_chan == NULL) {
+ comedi_spin_unlock_irqrestore(&private(dev)->mite_channel_lock,
+ flags);
+ comedi_error(dev,
+ "failed to reserve mite dma channel for counter.");
+ return -EBUSY;
+ }
+ mite_chan->dir = direction;
+ ni_tio_set_mite_channel(counter, mite_chan);
+ ni_660x_set_dma_channel(dev, mite_chan->channel, counter);
+ comedi_spin_unlock_irqrestore(&private(dev)->mite_channel_lock, flags);
+ return 0;
+}
+
+void ni_660x_release_mite_channel(struct comedi_device * dev, struct ni_gpct *counter)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&private(dev)->mite_channel_lock, flags);
+ if (counter->mite_chan) {
+ struct mite_channel *mite_chan = counter->mite_chan;
+
+ ni_660x_unset_dma_channel(dev, mite_chan->channel, counter);
+ ni_tio_set_mite_channel(counter, NULL);
+ mite_release_channel(mite_chan);
+ }
+ comedi_spin_unlock_irqrestore(&private(dev)->mite_channel_lock, flags);
+}
+
+static int ni_660x_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int retval;
+
+ struct ni_gpct *counter = subdev_to_counter(s);
+// const struct comedi_cmd *cmd = &s->async->cmd;
+
+ retval = ni_660x_request_mite_channel(dev, counter, COMEDI_INPUT);
+ if (retval) {
+ comedi_error(dev,
+ "no dma channel available for use by counter");
+ return retval;
+ }
+ ni_tio_acknowledge_and_confirm(counter, NULL, NULL, NULL, NULL);
+ retval = ni_tio_cmd(counter, s->async);
+
+ return retval;
+}
+
+static int ni_660x_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ struct ni_gpct *counter = subdev_to_counter(s);
+
+ return ni_tio_cmdtest(counter, cmd);
+}
+
+static int ni_660x_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct ni_gpct *counter = subdev_to_counter(s);
+ int retval;
+
+ retval = ni_tio_cancel(counter);
+ ni_660x_release_mite_channel(dev, counter);
+ return retval;
+}
+
+static void set_tio_counterswap(struct comedi_device * dev, int chipset)
+{
+ /* See P. 3.5 of the Register-Level Programming manual. The
+ CounterSwap bit has to be set on the second chip, otherwise
+ it will try to use the same pins as the first chip.
+ */
+ if (chipset)
+ ni_660x_write_register(dev, chipset, CounterSwap,
+ ClockConfigRegister);
+ else
+ ni_660x_write_register(dev, chipset, 0, ClockConfigRegister);
+}
+
+static void ni_660x_handle_gpct_interrupt(struct comedi_device * dev,
+ struct comedi_subdevice * s)
+{
+ ni_tio_handle_interrupt(subdev_to_counter(s), s);
+ if (s->async->events) {
+ if (s->async->
+ events & (COMEDI_CB_EOA | COMEDI_CB_ERROR |
+ COMEDI_CB_OVERFLOW)) {
+ ni_660x_cancel(dev, s);
+ }
+ comedi_event(dev, s);
+ }
+}
+
+static irqreturn_t ni_660x_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s;
+ unsigned i;
+
+ if (dev->attached == 0)
+ return IRQ_NONE;
+ smp_mb();
+ for (i = 0; i < ni_660x_num_counters(dev); ++i) {
+ s = dev->subdevices + NI_660X_GPCT_SUBDEV(i);
+ ni_660x_handle_gpct_interrupt(dev, s);
+ }
+ return IRQ_HANDLED;
+}
+
+static int ni_660x_buf_change(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned long new_size)
+{
+ int ret;
+
+ ret = mite_buf_change(mite_ring(private(dev), subdev_to_counter(s)),
+ s->async);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ni_660x_allocate_private(struct comedi_device * dev)
+{
+ int retval;
+ unsigned i;
+
+ if ((retval = alloc_private(dev, sizeof(struct ni_660x_private))) < 0)
+ return retval;
+ spin_lock_init(&private(dev)->mite_channel_lock);
+ spin_lock_init(&private(dev)->soft_reg_copy_lock);
+ for (i = 0; i < NUM_PFI_CHANNELS; ++i) {
+ private(dev)->pfi_output_selects[i] = pfi_output_select_counter;
+ }
+ return 0;
+}
+
+static int ni_660x_alloc_mite_rings(struct comedi_device * dev)
+{
+ unsigned i;
+ unsigned j;
+
+ for (i = 0; i < board(dev)->n_chips; ++i) {
+ for (j = 0; j < counters_per_chip; ++j) {
+ private(dev)->mite_rings[i][j] =
+ mite_alloc_ring(private(dev)->mite);
+ if (private(dev)->mite_rings[i][j] == NULL) {
+ return -ENOMEM;
+ }
+ }
+ }
+ return 0;
+}
+
+static void ni_660x_free_mite_rings(struct comedi_device * dev)
+{
+ unsigned i;
+ unsigned j;
+
+ for (i = 0; i < board(dev)->n_chips; ++i) {
+ for (j = 0; j < counters_per_chip; ++j) {
+ mite_free_ring(private(dev)->mite_rings[i][j]);
+ }
+ }
+}
+
+static int ni_660x_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int ret;
+ unsigned i;
+ unsigned global_interrupt_config_bits;
+
+ printk("comedi%d: ni_660x: ", dev->minor);
+
+ ret = ni_660x_allocate_private(dev);
+ if (ret < 0)
+ return ret;
+ ret = ni_660x_find_device(dev, it->options[0], it->options[1]);
+ if (ret < 0)
+ return ret;
+
+ dev->board_name = board(dev)->name;
+
+ ret = mite_setup2(private(dev)->mite, 1);
+ if (ret < 0) {
+ printk("error setting up mite\n");
+ return ret;
+ }
+ comedi_set_hw_dev(dev, &private(dev)->mite->pcidev->dev);
+ ret = ni_660x_alloc_mite_rings(dev);
+ if (ret < 0)
+ return ret;
+
+ printk(" %s ", dev->board_name);
+
+ dev->n_subdevices = 2 + NI_660X_MAX_NUM_COUNTERS;
+
+ if (alloc_subdevices(dev, dev->n_subdevices) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ /* Old GENERAL-PURPOSE COUNTER/TIME (GPCT) subdevice, no longer used */
+ s->type = COMEDI_SUBD_UNUSED;
+
+ s = dev->subdevices + NI_660X_DIO_SUBDEV;
+ /* DIGITAL I/O SUBDEVICE */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = NUM_PFI_CHANNELS;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = ni_660x_dio_insn_bits;
+ s->insn_config = ni_660x_dio_insn_config;
+ s->io_bits = 0; /* all bits default to input */
+ // we use the ioconfig registers to control dio direction, so zero output enables in stc dio control reg
+ ni_660x_write_register(dev, 0, 0, STCDIOControl);
+
+ private(dev)->counter_dev = ni_gpct_device_construct(dev,
+ &ni_gpct_write_register, &ni_gpct_read_register,
+ ni_gpct_variant_660x, ni_660x_num_counters(dev));
+ if (private(dev)->counter_dev == NULL)
+ return -ENOMEM;
+ for (i = 0; i < NI_660X_MAX_NUM_COUNTERS; ++i) {
+ s = dev->subdevices + NI_660X_GPCT_SUBDEV(i);
+ if (i < ni_660x_num_counters(dev)) {
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags =
+ SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL |
+ SDF_CMD_READ /* | SDF_CMD_WRITE */ ;
+ s->n_chan = 3;
+ s->maxdata = 0xffffffff;
+ s->insn_read = ni_660x_GPCT_rinsn;
+ s->insn_write = ni_660x_GPCT_winsn;
+ s->insn_config = ni_660x_GPCT_insn_config;
+ s->do_cmd = &ni_660x_cmd;
+ s->len_chanlist = 1;
+ s->do_cmdtest = &ni_660x_cmdtest;
+ s->cancel = &ni_660x_cancel;
+ s->async_dma_dir = DMA_BIDIRECTIONAL;
+ s->buf_change = &ni_660x_buf_change;
+ s->private = &private(dev)->counter_dev->counters[i];
+
+ private(dev)->counter_dev->counters[i].chip_index =
+ i / counters_per_chip;
+ private(dev)->counter_dev->counters[i].counter_index =
+ i % counters_per_chip;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+ }
+ for (i = 0; i < board(dev)->n_chips; ++i) {
+ init_tio_chip(dev, i);
+ }
+ for (i = 0; i < ni_660x_num_counters(dev); ++i) {
+ ni_tio_init_counter(&private(dev)->counter_dev->counters[i]);
+ }
+ for (i = 0; i < NUM_PFI_CHANNELS; ++i) {
+ if (i < min_counter_pfi_chan)
+ ni_660x_set_pfi_routing(dev, i, pfi_output_select_do);
+ else
+ ni_660x_set_pfi_routing(dev, i,
+ pfi_output_select_counter);
+ ni_660x_select_pfi_output(dev, i, pfi_output_select_high_Z);
+ }
+ /* to be safe, set counterswap bits on tio chips after all the counter
+ outputs have been set to high impedance mode */
+ for (i = 0; i < board(dev)->n_chips; ++i) {
+ set_tio_counterswap(dev, i);
+ }
+ if ((ret = comedi_request_irq(mite_irq(private(dev)->mite),
+ &ni_660x_interrupt, IRQF_SHARED, "ni_660x",
+ dev)) < 0) {
+ printk(" irq not available\n");
+ return ret;
+ }
+ dev->irq = mite_irq(private(dev)->mite);
+ global_interrupt_config_bits = Global_Int_Enable_Bit;
+ if (board(dev)->n_chips > 1)
+ global_interrupt_config_bits |= Cascade_Int_Enable_Bit;
+ ni_660x_write_register(dev, 0, global_interrupt_config_bits,
+ GlobalInterruptConfigRegister);
+ printk("attached\n");
+ return 0;
+}
+
+static int ni_660x_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: ni_660x: remove\n", dev->minor);
+
+ /* Free irq */
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+
+ if (dev->private) {
+ if (private(dev)->counter_dev)
+ ni_gpct_device_destroy(private(dev)->counter_dev);
+ if (private(dev)->mite) {
+ ni_660x_free_mite_rings(dev);
+ mite_unsetup(private(dev)->mite);
+ }
+ }
+ return 0;
+}
+
+static int
+ni_660x_GPCT_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ return ni_tio_rinsn(subdev_to_counter(s), insn, data);
+}
+
+static void init_tio_chip(struct comedi_device * dev, int chipset)
+{
+ unsigned i;
+
+ // init dma configuration register
+ private(dev)->dma_configuration_soft_copies[chipset] = 0;
+ for (i = 0; i < MAX_DMA_CHANNEL; ++i) {
+ private(dev)->dma_configuration_soft_copies[chipset] |=
+ dma_select_bits(i,
+ dma_selection_none) & dma_select_mask(i);
+ }
+ ni_660x_write_register(dev, chipset,
+ private(dev)->dma_configuration_soft_copies[chipset],
+ DMAConfigRegister);
+ for(i = 0; i < NUM_PFI_CHANNELS; ++i)
+ {
+ ni_660x_write_register(dev, chipset, 0, IOConfigReg(i));
+ }
+}
+
+static int
+ni_660x_GPCT_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ return ni_tio_insn_config(subdev_to_counter(s), insn, data);
+}
+
+static int ni_660x_GPCT_winsn(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ return ni_tio_winsn(subdev_to_counter(s), insn, data);
+}
+
+static int ni_660x_find_device(struct comedi_device * dev, int bus, int slot)
+{
+ struct mite_struct *mite;
+ int i;
+
+ for (mite = mite_devices; mite; mite = mite->next) {
+ if (mite->used)
+ continue;
+ if (bus || slot) {
+ if (bus != mite->pcidev->bus->number ||
+ slot != PCI_SLOT(mite->pcidev->devfn))
+ continue;
+ }
+
+ for (i = 0; i < n_ni_660x_boards; i++) {
+ if (mite_device_id(mite) == ni_660x_boards[i].dev_id) {
+ dev->board_ptr = ni_660x_boards + i;
+ private(dev)->mite = mite;
+ return 0;
+ }
+ }
+ }
+ printk("no device found\n");
+ mite_list_devices();
+ return -EIO;
+}
+
+static int ni_660x_dio_insn_bits(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned base_bitfield_channel = CR_CHAN(insn->chanspec);
+
+ // Check if we have to write some bits
+ if (data[0]) {
+ s->state &= ~(data[0] << base_bitfield_channel);
+ s->state |= (data[0] & data[1]) << base_bitfield_channel;
+ /* Write out the new digital output lines */
+ ni_660x_write_register(dev, 0, s->state, DIO32Output);
+ }
+ /* on return, data[1] contains the value of the digital
+ * input and output lines. */
+ data[1] =
+ (ni_660x_read_register(dev, 0,
+ DIO32Input) >> base_bitfield_channel);
+ return 2;
+}
+
+static void ni_660x_select_pfi_output(struct comedi_device * dev, unsigned pfi_channel,
+ unsigned output_select)
+{
+ static const unsigned counter_4_7_first_pfi = 8;
+ static const unsigned counter_4_7_last_pfi = 23;
+ unsigned active_chipset = 0;
+ unsigned idle_chipset = 0;
+ unsigned active_bits;
+ unsigned idle_bits;
+
+ if(board(dev)->n_chips > 1) {
+ if(output_select == pfi_output_select_counter &&
+ pfi_channel >= counter_4_7_first_pfi &&
+ pfi_channel <= counter_4_7_last_pfi) {
+ active_chipset = 1;
+ idle_chipset = 0;
+ }else {
+ active_chipset = 0;
+ idle_chipset = 1;
+ }
+ }
+
+ if(idle_chipset != active_chipset) {
+ idle_bits = ni_660x_read_register(dev, idle_chipset, IOConfigReg(pfi_channel));
+ idle_bits &= ~pfi_output_select_mask(pfi_channel);
+ idle_bits |= pfi_output_select_bits(pfi_channel, pfi_output_select_high_Z);
+ ni_660x_write_register(dev, idle_chipset, idle_bits, IOConfigReg(pfi_channel));
+ }
+
+ active_bits = ni_660x_read_register(dev, active_chipset, IOConfigReg(pfi_channel));
+ active_bits &= ~pfi_output_select_mask(pfi_channel);
+ active_bits |= pfi_output_select_bits(pfi_channel, output_select);
+ ni_660x_write_register(dev, active_chipset, active_bits, IOConfigReg(pfi_channel));
+}
+
+static int ni_660x_set_pfi_routing(struct comedi_device * dev, unsigned chan,
+ unsigned source)
+{
+ if (source > num_pfi_output_selects)
+ return -EINVAL;
+ if (source == pfi_output_select_high_Z)
+ return -EINVAL;
+ if (chan < min_counter_pfi_chan) {
+ if (source == pfi_output_select_counter)
+ return -EINVAL;
+ } else if (chan > max_dio_pfi_chan) {
+ if (source == pfi_output_select_do)
+ return -EINVAL;
+ }
+ BUG_ON(chan >= NUM_PFI_CHANNELS);
+
+ private(dev)->pfi_output_selects[chan] = source;
+ if (private(dev)->pfi_direction_bits & (((uint64_t) 1) << chan))
+ ni_660x_select_pfi_output(dev, chan,
+ private(dev)->pfi_output_selects[chan]);
+ return 0;
+}
+
+static unsigned ni_660x_get_pfi_routing(struct comedi_device * dev, unsigned chan)
+{
+ BUG_ON(chan >= NUM_PFI_CHANNELS);
+ return private(dev)->pfi_output_selects[chan];
+}
+
+static void ni660x_config_filter(struct comedi_device * dev, unsigned pfi_channel,
+ enum ni_gpct_filter_select filter)
+{
+ unsigned bits = ni_660x_read_register(dev, 0, IOConfigReg(pfi_channel));
+ bits &= ~pfi_input_select_mask(pfi_channel);
+ bits |= pfi_input_select_bits(pfi_channel, filter);
+ ni_660x_write_register(dev, 0, bits, IOConfigReg(pfi_channel));
+}
+
+static int ni_660x_dio_insn_config(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+
+ /* The input or output configuration of each digital line is
+ * configured by a special insn_config instruction. chanspec
+ * contains the channel to be changed, and data[0] contains the
+ * value COMEDI_INPUT or COMEDI_OUTPUT. */
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ private(dev)->pfi_direction_bits |= ((uint64_t) 1) << chan;
+ ni_660x_select_pfi_output(dev, chan,
+ private(dev)->pfi_output_selects[chan]);
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ private(dev)->pfi_direction_bits &= ~(((uint64_t) 1) << chan);
+ ni_660x_select_pfi_output(dev, chan, pfi_output_select_high_Z);
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (private(dev)->
+ pfi_direction_bits & (((uint64_t) 1) << chan)) ?
+ COMEDI_OUTPUT : COMEDI_INPUT;
+ return 0;
+ case INSN_CONFIG_SET_ROUTING:
+ return ni_660x_set_pfi_routing(dev, chan, data[1]);
+ break;
+ case INSN_CONFIG_GET_ROUTING:
+ data[1] = ni_660x_get_pfi_routing(dev, chan);
+ break;
+ case INSN_CONFIG_FILTER:
+ ni660x_config_filter(dev, chan, data[1]);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ };
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/ni_670x.c b/drivers/staging/comedi/drivers/ni_670x.c
new file mode 100644
index 000000000000..d1312e073a65
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_670x.c
@@ -0,0 +1,337 @@
+/*
+ comedi/drivers/ni_670x.c
+ Hardware driver for NI 670x devices
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2001 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: ni_670x
+Description: National Instruments 670x
+Author: Bart Joris <bjoris@advalvas.be>
+Updated: Wed, 11 Dec 2002 18:25:35 -0800
+Devices: [National Instruments] PCI-6703 (ni_670x), PCI-6704
+Status: unknown
+
+Commands are not supported.
+*/
+
+/*
+ Bart Joris <bjoris@advalvas.be> Last updated on 20/08/2001
+
+ Manuals:
+
+ 322110a.pdf PCI/PXI-6704 User Manual
+ 322110b.pdf PCI/PXI-6703/6704 User Manual
+
+*/
+
+#include "../comedidev.h"
+
+#include "mite.h"
+
+#define PCI_VENDOR_ID_NATINST 0x1093
+
+#define AO_VALUE_OFFSET 0x00
+#define AO_CHAN_OFFSET 0x0c
+#define AO_STATUS_OFFSET 0x10
+#define AO_CONTROL_OFFSET 0x10
+#define DIO_PORT0_DIR_OFFSET 0x20
+#define DIO_PORT0_DATA_OFFSET 0x24
+#define DIO_PORT1_DIR_OFFSET 0x28
+#define DIO_PORT1_DATA_OFFSET 0x2c
+#define MISC_STATUS_OFFSET 0x14
+#define MISC_CONTROL_OFFSET 0x14
+
+/* Board description*/
+
+struct ni_670x_board {
+ unsigned short dev_id;
+ const char *name;
+ unsigned short ao_chans;
+ unsigned short ao_bits;
+};
+
+static const struct ni_670x_board ni_670x_boards[] = {
+ {
+ dev_id: 0x2c90,
+ name: "PCI-6703",
+ ao_chans:16,
+ ao_bits: 16,
+ },
+ {
+ dev_id: 0x1920,
+ name: "PXI-6704",
+ ao_chans:32,
+ ao_bits: 16,
+ },
+ {
+ dev_id: 0x1290,
+ name: "PCI-6704",
+ ao_chans:32,
+ ao_bits: 16,
+ },
+};
+
+static DEFINE_PCI_DEVICE_TABLE(ni_670x_pci_table) = {
+ {PCI_VENDOR_ID_NATINST, 0x2c90, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ //{ PCI_VENDOR_ID_NATINST, 0x0000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, ni_670x_pci_table);
+
+#define thisboard ((struct ni_670x_board *)dev->board_ptr)
+
+struct ni_670x_private {
+
+ struct mite_struct *mite;
+ int boardtype;
+ int dio;
+ unsigned int ao_readback[32];
+};
+
+
+#define devpriv ((struct ni_670x_private *)dev->private)
+#define n_ni_670x_boards (sizeof(ni_670x_boards)/sizeof(ni_670x_boards[0]))
+
+static int ni_670x_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int ni_670x_detach(struct comedi_device * dev);
+
+static struct comedi_driver driver_ni_670x = {
+ driver_name:"ni_670x",
+ module:THIS_MODULE,
+ attach:ni_670x_attach,
+ detach:ni_670x_detach,
+};
+
+COMEDI_PCI_INITCLEANUP(driver_ni_670x, ni_670x_pci_table);
+
+static struct comedi_lrange range_0_20mA = { 1, {RANGE_mA(0, 20)} };
+
+static int ni_670x_find_device(struct comedi_device * dev, int bus, int slot);
+
+static int ni_670x_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ni_670x_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ni_670x_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ni_670x_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int ni_670x_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int ret;
+ int i;
+
+ printk("comedi%d: ni_670x: ", dev->minor);
+
+ if ((ret = alloc_private(dev, sizeof(struct ni_670x_private))) < 0)
+ return ret;
+
+ ret = ni_670x_find_device(dev, it->options[0], it->options[1]);
+ if (ret < 0)
+ return ret;
+
+ ret = mite_setup(devpriv->mite);
+ if (ret < 0) {
+ printk("error setting up mite\n");
+ return ret;
+ }
+ dev->board_name = thisboard->name;
+ dev->irq = mite_irq(devpriv->mite);
+ printk(" %s", dev->board_name);
+
+ if (alloc_subdevices(dev, 2) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = thisboard->ao_chans;
+ s->maxdata = 0xffff;
+ if (s->n_chan == 32) {
+ const struct comedi_lrange **range_table_list;
+
+ range_table_list = kmalloc(sizeof(struct comedi_lrange *) * 32,
+ GFP_KERNEL);
+ if (!range_table_list)
+ return -ENOMEM;
+ s->range_table_list = range_table_list;
+ for (i = 0; i < 16; i++) {
+ range_table_list[i] = &range_bipolar10;
+ range_table_list[16 + i] = &range_0_20mA;
+ }
+ } else {
+ s->range_table = &range_bipolar10;
+ }
+ s->insn_write = &ni_670x_ao_winsn;
+ s->insn_read = &ni_670x_ao_rinsn;
+
+ s = dev->subdevices + 1;
+ /* digital i/o subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = ni_670x_dio_insn_bits;
+ s->insn_config = ni_670x_dio_insn_config;
+
+ writel(0x10, devpriv->mite->daq_io_addr + MISC_CONTROL_OFFSET); /* Config of misc registers */
+ writel(0x00, devpriv->mite->daq_io_addr + AO_CONTROL_OFFSET); /* Config of ao registers */
+
+ printk("attached\n");
+
+ return 1;
+}
+
+static int ni_670x_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: ni_670x: remove\n", dev->minor);
+
+ if (dev->subdevices[0].range_table_list) {
+ kfree(dev->subdevices[0].range_table_list);
+ }
+ if (dev->private && devpriv->mite)
+ mite_unsetup(devpriv->mite);
+
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+
+ return 0;
+}
+
+static int ni_670x_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ /* Channel number mapping :
+
+ NI 6703/ NI 6704 | NI 6704 Only
+ ----------------------------------------------------
+ vch(0) : 0 | ich(16) : 1
+ vch(1) : 2 | ich(17) : 3
+ . : . | . .
+ . : . | . .
+ . : . | . .
+ vch(15) : 30 | ich(31) : 31 */
+
+ for (i = 0; i < insn->n; i++) {
+ writel(((chan & 15) << 1) | ((chan & 16) >> 4), devpriv->mite->daq_io_addr + AO_CHAN_OFFSET); /* First write in channel register which channel to use */
+ writel(data[i], devpriv->mite->daq_io_addr + AO_VALUE_OFFSET); /* write channel value */
+ devpriv->ao_readback[chan] = data[i];
+ }
+
+ return i;
+}
+
+static int ni_670x_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = devpriv->ao_readback[chan];
+
+ return i;
+}
+
+static int ni_670x_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ /* The insn data is a mask in data[0] and the new data
+ * in data[1], each channel cooresponding to a bit. */
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+ writel(s->state,
+ devpriv->mite->daq_io_addr + DIO_PORT0_DATA_OFFSET);
+ }
+
+ /* on return, data[1] contains the value of the digital
+ * input lines. */
+ data[1] = readl(devpriv->mite->daq_io_addr + DIO_PORT0_DATA_OFFSET);
+
+ return 2;
+}
+
+static int ni_670x_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= 1 << chan;
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~(1 << chan);
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (s->
+ io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ writel(s->io_bits, devpriv->mite->daq_io_addr + DIO_PORT0_DIR_OFFSET);
+
+ return insn->n;
+}
+
+static int ni_670x_find_device(struct comedi_device * dev, int bus, int slot)
+{
+ struct mite_struct *mite;
+ int i;
+
+ for (mite = mite_devices; mite; mite = mite->next) {
+ if (mite->used)
+ continue;
+ if (bus || slot) {
+ if (bus != mite->pcidev->bus->number
+ || slot != PCI_SLOT(mite->pcidev->devfn))
+ continue;
+ }
+
+ for (i = 0; i < n_ni_670x_boards; i++) {
+ if (mite_device_id(mite) == ni_670x_boards[i].dev_id) {
+ dev->board_ptr = ni_670x_boards + i;
+ devpriv->mite = mite;
+
+ return 0;
+ }
+ }
+ }
+ printk("no device found\n");
+ mite_list_devices();
+ return -EIO;
+}
diff --git a/drivers/staging/comedi/drivers/ni_at_a2150.c b/drivers/staging/comedi/drivers/ni_at_a2150.c
new file mode 100644
index 000000000000..5d45bf24a5ef
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_at_a2150.c
@@ -0,0 +1,909 @@
+/*
+ comedi/drivers/ni_at_a2150.c
+ Driver for National Instruments AT-A2150 boards
+ Copyright (C) 2001, 2002 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+************************************************************************
+*/
+/*
+Driver: ni_at_a2150
+Description: National Instruments AT-A2150
+Author: Frank Mori Hess
+Status: works
+Devices: [National Instruments] AT-A2150C (at_a2150c), AT-2150S (at_a2150s)
+
+If you want to ac couple the board's inputs, use AREF_OTHER.
+
+Configuration options:
+ [0] - I/O port base address
+ [1] - IRQ (optional, required for timed conversions)
+ [2] - DMA (optional, required for timed conversions)
+
+*/
+/*
+Yet another driver for obsolete hardware brought to you by Frank Hess.
+Testing and debugging help provided by Dave Andruczyk.
+
+This driver supports the boards:
+
+AT-A2150C
+AT-A2150S
+
+The only difference is their master clock frequencies.
+
+Options:
+ [0] - base io address
+ [1] - irq
+ [2] - dma channel
+
+References (from ftp://ftp.natinst.com/support/manuals):
+
+ 320360.pdf AT-A2150 User Manual
+
+TODO:
+
+analog level triggering
+TRIG_WAKE_EOS
+
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+#include <asm/dma.h>
+
+#include "8253.h"
+#include "comedi_fc.h"
+
+#define A2150_SIZE 28
+#define A2150_DMA_BUFFER_SIZE 0xff00 // size in bytes of dma buffer
+
+//#define A2150_DEBUG // enable debugging code
+#undef A2150_DEBUG // disable debugging code
+
+/* Registers and bits */
+#define CONFIG_REG 0x0
+#define CHANNEL_BITS(x) ((x) & 0x7)
+#define CHANNEL_MASK 0x7
+#define CLOCK_SELECT_BITS(x) (((x) & 0x3) << 3)
+#define CLOCK_DIVISOR_BITS(x) (((x) & 0x3) << 5)
+#define CLOCK_MASK (0xf << 3)
+#define ENABLE0_BIT 0x80 // enable (don't internally ground) channels 0 and 1
+#define ENABLE1_BIT 0x100 // enable (don't internally ground) channels 2 and 3
+#define AC0_BIT 0x200 // ac couple channels 0,1
+#define AC1_BIT 0x400 // ac couple channels 2,3
+#define APD_BIT 0x800 // analog power down
+#define DPD_BIT 0x1000 // digital power down
+#define TRIGGER_REG 0x2 // trigger config register
+#define POST_TRIGGER_BITS 0x2
+#define DELAY_TRIGGER_BITS 0x3
+#define HW_TRIG_EN 0x10 // enable hardware trigger
+#define FIFO_START_REG 0x6 // software start aquistion trigger
+#define FIFO_RESET_REG 0x8 // clears fifo + fifo flags
+#define FIFO_DATA_REG 0xa // read data
+#define DMA_TC_CLEAR_REG 0xe // clear dma terminal count interrupt
+#define STATUS_REG 0x12 // read only
+#define FNE_BIT 0x1 // fifo not empty
+#define OVFL_BIT 0x8 // fifo overflow
+#define EDAQ_BIT 0x10 // end of aquisition interrupt
+#define DCAL_BIT 0x20 // offset calibration in progress
+#define INTR_BIT 0x40 // interrupt has occured
+#define DMA_TC_BIT 0x80 // dma terminal count interrupt has occured
+#define ID_BITS(x) (((x) >> 8) & 0x3)
+#define IRQ_DMA_CNTRL_REG 0x12 // write only
+#define DMA_CHAN_BITS(x) ((x) & 0x7) // sets dma channel
+#define DMA_EN_BIT 0x8 // enables dma
+#define IRQ_LVL_BITS(x) (((x) & 0xf) << 4) // sets irq level
+#define FIFO_INTR_EN_BIT 0x100 // enable fifo interrupts
+#define FIFO_INTR_FHF_BIT 0x200 // interrupt fifo half full
+#define DMA_INTR_EN_BIT 0x800 // enable interrupt on dma terminal count
+#define DMA_DEM_EN_BIT 0x1000 // enables demand mode dma
+#define I8253_BASE_REG 0x14
+#define I8253_MODE_REG 0x17
+#define HW_COUNT_DISABLE 0x30 // disable hardware counting of conversions
+
+struct a2150_board {
+ const char *name;
+ int clock[4]; // master clock periods, in nanoseconds
+ int num_clocks; // number of available master clock speeds
+ int ai_speed; // maximum conversion rate in nanoseconds
+};
+
+//analog input range
+static const struct comedi_lrange range_a2150 = {
+ 1,
+ {
+ RANGE(-2.828, 2.828),
+ }
+};
+
+// enum must match board indices
+enum { a2150_c, a2150_s };
+static const struct a2150_board a2150_boards[] = {
+ {
+ name: "at-a2150c",
+ clock: {31250, 22676, 20833, 19531},
+ num_clocks:4,
+ ai_speed:19531,
+ },
+ {
+ name: "at-a2150s",
+ clock: {62500, 50000, 41667, 0},
+ num_clocks:3,
+ ai_speed:41667,
+ },
+};
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct a2150_board *)dev->board_ptr)
+
+struct a2150_private {
+
+ volatile unsigned int count; /* number of data points left to be taken */
+ unsigned int dma; // dma channel
+ s16 *dma_buffer; // dma buffer
+ unsigned int dma_transfer_size; // size in bytes of dma transfers
+ int irq_dma_bits; // irq/dma register bits
+ int config_bits; // config register bits
+};
+
+
+#define devpriv ((struct a2150_private *)dev->private)
+
+static int a2150_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int a2150_detach(struct comedi_device * dev);
+static int a2150_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+
+static struct comedi_driver driver_a2150 = {
+ driver_name:"ni_at_a2150",
+ module:THIS_MODULE,
+ attach:a2150_attach,
+ detach:a2150_detach,
+};
+
+static irqreturn_t a2150_interrupt(int irq, void *d PT_REGS_ARG);
+static int a2150_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int a2150_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int a2150_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int a2150_get_timing(struct comedi_device * dev, unsigned int *period,
+ int flags);
+static int a2150_probe(struct comedi_device * dev);
+static int a2150_set_chanlist(struct comedi_device * dev, unsigned int start_channel,
+ unsigned int num_channels);
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_INITCLEANUP(driver_a2150);
+
+#ifdef A2150_DEBUG
+
+static void ni_dump_regs(struct comedi_device * dev)
+{
+ rt_printk("config bits 0x%x\n", devpriv->config_bits);
+ rt_printk("irq dma bits 0x%x\n", devpriv->irq_dma_bits);
+ rt_printk("status bits 0x%x\n", inw(dev->iobase + STATUS_REG));
+}
+
+#endif
+
+/* interrupt service routine */
+static irqreturn_t a2150_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ int i;
+ int status;
+ unsigned long flags;
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async;
+ struct comedi_cmd *cmd;
+ unsigned int max_points, num_points, residue, leftover;
+ short dpnt;
+ static const int sample_size = sizeof(devpriv->dma_buffer[0]);
+
+ if (dev->attached == 0) {
+ comedi_error(dev, "premature interrupt");
+ return IRQ_HANDLED;
+ }
+ // initialize async here to make sure s is not NULL
+ async = s->async;
+ async->events = 0;
+ cmd = &async->cmd;
+
+ status = inw(dev->iobase + STATUS_REG);
+
+ if ((status & INTR_BIT) == 0) {
+ comedi_error(dev, "spurious interrupt");
+ return IRQ_NONE;
+ }
+
+ if (status & OVFL_BIT) {
+ comedi_error(dev, "fifo overflow");
+ a2150_cancel(dev, s);
+ async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ }
+
+ if ((status & DMA_TC_BIT) == 0) {
+ comedi_error(dev, "caught non-dma interrupt? Aborting.");
+ a2150_cancel(dev, s);
+ async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+ }
+
+ flags = claim_dma_lock();
+ disable_dma(devpriv->dma);
+ /* clear flip-flop to make sure 2-byte registers for
+ * count and address get set correctly */
+ clear_dma_ff(devpriv->dma);
+
+ // figure out how many points to read
+ max_points = devpriv->dma_transfer_size / sample_size;
+ /* residue is the number of points left to be done on the dma
+ * transfer. It should always be zero at this point unless
+ * the stop_src is set to external triggering.
+ */
+ residue = get_dma_residue(devpriv->dma) / sample_size;
+ num_points = max_points - residue;
+ if (devpriv->count < num_points && cmd->stop_src == TRIG_COUNT)
+ num_points = devpriv->count;
+
+ // figure out how many points will be stored next time
+ leftover = 0;
+ if (cmd->stop_src == TRIG_NONE) {
+ leftover = devpriv->dma_transfer_size / sample_size;
+ } else if (devpriv->count > max_points) {
+ leftover = devpriv->count - max_points;
+ if (leftover > max_points)
+ leftover = max_points;
+ }
+ /* there should only be a residue if collection was stopped by having
+ * the stop_src set to an external trigger, in which case there
+ * will be no more data
+ */
+ if (residue)
+ leftover = 0;
+
+ for (i = 0; i < num_points; i++) {
+ /* write data point to comedi buffer */
+ dpnt = devpriv->dma_buffer[i];
+ // convert from 2's complement to unsigned coding
+ dpnt ^= 0x8000;
+ cfc_write_to_buffer(s, dpnt);
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (--devpriv->count == 0) { /* end of acquisition */
+ a2150_cancel(dev, s);
+ async->events |= COMEDI_CB_EOA;
+ break;
+ }
+ }
+ }
+ // re-enable dma
+ if (leftover) {
+ set_dma_addr(devpriv->dma, virt_to_bus(devpriv->dma_buffer));
+ set_dma_count(devpriv->dma, leftover * sample_size);
+ enable_dma(devpriv->dma);
+ }
+ release_dma_lock(flags);
+
+ async->events |= COMEDI_CB_BLOCK;
+
+ comedi_event(dev, s);
+
+ /* clear interrupt */
+ outw(0x00, dev->iobase + DMA_TC_CLEAR_REG);
+
+ return IRQ_HANDLED;
+}
+
+// probes board type, returns offset
+static int a2150_probe(struct comedi_device * dev)
+{
+ int status = inw(dev->iobase + STATUS_REG);
+ return ID_BITS(status);
+}
+
+static int a2150_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase = it->options[0];
+ unsigned int irq = it->options[1];
+ unsigned int dma = it->options[2];
+ static const int timeout = 2000;
+ int i;
+
+ printk("comedi%d: %s: io 0x%lx", dev->minor, driver_a2150.driver_name,
+ iobase);
+ if (irq) {
+ printk(", irq %u", irq);
+ } else {
+ printk(", no irq");
+ }
+ if (dma) {
+ printk(", dma %u", dma);
+ } else {
+ printk(", no dma");
+ }
+ printk("\n");
+
+ /* allocate and initialize dev->private */
+ if (alloc_private(dev, sizeof(struct a2150_private)) < 0)
+ return -ENOMEM;
+
+ if (iobase == 0) {
+ printk(" io base address required\n");
+ return -EINVAL;
+ }
+
+ /* check if io addresses are available */
+ if (!request_region(iobase, A2150_SIZE, driver_a2150.driver_name)) {
+ printk(" I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ /* grab our IRQ */
+ if (irq) {
+ // check that irq is supported
+ if (irq < 3 || irq == 8 || irq == 13 || irq > 15) {
+ printk(" invalid irq line %u\n", irq);
+ return -EINVAL;
+ }
+ if (comedi_request_irq(irq, a2150_interrupt, 0,
+ driver_a2150.driver_name, dev)) {
+ printk("unable to allocate irq %u\n", irq);
+ return -EINVAL;
+ }
+ devpriv->irq_dma_bits |= IRQ_LVL_BITS(irq);
+ dev->irq = irq;
+ }
+ // initialize dma
+ if (dma) {
+ if (dma == 4 || dma > 7) {
+ printk(" invalid dma channel %u\n", dma);
+ return -EINVAL;
+ }
+ if (request_dma(dma, driver_a2150.driver_name)) {
+ printk(" failed to allocate dma channel %u\n", dma);
+ return -EINVAL;
+ }
+ devpriv->dma = dma;
+ devpriv->dma_buffer =
+ kmalloc(A2150_DMA_BUFFER_SIZE, GFP_KERNEL | GFP_DMA);
+ if (devpriv->dma_buffer == NULL)
+ return -ENOMEM;
+
+ disable_dma(dma);
+ set_dma_mode(dma, DMA_MODE_READ);
+
+ devpriv->irq_dma_bits |= DMA_CHAN_BITS(dma);
+ }
+
+ dev->board_ptr = a2150_boards + a2150_probe(dev);
+ dev->board_name = thisboard->name;
+
+ if (alloc_subdevices(dev, 1) < 0)
+ return -ENOMEM;
+
+ /* analog input subdevice */
+ s = dev->subdevices + 0;
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_OTHER | SDF_CMD_READ;
+ s->n_chan = 4;
+ s->len_chanlist = 4;
+ s->maxdata = 0xffff;
+ s->range_table = &range_a2150;
+ s->do_cmd = a2150_ai_cmd;
+ s->do_cmdtest = a2150_ai_cmdtest;
+ s->insn_read = a2150_ai_rinsn;
+ s->cancel = a2150_cancel;
+
+ /* need to do this for software counting of completed conversions, to
+ * prevent hardware count from stopping aquisition */
+ outw(HW_COUNT_DISABLE, dev->iobase + I8253_MODE_REG);
+
+ // set card's irq and dma levels
+ outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG);
+
+ // reset and sync adc clock circuitry
+ outw_p(DPD_BIT | APD_BIT, dev->iobase + CONFIG_REG);
+ outw_p(DPD_BIT, dev->iobase + CONFIG_REG);
+ // initialize configuration register
+ devpriv->config_bits = 0;
+ outw(devpriv->config_bits, dev->iobase + CONFIG_REG);
+ // wait until offset calibration is done, then enable analog inputs
+ for (i = 0; i < timeout; i++) {
+ if ((DCAL_BIT & inw(dev->iobase + STATUS_REG)) == 0)
+ break;
+ comedi_udelay(1000);
+ }
+ if (i == timeout) {
+ printk(" timed out waiting for offset calibration to complete\n");
+ return -ETIME;
+ }
+ devpriv->config_bits |= ENABLE0_BIT | ENABLE1_BIT;
+ outw(devpriv->config_bits, dev->iobase + CONFIG_REG);
+
+ return 0;
+};
+
+static int a2150_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: %s: remove\n", dev->minor, driver_a2150.driver_name);
+
+ /* only free stuff if it has been allocated by _attach */
+ if (dev->iobase) {
+ // put board in power-down mode
+ outw(APD_BIT | DPD_BIT, dev->iobase + CONFIG_REG);
+ release_region(dev->iobase, A2150_SIZE);
+ }
+
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+ if (devpriv) {
+ if (devpriv->dma)
+ free_dma(devpriv->dma);
+ if (devpriv->dma_buffer)
+ kfree(devpriv->dma_buffer);
+ }
+
+ return 0;
+};
+
+static int a2150_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ // disable dma on card
+ devpriv->irq_dma_bits &= ~DMA_INTR_EN_BIT & ~DMA_EN_BIT;
+ outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG);
+
+ // disable computer's dma
+ disable_dma(devpriv->dma);
+
+ // clear fifo and reset triggering circuitry
+ outw(0, dev->iobase + FIFO_RESET_REG);
+
+ return 0;
+}
+
+static int a2150_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+ int startChan;
+ int i;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_EXT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < thisboard->ai_speed) {
+ cmd->convert_arg = thisboard->ai_speed;
+ err++;
+ }
+ }
+ if (!cmd->chanlist_len) {
+ cmd->chanlist_len = 1;
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (!cmd->stop_arg) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ } else { /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ a2150_get_timing(dev, &cmd->scan_begin_arg, cmd->flags);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ // check channel/gain list against card's limitations
+ if (cmd->chanlist) {
+ startChan = CR_CHAN(cmd->chanlist[0]);
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ if (CR_CHAN(cmd->chanlist[i]) != (startChan + i)) {
+ comedi_error(dev,
+ "entries in chanlist must be consecutive channels, counting upwards\n");
+ err++;
+ }
+ }
+ if (cmd->chanlist_len == 2 && CR_CHAN(cmd->chanlist[0]) == 1) {
+ comedi_error(dev,
+ "length 2 chanlist must be channels 0,1 or channels 2,3");
+ err++;
+ }
+ if (cmd->chanlist_len == 3) {
+ comedi_error(dev,
+ "chanlist must have 1,2 or 4 channels");
+ err++;
+ }
+ if (CR_AREF(cmd->chanlist[0]) != CR_AREF(cmd->chanlist[1]) ||
+ CR_AREF(cmd->chanlist[2]) != CR_AREF(cmd->chanlist[3]))
+ {
+ comedi_error(dev,
+ "channels 0/1 and 2/3 must have the same analog reference");
+ err++;
+ }
+ }
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int a2150_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned long lock_flags;
+ unsigned int old_config_bits = devpriv->config_bits;
+ unsigned int trigger_bits;
+
+ if (!dev->irq || !devpriv->dma) {
+ comedi_error(dev,
+ " irq and dma required, cannot do hardware conversions");
+ return -1;
+ }
+ if (cmd->flags & TRIG_RT) {
+ comedi_error(dev,
+ " dma incompatible with hard real-time interrupt (TRIG_RT), aborting");
+ return -1;
+ }
+ // clear fifo and reset triggering circuitry
+ outw(0, dev->iobase + FIFO_RESET_REG);
+
+ /* setup chanlist */
+ if (a2150_set_chanlist(dev, CR_CHAN(cmd->chanlist[0]),
+ cmd->chanlist_len) < 0)
+ return -1;
+
+ // setup ac/dc coupling
+ if (CR_AREF(cmd->chanlist[0]) == AREF_OTHER)
+ devpriv->config_bits |= AC0_BIT;
+ else
+ devpriv->config_bits &= ~AC0_BIT;
+ if (CR_AREF(cmd->chanlist[2]) == AREF_OTHER)
+ devpriv->config_bits |= AC1_BIT;
+ else
+ devpriv->config_bits &= ~AC1_BIT;
+
+ // setup timing
+ a2150_get_timing(dev, &cmd->scan_begin_arg, cmd->flags);
+
+ // send timing, channel, config bits
+ outw(devpriv->config_bits, dev->iobase + CONFIG_REG);
+
+ // initialize number of samples remaining
+ devpriv->count = cmd->stop_arg * cmd->chanlist_len;
+
+ // enable computer's dma
+ lock_flags = claim_dma_lock();
+ disable_dma(devpriv->dma);
+ /* clear flip-flop to make sure 2-byte registers for
+ * count and address get set correctly */
+ clear_dma_ff(devpriv->dma);
+ set_dma_addr(devpriv->dma, virt_to_bus(devpriv->dma_buffer));
+ // set size of transfer to fill in 1/3 second
+#define ONE_THIRD_SECOND 333333333
+ devpriv->dma_transfer_size =
+ sizeof(devpriv->dma_buffer[0]) * cmd->chanlist_len *
+ ONE_THIRD_SECOND / cmd->scan_begin_arg;
+ if (devpriv->dma_transfer_size > A2150_DMA_BUFFER_SIZE)
+ devpriv->dma_transfer_size = A2150_DMA_BUFFER_SIZE;
+ if (devpriv->dma_transfer_size < sizeof(devpriv->dma_buffer[0]))
+ devpriv->dma_transfer_size = sizeof(devpriv->dma_buffer[0]);
+ devpriv->dma_transfer_size -=
+ devpriv->dma_transfer_size % sizeof(devpriv->dma_buffer[0]);
+ set_dma_count(devpriv->dma, devpriv->dma_transfer_size);
+ enable_dma(devpriv->dma);
+ release_dma_lock(lock_flags);
+
+ /* clear dma interrupt before enabling it, to try and get rid of that
+ * one spurious interrupt that has been happening */
+ outw(0x00, dev->iobase + DMA_TC_CLEAR_REG);
+
+ // enable dma on card
+ devpriv->irq_dma_bits |= DMA_INTR_EN_BIT | DMA_EN_BIT;
+ outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG);
+
+ // may need to wait 72 sampling periods if timing was changed
+ i8254_load(dev->iobase + I8253_BASE_REG, 0, 2, 72, 0);
+
+ // setup start triggering
+ trigger_bits = 0;
+ // decide if we need to wait 72 periods for valid data
+ if (cmd->start_src == TRIG_NOW &&
+ (old_config_bits & CLOCK_MASK) !=
+ (devpriv->config_bits & CLOCK_MASK)) {
+ // set trigger source to delay trigger
+ trigger_bits |= DELAY_TRIGGER_BITS;
+ } else {
+ // otherwise no delay
+ trigger_bits |= POST_TRIGGER_BITS;
+ }
+ // enable external hardware trigger
+ if (cmd->start_src == TRIG_EXT) {
+ trigger_bits |= HW_TRIG_EN;
+ } else if (cmd->start_src == TRIG_OTHER) {
+ // XXX add support for level/slope start trigger using TRIG_OTHER
+ comedi_error(dev, "you shouldn't see this?");
+ }
+ // send trigger config bits
+ outw(trigger_bits, dev->iobase + TRIGGER_REG);
+
+ // start aquisition for soft trigger
+ if (cmd->start_src == TRIG_NOW) {
+ outw(0, dev->iobase + FIFO_START_REG);
+ }
+#ifdef A2150_DEBUG
+ ni_dump_regs(dev);
+#endif
+
+ return 0;
+}
+
+static int a2150_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int i, n;
+ static const int timeout = 100000;
+ static const int filter_delay = 36;
+
+ // clear fifo and reset triggering circuitry
+ outw(0, dev->iobase + FIFO_RESET_REG);
+
+ /* setup chanlist */
+ if (a2150_set_chanlist(dev, CR_CHAN(insn->chanspec), 1) < 0)
+ return -1;
+
+ // set dc coupling
+ devpriv->config_bits &= ~AC0_BIT;
+ devpriv->config_bits &= ~AC1_BIT;
+
+ // send timing, channel, config bits
+ outw(devpriv->config_bits, dev->iobase + CONFIG_REG);
+
+ // disable dma on card
+ devpriv->irq_dma_bits &= ~DMA_INTR_EN_BIT & ~DMA_EN_BIT;
+ outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG);
+
+ // setup start triggering
+ outw(0, dev->iobase + TRIGGER_REG);
+
+ // start aquisition for soft trigger
+ outw(0, dev->iobase + FIFO_START_REG);
+
+ /* there is a 35.6 sample delay for data to get through the antialias filter */
+ for (n = 0; n < filter_delay; n++) {
+ for (i = 0; i < timeout; i++) {
+ if (inw(dev->iobase + STATUS_REG) & FNE_BIT)
+ break;
+ comedi_udelay(1);
+ }
+ if (i == timeout) {
+ comedi_error(dev, "timeout");
+ return -ETIME;
+ }
+ inw(dev->iobase + FIFO_DATA_REG);
+ }
+
+ // read data
+ for (n = 0; n < insn->n; n++) {
+ for (i = 0; i < timeout; i++) {
+ if (inw(dev->iobase + STATUS_REG) & FNE_BIT)
+ break;
+ comedi_udelay(1);
+ }
+ if (i == timeout) {
+ comedi_error(dev, "timeout");
+ return -ETIME;
+ }
+#ifdef A2150_DEBUG
+ ni_dump_regs(dev);
+#endif
+ data[n] = inw(dev->iobase + FIFO_DATA_REG);
+#ifdef A2150_DEBUG
+ rt_printk(" data is %i\n", data[n]);
+#endif
+ data[n] ^= 0x8000;
+ }
+
+ // clear fifo and reset triggering circuitry
+ outw(0, dev->iobase + FIFO_RESET_REG);
+
+ return n;
+}
+
+/* sets bits in devpriv->clock_bits to nearest approximation of requested period,
+ * adjusts requested period to actual timing. */
+static int a2150_get_timing(struct comedi_device * dev, unsigned int *period,
+ int flags)
+{
+ int lub, glb, temp;
+ int lub_divisor_shift, lub_index, glb_divisor_shift, glb_index;
+ int i, j;
+
+ // initialize greatest lower and least upper bounds
+ lub_divisor_shift = 3;
+ lub_index = 0;
+ lub = thisboard->clock[lub_index] * (1 << lub_divisor_shift);
+ glb_divisor_shift = 0;
+ glb_index = thisboard->num_clocks - 1;
+ glb = thisboard->clock[glb_index] * (1 << glb_divisor_shift);
+
+ // make sure period is in available range
+ if (*period < glb)
+ *period = glb;
+ if (*period > lub)
+ *period = lub;
+
+ // we can multiply period by 1, 2, 4, or 8, using (1 << i)
+ for (i = 0; i < 4; i++) {
+ // there are a maximum of 4 master clocks
+ for (j = 0; j < thisboard->num_clocks; j++) {
+ // temp is the period in nanosec we are evaluating
+ temp = thisboard->clock[j] * (1 << i);
+ // if it is the best match yet
+ if (temp < lub && temp >= *period) {
+ lub_divisor_shift = i;
+ lub_index = j;
+ lub = temp;
+ }
+ if (temp > glb && temp <= *period) {
+ glb_divisor_shift = i;
+ glb_index = j;
+ glb = temp;
+ }
+ }
+ }
+ flags &= TRIG_ROUND_MASK;
+ switch (flags) {
+ case TRIG_ROUND_NEAREST:
+ default:
+ // if least upper bound is better approximation
+ if (lub - *period < *period - glb) {
+ *period = lub;
+ } else {
+ *period = glb;
+ }
+ break;
+ case TRIG_ROUND_UP:
+ *period = lub;
+ break;
+ case TRIG_ROUND_DOWN:
+ *period = glb;
+ break;
+ }
+
+ // set clock bits for config register appropriately
+ devpriv->config_bits &= ~CLOCK_MASK;
+ if (*period == lub) {
+ devpriv->config_bits |=
+ CLOCK_SELECT_BITS(lub_index) |
+ CLOCK_DIVISOR_BITS(lub_divisor_shift);
+ } else {
+ devpriv->config_bits |=
+ CLOCK_SELECT_BITS(glb_index) |
+ CLOCK_DIVISOR_BITS(glb_divisor_shift);
+ }
+
+ return 0;
+}
+
+static int a2150_set_chanlist(struct comedi_device * dev, unsigned int start_channel,
+ unsigned int num_channels)
+{
+ if (start_channel + num_channels > 4)
+ return -1;
+
+ devpriv->config_bits &= ~CHANNEL_MASK;
+
+ switch (num_channels) {
+ case 1:
+ devpriv->config_bits |= CHANNEL_BITS(0x4 | start_channel);
+ break;
+ case 2:
+ if (start_channel == 0) {
+ devpriv->config_bits |= CHANNEL_BITS(0x2);
+ } else if (start_channel == 2) {
+ devpriv->config_bits |= CHANNEL_BITS(0x3);
+ } else {
+ return -1;
+ }
+ break;
+ case 4:
+ devpriv->config_bits |= CHANNEL_BITS(0x1);
+ break;
+ default:
+ return -1;
+ break;
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/ni_at_ao.c b/drivers/staging/comedi/drivers/ni_at_ao.c
new file mode 100644
index 000000000000..2cc46b672588
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_at_ao.c
@@ -0,0 +1,454 @@
+/*
+ comedi/drivers/ni_at_ao.c
+ Driver for NI AT-AO-6/10 boards
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000,2002 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: ni_at_ao
+Description: National Instruments AT-AO-6/10
+Devices: [National Instruments] AT-AO-6 (at-ao-6), AT-AO-10 (at-ao-10)
+Status: should work
+Author: ds
+Updated: Sun Dec 26 12:26:28 EST 2004
+
+Configuration options:
+ [0] - I/O port base address
+ [1] - IRQ (unused)
+ [2] - DMA (unused)
+ [3] - analog output range, set by jumpers on hardware (0 for -10 to 10V bipolar, 1 for 0V to 10V unipolar)
+
+*/
+/*
+ * Register-level programming information can be found in NI
+ * document 320379.pdf.
+ */
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+/* board egisters */
+/* registers with _2_ are accessed when GRP2WR is set in CFG1 */
+
+#define ATAO_SIZE 0x20
+
+#define ATAO_2_DMATCCLR 0x00 /* W 16 */
+#define ATAO_DIN 0x00 /* R 16 */
+#define ATAO_DOUT 0x00 /* W 16 */
+
+#define ATAO_CFG2 0x02 /* W 16 */
+#define CALLD1 0x8000
+#define CALLD0 0x4000
+#define FFRTEN 0x2000
+#define DAC2S8 0x1000
+#define DAC2S6 0x0800
+#define DAC2S4 0x0400
+#define DAC2S2 0x0200
+#define DAC2S0 0x0100
+#define LDAC8 0x0080
+#define LDAC6 0x0040
+#define LDAC4 0x0020
+#define LDAC2 0x0010
+#define LDAC0 0x0008
+#define PROMEN 0x0004
+#define SCLK 0x0002
+#define SDATA 0x0001
+
+#define ATAO_2_INT1CLR 0x02 /* W 16 */
+
+#define ATAO_CFG3 0x04 /* W 16 */
+#define DMAMODE 0x0040
+#define CLKOUT 0x0020
+#define RCLKEN 0x0010
+#define DOUTEN2 0x0008
+#define DOUTEN1 0x0004
+#define EN2_5V 0x0002
+#define SCANEN 0x0001
+
+#define ATAO_2_INT2CLR 0x04 /* W 16 */
+
+#define ATAO_82C53_BASE 0x06 /* RW 8 */
+
+#define ATAO_82C53_CNTR1 0x06 /* RW 8 */
+#define ATAO_82C53_CNTR2 0x07 /* RW 8 */
+#define ATAO_82C53_CNTR3 0x08 /* RW 8 */
+#define ATAO_82C53_CNTRCMD 0x09 /* W 8 */
+#define CNTRSEL1 0x80
+#define CNTRSEL0 0x40
+#define RWSEL1 0x20
+#define RWSEL0 0x10
+#define MODESEL2 0x08
+#define MODESEL1 0x04
+#define MODESEL0 0x02
+#define BCDSEL 0x01
+ /* read-back command */
+#define COUNT 0x20
+#define STATUS 0x10
+#define CNTR3 0x08
+#define CNTR2 0x04
+#define CNTR1 0x02
+ /* status */
+#define OUT 0x80
+#define _NULL 0x40
+#define RW1 0x20
+#define RW0 0x10
+#define MODE2 0x08
+#define MODE1 0x04
+#define MODE0 0x02
+#define BCD 0x01
+
+#define ATAO_2_RTSISHFT 0x06 /* W 8 */
+#define RSI 0x01
+
+#define ATAO_2_RTSISTRB 0x07 /* W 8 */
+
+#define ATAO_CFG1 0x0a /* W 16 */
+#define EXTINT2EN 0x8000
+#define EXTINT1EN 0x4000
+#define CNTINT2EN 0x2000
+#define CNTINT1EN 0x1000
+#define TCINTEN 0x0800
+#define CNT1SRC 0x0400
+#define CNT2SRC 0x0200
+#define FIFOEN 0x0100
+#define GRP2WR 0x0080
+#define EXTUPDEN 0x0040
+#define DMARQ 0x0020
+#define DMAEN 0x0010
+#define CH_mask 0x000f
+#define ATAO_STATUS 0x0a /* R 16 */
+#define FH 0x0040
+#define FE 0x0020
+#define FF 0x0010
+#define INT2 0x0008
+#define INT1 0x0004
+#define TCINT 0x0002
+#define PROMOUT 0x0001
+
+#define ATAO_FIFO_WRITE 0x0c /* W 16 */
+#define ATAO_FIFO_CLEAR 0x0c /* R 16 */
+#define ATAO_DACn(x) (0x0c + 2*(x)) /* W */
+
+/*
+ * Board descriptions for two imaginary boards. Describing the
+ * boards in this way is optional, and completely driver-dependent.
+ * Some drivers use arrays such as this, other do not.
+ */
+struct atao_board {
+ const char *name;
+ int n_ao_chans;
+};
+
+static const struct atao_board atao_boards[] = {
+ {
+ name: "ai-ao-6",
+ n_ao_chans:6,
+ },
+ {
+ name: "ai-ao-10",
+ n_ao_chans:10,
+ },
+};
+
+#define thisboard ((struct atao_board *)dev->board_ptr)
+
+struct atao_private {
+
+ unsigned short cfg1;
+ unsigned short cfg2;
+ unsigned short cfg3;
+
+ /* Used for AO readback */
+ unsigned int ao_readback[10];
+};
+
+#define devpriv ((struct atao_private *)dev->private)
+
+static int atao_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int atao_detach(struct comedi_device * dev);
+static struct comedi_driver driver_atao = {
+ driver_name:"ni_at_ao",
+ module:THIS_MODULE,
+ attach:atao_attach,
+ detach:atao_detach,
+ board_name:&atao_boards[0].name,
+ offset:sizeof(struct atao_board),
+ num_names:sizeof(atao_boards) / sizeof(struct atao_board),
+};
+
+COMEDI_INITCLEANUP(driver_atao);
+
+static void atao_reset(struct comedi_device * dev);
+
+static int atao_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int atao_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int atao_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int atao_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int atao_calib_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int atao_calib_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int atao_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+ int ao_unipolar;
+
+ iobase = it->options[0];
+ if (iobase == 0)
+ iobase = 0x1c0;
+ ao_unipolar = it->options[3];
+
+ printk("comedi%d: ni_at_ao: 0x%04lx", dev->minor, iobase);
+
+ if (!request_region(iobase, ATAO_SIZE, "ni_at_ao")) {
+ printk(" I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ //dev->board_ptr = atao_probe(dev);
+
+ dev->board_name = thisboard->name;
+
+ if (alloc_private(dev, sizeof(struct atao_private)) < 0)
+ return -ENOMEM;
+
+ if (alloc_subdevices(dev, 4) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = thisboard->n_ao_chans;
+ s->maxdata = (1 << 12) - 1;
+ if (ao_unipolar)
+ s->range_table = &range_unipolar10;
+ else
+ s->range_table = &range_bipolar10;
+ s->insn_write = &atao_ao_winsn;
+ s->insn_read = &atao_ao_rinsn;
+
+ s = dev->subdevices + 1;
+ /* digital i/o subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = atao_dio_insn_bits;
+ s->insn_config = atao_dio_insn_config;
+
+ s = dev->subdevices + 2;
+ /* caldac subdevice */
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = 21;
+ s->maxdata = 0xff;
+ s->insn_read = atao_calib_insn_read;
+ s->insn_write = atao_calib_insn_write;
+
+ s = dev->subdevices + 3;
+ /* eeprom subdevice */
+ //s->type=COMEDI_SUBD_EEPROM;
+ s->type = COMEDI_SUBD_UNUSED;
+
+ atao_reset(dev);
+
+ printk("\n");
+
+ return 0;
+}
+
+static int atao_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: atao: remove\n", dev->minor);
+
+ if (dev->iobase)
+ release_region(dev->iobase, ATAO_SIZE);
+
+ return 0;
+}
+
+static void atao_reset(struct comedi_device * dev)
+{
+ /* This is the reset sequence described in the manual */
+
+ devpriv->cfg1 = 0;
+ outw(devpriv->cfg1, dev->iobase + ATAO_CFG1);
+
+ outb(RWSEL0 | MODESEL2, dev->iobase + ATAO_82C53_CNTRCMD);
+ outb(0x03, dev->iobase + ATAO_82C53_CNTR1);
+ outb(CNTRSEL0 | RWSEL0 | MODESEL2, dev->iobase + ATAO_82C53_CNTRCMD);
+
+ devpriv->cfg2 = 0;
+ outw(devpriv->cfg2, dev->iobase + ATAO_CFG2);
+
+ devpriv->cfg3 = 0;
+ outw(devpriv->cfg3, dev->iobase + ATAO_CFG3);
+
+ inw(dev->iobase + ATAO_FIFO_CLEAR);
+
+ devpriv->cfg1 |= GRP2WR;
+ outw(devpriv->cfg1, dev->iobase + ATAO_CFG1);
+
+ outw(0, dev->iobase + ATAO_2_INT1CLR);
+ outw(0, dev->iobase + ATAO_2_INT2CLR);
+ outw(0, dev->iobase + ATAO_2_DMATCCLR);
+
+ devpriv->cfg1 &= ~GRP2WR;
+ outw(devpriv->cfg1, dev->iobase + ATAO_CFG1);
+}
+
+static int atao_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+ short bits;
+
+ for (i = 0; i < insn->n; i++) {
+ bits = data[i] - 0x800;
+ if (chan == 0) {
+ devpriv->cfg1 |= GRP2WR;
+ outw(devpriv->cfg1, dev->iobase + ATAO_CFG1);
+ }
+ outw(bits, dev->iobase + ATAO_DACn(chan));
+ if (chan == 0) {
+ devpriv->cfg1 &= ~GRP2WR;
+ outw(devpriv->cfg1, dev->iobase + ATAO_CFG1);
+ }
+ devpriv->ao_readback[chan] = data[i];
+ }
+
+ return i;
+}
+
+static int atao_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = devpriv->ao_readback[chan];
+
+ return i;
+}
+
+static int atao_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+ outw(s->state, dev->iobase + ATAO_DOUT);
+ }
+
+ data[1] = inw(dev->iobase + ATAO_DIN);
+
+ return 2;
+}
+
+static int atao_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask, bit;
+
+ /* The input or output configuration of each digital line is
+ * configured by a special insn_config instruction. chanspec
+ * contains the channel to be changed, and data[0] contains the
+ * value COMEDI_INPUT or COMEDI_OUTPUT. */
+
+ mask = (chan < 4) ? 0x0f : 0xf0;
+ bit = (chan < 4) ? DOUTEN1 : DOUTEN2;
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= mask;
+ devpriv->cfg3 |= bit;
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~mask;
+ devpriv->cfg3 &= ~bit;
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (s->
+ io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ outw(devpriv->cfg3, dev->iobase + ATAO_CFG3);
+
+ return 1;
+}
+
+/*
+ * Figure 2-1 in the manual shows 3 chips labeled DAC8800, which
+ * are 8-channel 8-bit DACs. These are most likely the calibration
+ * DACs. It is not explicitly stated in the manual how to access
+ * the caldacs, but we can guess.
+ */
+static int atao_calib_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ for (i = 0; i < insn->n; i++) {
+ data[i] = 0; /* XXX */
+ }
+ return insn->n;
+}
+
+static int atao_calib_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int bitstring, bit;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ bitstring = ((chan & 0x7) << 8) | (data[insn->n - 1] & 0xff);
+
+ for (bit = 1 << (11 - 1); bit; bit >>= 1) {
+ outw(devpriv->cfg2 | ((bit & bitstring) ? SDATA : 0),
+ dev->iobase + ATAO_CFG2);
+ outw(devpriv->cfg2 | SCLK | ((bit & bitstring) ? SDATA : 0),
+ dev->iobase + ATAO_CFG2);
+ }
+ /* strobe the appropriate caldac */
+ outw(devpriv->cfg2 | (((chan >> 3) + 1) << 14),
+ dev->iobase + ATAO_CFG2);
+ outw(devpriv->cfg2, dev->iobase + ATAO_CFG2);
+
+ return insn->n;
+}
diff --git a/drivers/staging/comedi/drivers/ni_atmio.c b/drivers/staging/comedi/drivers/ni_atmio.c
new file mode 100644
index 000000000000..fcc38535afc5
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_atmio.c
@@ -0,0 +1,513 @@
+/*
+ comedi/drivers/ni_atmio.c
+ Hardware driver for NI AT-MIO E series cards
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2001 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+Driver: ni_atmio
+Description: National Instruments AT-MIO-E series
+Author: ds
+Devices: [National Instruments] AT-MIO-16E-1 (ni_atmio),
+ AT-MIO-16E-2, AT-MIO-16E-10, AT-MIO-16DE-10, AT-MIO-64E-3,
+ AT-MIO-16XE-50, AT-MIO-16XE-10, AT-AI-16XE-10
+Status: works
+Updated: Thu May 1 20:03:02 CDT 2003
+
+The driver has 2.6 kernel isapnp support, and
+will automatically probe for a supported board if the
+I/O base is left unspecified with comedi_config.
+However, many of
+the isapnp id numbers are unknown. If your board is not
+recognized, please send the output of 'cat /proc/isapnp'
+(you may need to modprobe the isa-pnp module for
+/proc/isapnp to exist) so the
+id numbers for your board can be added to the driver.
+
+Otherwise, you can use the isapnptools package to configure
+your board. Use isapnp to
+configure the I/O base and IRQ for the board, and then pass
+the same values as
+parameters in comedi_config. A sample isapnp.conf file is included
+in the etc/ directory of Comedilib.
+
+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.
+
+To use the extended digital I/O on some of the boards, enable the
+8255 driver when configuring the Comedi source tree.
+
+External triggering is supported for some events. The channel index
+(scan_begin_arg, etc.) maps to PFI0 - PFI9.
+
+Some of the more esoteric triggering possibilities of these boards
+are not supported.
+*/
+/*
+ The real guts of the driver is in ni_mio_common.c, which is included
+ both here and in ni_pcimio.c
+
+ Interrupt support added by Truxton Fulton <trux@truxton.com>
+
+ References for specifications:
+
+ 340747b.pdf Register Level Programmer Manual (obsolete)
+ 340747c.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 "../comedidev.h"
+
+#include <linux/delay.h>
+#include <linux/isapnp.h>
+
+#include "ni_stc.h"
+#include "8255.h"
+
+#undef DEBUG
+
+#define ATMIO 1
+#undef PCIMIO
+
+/*
+ * AT specific setup
+ */
+
+#define NI_SIZE 0x20
+
+#define MAX_N_CALDACS 32
+
+static const ni_board ni_boards[] = {
+ {device_id:44,
+ isapnp_id:0x0000,/* XXX unknown */
+ name: "at-mio-16e-1",
+ n_adchan:16,
+ adbits: 12,
+ ai_fifo_depth:8192,
+ alwaysdither:0,
+ gainlkup:ai_gain_16,
+ ai_speed:800,
+ n_aochan:2,
+ aobits: 12,
+ ao_fifo_depth:2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ ao_unipolar:1,
+ ao_speed:1000,
+ has_8255:0,
+ .num_p0_dio_channels = 8,
+ caldac: {mb88341},
+ },
+ {device_id:25,
+ isapnp_id:0x1900,
+ name: "at-mio-16e-2",
+ n_adchan:16,
+ adbits: 12,
+ ai_fifo_depth:2048,
+ alwaysdither:0,
+ gainlkup:ai_gain_16,
+ ai_speed:2000,
+ n_aochan:2,
+ aobits: 12,
+ ao_fifo_depth:2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ ao_unipolar:1,
+ ao_speed:1000,
+ has_8255:0,
+ .num_p0_dio_channels = 8,
+ caldac: {mb88341},
+ },
+ {device_id:36,
+ isapnp_id:0x2400,
+ name: "at-mio-16e-10",
+ n_adchan:16,
+ adbits: 12,
+ ai_fifo_depth:512,
+ alwaysdither:0,
+ gainlkup:ai_gain_16,
+ ai_speed:10000,
+ n_aochan:2,
+ aobits: 12,
+ ao_fifo_depth:0,
+ .ao_range_table = &range_ni_E_ao_ext,
+ ao_unipolar:1,
+ ao_speed:10000,
+ .num_p0_dio_channels = 8,
+ caldac: {ad8804_debug},
+ has_8255:0,
+ },
+ {device_id:37,
+ isapnp_id:0x2500,
+ name: "at-mio-16de-10",
+ n_adchan:16,
+ adbits: 12,
+ ai_fifo_depth:512,
+ alwaysdither:0,
+ gainlkup:ai_gain_16,
+ ai_speed:10000,
+ n_aochan:2,
+ aobits: 12,
+ ao_fifo_depth:0,
+ .ao_range_table = &range_ni_E_ao_ext,
+ ao_unipolar:1,
+ ao_speed:10000,
+ .num_p0_dio_channels = 8,
+ caldac: {ad8804_debug},
+ has_8255:1,
+ },
+ {device_id:38,
+ isapnp_id:0x2600,
+ name: "at-mio-64e-3",
+ n_adchan:64,
+ adbits: 12,
+ ai_fifo_depth:2048,
+ alwaysdither:0,
+ gainlkup:ai_gain_16,
+ ai_speed:2000,
+ n_aochan:2,
+ aobits: 12,
+ ao_fifo_depth:2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ ao_unipolar:1,
+ ao_speed:1000,
+ has_8255:0,
+ .num_p0_dio_channels = 8,
+ caldac: {ad8804_debug},
+ },
+ {device_id:39,
+ isapnp_id:0x2700,
+ name: "at-mio-16xe-50",
+ n_adchan:16,
+ adbits: 16,
+ ai_fifo_depth:512,
+ alwaysdither:1,
+ gainlkup:ai_gain_8,
+ ai_speed:50000,
+ n_aochan:2,
+ aobits: 12,
+ ao_fifo_depth:0,
+ .ao_range_table = &range_bipolar10,
+ ao_unipolar:0,
+ ao_speed:50000,
+ .num_p0_dio_channels = 8,
+ caldac: {dac8800, dac8043},
+ has_8255:0,
+ },
+ {device_id:50,
+ isapnp_id:0x0000,/* XXX unknown */
+ name: "at-mio-16xe-10",
+ n_adchan:16,
+ adbits: 16,
+ ai_fifo_depth:512,
+ alwaysdither:1,
+ gainlkup:ai_gain_14,
+ ai_speed:10000,
+ n_aochan:2,
+ aobits: 16,
+ ao_fifo_depth:2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ ao_unipolar:1,
+ ao_speed:1000,
+ .num_p0_dio_channels = 8,
+ caldac: {dac8800, dac8043, ad8522},
+ has_8255:0,
+ },
+ {device_id:51,
+ isapnp_id:0x0000,/* XXX unknown */
+ name: "at-ai-16xe-10",
+ n_adchan:16,
+ adbits: 16,
+ ai_fifo_depth:512,
+ alwaysdither:1, /* unknown */
+ gainlkup:ai_gain_14,
+ ai_speed:10000,
+ n_aochan:0,
+ aobits: 0,
+ ao_fifo_depth:0,
+ ao_unipolar:0,
+ .num_p0_dio_channels = 8,
+ caldac: {dac8800, dac8043, ad8522},
+ has_8255:0,
+ }
+};
+
+static const 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
+
+#define NI_E_IRQ_FLAGS 0
+
+typedef struct {
+ struct pnp_dev *isapnp_dev;
+ NI_PRIVATE_COMMON} ni_private;
+#define devpriv ((ni_private *)dev->private)
+
+/* How we access registers */
+
+#define ni_writel(a,b) (outl((a),(b)+dev->iobase))
+#define ni_readl(a) (inl((a)+dev->iobase))
+#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))
+
+/* How we access windowed registers */
+
+/* We automatically take advantage of STC registers that can be
+ * read/written directly in the I/O space of the board. The
+ * AT-MIO devices map the low 8 STC registers to iobase+addr*2. */
+
+static void ni_atmio_win_out(struct comedi_device * dev, uint16_t data, int addr)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->window_lock, flags);
+ if ((addr) < 8) {
+ ni_writew(data, addr * 2);
+ } else {
+ ni_writew(addr, Window_Address);
+ ni_writew(data, Window_Data);
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->window_lock, flags);
+}
+
+static uint16_t ni_atmio_win_in(struct comedi_device * dev, int addr)
+{
+ unsigned long flags;
+ uint16_t ret;
+
+ comedi_spin_lock_irqsave(&devpriv->window_lock, flags);
+ if (addr < 8) {
+ ret = ni_readw(addr * 2);
+ } else {
+ ni_writew(addr, Window_Address);
+ ret = ni_readw(Window_Data);
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->window_lock, flags);
+
+ return ret;
+}
+
+static struct pnp_device_id device_ids[] = {
+ {.id = "NIC1900",.driver_data = 0},
+ {.id = "NIC2400",.driver_data = 0},
+ {.id = "NIC2500",.driver_data = 0},
+ {.id = "NIC2600",.driver_data = 0},
+ {.id = "NIC2700",.driver_data = 0},
+ {.id = ""}
+};
+
+MODULE_DEVICE_TABLE(pnp, device_ids);
+
+static int ni_atmio_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int ni_atmio_detach(struct comedi_device * dev);
+static struct comedi_driver driver_atmio = {
+ driver_name:"ni_atmio",
+ module:THIS_MODULE,
+ attach:ni_atmio_attach,
+ detach:ni_atmio_detach,
+};
+
+COMEDI_INITCLEANUP(driver_atmio);
+
+#include "ni_mio_common.c"
+
+static int ni_getboardtype(struct comedi_device * dev);
+
+/* clean up allocated resources */
+static int ni_atmio_detach(struct comedi_device * dev)
+{
+ mio_common_detach(dev);
+
+ if (dev->iobase)
+ release_region(dev->iobase, NI_SIZE);
+ if (dev->irq) {
+ comedi_free_irq(dev->irq, dev);
+ }
+ if (devpriv->isapnp_dev)
+ pnp_device_detach(devpriv->isapnp_dev);
+
+ return 0;
+}
+
+static int ni_isapnp_find_board(struct pnp_dev **dev)
+{
+ struct pnp_dev *isapnp_dev = NULL;
+ int i;
+
+ for (i = 0; i < n_ni_boards; i++) {
+ isapnp_dev = pnp_find_dev(NULL,
+ ISAPNP_VENDOR('N', 'I', 'C'),
+ ISAPNP_FUNCTION(ni_boards[i].isapnp_id), NULL);
+
+ if (isapnp_dev == NULL || isapnp_dev->card == NULL)
+ continue;
+
+ if (pnp_device_attach(isapnp_dev) < 0) {
+ printk("ni_atmio: %s found but already active, skipping.\n", ni_boards[i].name);
+ continue;
+ }
+ if (pnp_activate_dev(isapnp_dev) < 0) {
+ pnp_device_detach(isapnp_dev);
+ return -EAGAIN;
+ }
+ if (!pnp_port_valid(isapnp_dev, 0)
+ || !pnp_irq_valid(isapnp_dev, 0)) {
+ pnp_device_detach(isapnp_dev);
+ printk("ni_atmio: pnp invalid port or irq, aborting\n");
+ return -ENOMEM;
+ }
+ break;
+ }
+ if (i == n_ni_boards)
+ return -ENODEV;
+ *dev = isapnp_dev;
+ return 0;
+}
+
+static int ni_atmio_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct pnp_dev *isapnp_dev;
+ int ret;
+ unsigned long iobase;
+ int board;
+ unsigned int irq;
+
+ /* allocate private area */
+ if ((ret = ni_alloc_private(dev)) < 0)
+ return ret;
+ devpriv->stc_writew = &ni_atmio_win_out;
+ devpriv->stc_readw = &ni_atmio_win_in;
+ devpriv->stc_writel = &win_out2;
+ devpriv->stc_readl = &win_in2;
+
+ iobase = it->options[0];
+ irq = it->options[1];
+ isapnp_dev = NULL;
+ if (iobase == 0) {
+ ret = ni_isapnp_find_board(&isapnp_dev);
+ if (ret < 0)
+ return ret;
+
+ iobase = pnp_port_start(isapnp_dev, 0);
+ irq = pnp_irq(isapnp_dev, 0);
+ devpriv->isapnp_dev = isapnp_dev;
+ }
+
+ /* reserve our I/O region */
+
+ printk("comedi%d: ni_atmio: 0x%04lx", dev->minor, iobase);
+ if (!request_region(iobase, NI_SIZE, "ni_atmio")) {
+ printk(" I/O port conflict\n");
+ return -EIO;
+ }
+
+ dev->iobase = iobase;
+
+#ifdef DEBUG
+ /* board existence sanity check */
+ {
+ 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;
+
+ dev->board_ptr = ni_boards + board;
+
+ printk(" %s", boardtype.name);
+ dev->board_name = boardtype.name;
+
+ /* irq stuff */
+
+ if (irq != 0) {
+ if (irq > 15 || ni_irqpin[irq] == -1) {
+ printk(" invalid irq %u\n", irq);
+ return -EINVAL;
+ }
+ printk(" ( irq = %u )", irq);
+ if ((ret = comedi_request_irq(irq, ni_E_interrupt,
+ NI_E_IRQ_FLAGS, "ni_atmio", dev)) < 0) {
+ printk(" irq not available\n");
+ return -EINVAL;
+ }
+ dev->irq = irq;
+ }
+
+ /* generic E series stuff in ni_mio_common.c */
+
+ if ((ret = ni_E_init(dev, it)) < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ni_getboardtype(struct comedi_device * dev)
+{
+ int device_id = ni_read_eeprom(dev, 511);
+ int i;
+
+ for (i = 0; i < n_ni_boards; i++) {
+ if (ni_boards[i].device_id == device_id) {
+ return i;
+ }
+ }
+ if (device_id == 255) {
+ printk(" can't find board\n");
+ } else if (device_id == 0) {
+ printk(" EEPROM read error (?) or device not found\n");
+ } else {
+ printk(" unknown device ID %d -- contact author\n", device_id);
+ }
+ return -1;
+}
diff --git a/drivers/staging/comedi/drivers/ni_atmio16d.c b/drivers/staging/comedi/drivers/ni_atmio16d.c
new file mode 100644
index 000000000000..35fcd172af2e
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_atmio16d.c
@@ -0,0 +1,860 @@
+/*
+ comedi/drivers/ni_atmio16d.c
+ Hardware driver for National Instruments AT-MIO16D board
+ Copyright (C) 2000 Chris R. Baugher <baugher@enteract.com>
+
+ 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.
+
+ */
+/*
+Driver: ni_atmio16d
+Description: National Instruments AT-MIO-16D
+Author: Chris R. Baugher <baugher@enteract.com>
+Status: unknown
+Devices: [National Instruments] AT-MIO-16 (atmio16), AT-MIO-16D (atmio16d)
+*/
+/*
+ * I must give credit here to Michal Dobes <dobes@tesnet.cz> who
+ * wrote the driver for Advantec's pcl812 boards. I used the interrupt
+ * handling code from his driver as an example for this one.
+ *
+ * Chris Baugher
+ * 5/1/2000
+ *
+ */
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+#include "8255.h"
+
+/* Configuration and Status Registers */
+#define COM_REG_1 0x00 /* wo 16 */
+#define STAT_REG 0x00 /* ro 16 */
+#define COM_REG_2 0x02 /* wo 16 */
+/* Event Strobe Registers */
+#define START_CONVERT_REG 0x08 /* wo 16 */
+#define START_DAQ_REG 0x0A /* wo 16 */
+#define AD_CLEAR_REG 0x0C /* wo 16 */
+#define EXT_STROBE_REG 0x0E /* wo 16 */
+/* Analog Output Registers */
+#define DAC0_REG 0x10 /* wo 16 */
+#define DAC1_REG 0x12 /* wo 16 */
+#define INT2CLR_REG 0x14 /* wo 16 */
+/* Analog Input Registers */
+#define MUX_CNTR_REG 0x04 /* wo 16 */
+#define MUX_GAIN_REG 0x06 /* wo 16 */
+#define AD_FIFO_REG 0x16 /* ro 16 */
+#define DMA_TC_INT_CLR_REG 0x16 /* wo 16 */
+/* AM9513A Counter/Timer Registers */
+#define AM9513A_DATA_REG 0x18 /* rw 16 */
+#define AM9513A_COM_REG 0x1A /* wo 16 */
+#define AM9513A_STAT_REG 0x1A /* ro 16 */
+/* MIO-16 Digital I/O Registers */
+#define MIO_16_DIG_IN_REG 0x1C /* ro 16 */
+#define MIO_16_DIG_OUT_REG 0x1C /* wo 16 */
+/* RTSI Switch Registers */
+#define RTSI_SW_SHIFT_REG 0x1E /* wo 8 */
+#define RTSI_SW_STROBE_REG 0x1F /* wo 8 */
+/* DIO-24 Registers */
+#define DIO_24_PORTA_REG 0x00 /* rw 8 */
+#define DIO_24_PORTB_REG 0x01 /* rw 8 */
+#define DIO_24_PORTC_REG 0x02 /* rw 8 */
+#define DIO_24_CNFG_REG 0x03 /* wo 8 */
+
+/* Command Register bits */
+#define COMREG1_2SCADC 0x0001
+#define COMREG1_1632CNT 0x0002
+#define COMREG1_SCANEN 0x0008
+#define COMREG1_DAQEN 0x0010
+#define COMREG1_DMAEN 0x0020
+#define COMREG1_CONVINTEN 0x0080
+#define COMREG2_SCN2 0x0010
+#define COMREG2_INTEN 0x0080
+#define COMREG2_DOUTEN0 0x0100
+#define COMREG2_DOUTEN1 0x0200
+/* Status Register bits */
+#define STAT_AD_OVERRUN 0x0100
+#define STAT_AD_OVERFLOW 0x0200
+#define STAT_AD_DAQPROG 0x0800
+#define STAT_AD_CONVAVAIL 0x2000
+#define STAT_AD_DAQSTOPINT 0x4000
+/* AM9513A Counter/Timer defines */
+#define CLOCK_1_MHZ 0x8B25
+#define CLOCK_100_KHZ 0x8C25
+#define CLOCK_10_KHZ 0x8D25
+#define CLOCK_1_KHZ 0x8E25
+#define CLOCK_100_HZ 0x8F25
+/* Other miscellaneous defines */
+#define ATMIO16D_SIZE 32 /* bus address range */
+#define devpriv ((struct atmio16d_private *)dev->private)
+#define ATMIO16D_TIMEOUT 10
+
+struct atmio16_board_t {
+
+ const char *name;
+ int has_8255;
+};
+
+static const struct atmio16_board_t atmio16_boards[] = {
+ {
+ name: "atmio16",
+ has_8255:0,
+ },
+ {
+ name: "atmio16d",
+ has_8255:1,
+ },
+};
+
+#define n_atmio16_boards sizeof(atmio16_boards)/sizeof(atmio16_boards[0])
+
+#define boardtype ((const struct atmio16_board_t *)dev->board_ptr)
+
+/* function prototypes */
+static int atmio16d_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int atmio16d_detach(struct comedi_device * dev);
+static irqreturn_t atmio16d_interrupt(int irq, void *d PT_REGS_ARG);
+static int atmio16d_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int atmio16d_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int atmio16d_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static void reset_counters(struct comedi_device * dev);
+static void reset_atmio16d(struct comedi_device * dev);
+
+/* main driver struct */
+static struct comedi_driver driver_atmio16d = {
+ driver_name:"atmio16",
+ module:THIS_MODULE,
+ attach:atmio16d_attach,
+ detach:atmio16d_detach,
+ board_name:&atmio16_boards[0].name,
+ num_names:n_atmio16_boards,
+ offset:sizeof(struct atmio16_board_t),
+};
+
+COMEDI_INITCLEANUP(driver_atmio16d);
+
+/* range structs */
+static const struct comedi_lrange range_atmio16d_ai_10_bipolar = { 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.02)
+ }
+};
+
+static const struct comedi_lrange range_atmio16d_ai_5_bipolar = { 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.01)
+ }
+};
+
+static const struct comedi_lrange range_atmio16d_ai_unipolar = { 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.02)
+ }
+};
+
+/* private data struct */
+struct atmio16d_private {
+ enum { adc_diff, adc_singleended } adc_mux;
+ enum { adc_bipolar10, adc_bipolar5, adc_unipolar10 } adc_range;
+ enum { adc_2comp, adc_straight } adc_coding;
+ enum { dac_bipolar, dac_unipolar } dac0_range, dac1_range;
+ enum { dac_internal, dac_external } dac0_reference, dac1_reference;
+ enum { dac_2comp, dac_straight } dac0_coding, dac1_coding;
+ const struct comedi_lrange *ao_range_type_list[2];
+ unsigned int ao_readback[2];
+ unsigned int com_reg_1_state; /* current state of command register 1 */
+ unsigned int com_reg_2_state; /* current state of command register 2 */
+};
+
+static void reset_counters(struct comedi_device * dev)
+{
+ /* Counter 2 */
+ outw(0xFFC2, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF02, dev->iobase + AM9513A_COM_REG);
+ outw(0x4, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0A, dev->iobase + AM9513A_COM_REG);
+ outw(0x3, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF42, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF42, dev->iobase + AM9513A_COM_REG);
+ /* Counter 3 */
+ outw(0xFFC4, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF03, dev->iobase + AM9513A_COM_REG);
+ outw(0x4, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0B, dev->iobase + AM9513A_COM_REG);
+ outw(0x3, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF44, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF44, dev->iobase + AM9513A_COM_REG);
+ /* Counter 4 */
+ outw(0xFFC8, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF04, dev->iobase + AM9513A_COM_REG);
+ outw(0x4, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0C, dev->iobase + AM9513A_COM_REG);
+ outw(0x3, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF48, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF48, dev->iobase + AM9513A_COM_REG);
+ /* Counter 5 */
+ outw(0xFFD0, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF05, dev->iobase + AM9513A_COM_REG);
+ outw(0x4, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0D, dev->iobase + AM9513A_COM_REG);
+ outw(0x3, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF50, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF50, dev->iobase + AM9513A_COM_REG);
+
+ outw(0, dev->iobase + AD_CLEAR_REG);
+}
+
+static void reset_atmio16d(struct comedi_device * dev)
+{
+ int i;
+
+ /* now we need to initialize the board */
+ outw(0, dev->iobase + COM_REG_1);
+ outw(0, dev->iobase + COM_REG_2);
+ outw(0, dev->iobase + MUX_GAIN_REG);
+ /* init AM9513A timer */
+ outw(0xFFFF, dev->iobase + AM9513A_COM_REG);
+ outw(0xFFEF, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF17, dev->iobase + AM9513A_COM_REG);
+ outw(0xF000, dev->iobase + AM9513A_DATA_REG);
+ for (i = 1; i <= 5; ++i) {
+ outw(0xFF00 + i, dev->iobase + AM9513A_COM_REG);
+ outw(0x0004, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF08 + i, dev->iobase + AM9513A_COM_REG);
+ outw(0x3, dev->iobase + AM9513A_DATA_REG);
+ }
+ outw(0xFF5F, dev->iobase + AM9513A_COM_REG);
+ /* timer init done */
+ outw(0, dev->iobase + AD_CLEAR_REG);
+ outw(0, dev->iobase + INT2CLR_REG);
+ /* select straight binary mode for Analog Input */
+ devpriv->com_reg_1_state |= 1;
+ outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
+ devpriv->adc_coding = adc_straight;
+ /* zero the analog outputs */
+ outw(2048, dev->iobase + DAC0_REG);
+ outw(2048, dev->iobase + DAC1_REG);
+}
+
+static irqreturn_t atmio16d_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+
+// printk("atmio16d_interrupt!\n");
+
+ comedi_buf_put(s->async, inw(dev->iobase + AD_FIFO_REG));
+
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+}
+
+static int atmio16d_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0, tmp;
+#ifdef DEBUG1
+ printk("atmio16d_ai_cmdtest\n");
+#endif
+ /* make sure triggers are valid */
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+ /* note that mutual compatiblity is not an issue here */
+ if (cmd->scan_begin_src != TRIG_FOLLOW &&
+ cmd->scan_begin_src != TRIG_EXT &&
+ cmd->scan_begin_src != TRIG_TIMER)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ /* internal trigger */
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ } else {
+#if 0
+ /* external trigger */
+ /* should be level/edge, hi/lo specification here */
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+#endif
+ }
+
+ if (cmd->convert_arg < 10000) {
+ cmd->convert_arg = 10000;
+ err++;
+ }
+#if 0
+ if (cmd->convert_arg > SLOWEST_TIMER) {
+ cmd->convert_arg = SLOWEST_TIMER;
+ err++;
+ }
+#endif
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ /* any count is allowed */
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ return 0;
+}
+
+static int atmio16d_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int timer, base_clock;
+ unsigned int sample_count, tmp, chan, gain;
+ int i;
+#ifdef DEBUG1
+ printk("atmio16d_ai_cmd\n");
+#endif
+ /* This is slowly becoming a working command interface. *
+ * It is still uber-experimental */
+
+ reset_counters(dev);
+ s->async->cur_chan = 0;
+
+ /* check if scanning multiple channels */
+ if (cmd->chanlist_len < 2) {
+ devpriv->com_reg_1_state &= ~COMREG1_SCANEN;
+ outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
+ } else {
+ devpriv->com_reg_1_state |= COMREG1_SCANEN;
+ devpriv->com_reg_2_state |= COMREG2_SCN2;
+ outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
+ outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
+ }
+
+ /* Setup the Mux-Gain Counter */
+ for (i = 0; i < cmd->chanlist_len; ++i) {
+ chan = CR_CHAN(cmd->chanlist[i]);
+ gain = CR_RANGE(cmd->chanlist[i]);
+ outw(i, dev->iobase + MUX_CNTR_REG);
+ tmp = chan | (gain << 6);
+ if (i == cmd->scan_end_arg - 1)
+ tmp |= 0x0010; /* set LASTONE bit */
+ outw(tmp, dev->iobase + MUX_GAIN_REG);
+ }
+
+ /* Now program the sample interval timer */
+ /* Figure out which clock to use then get an
+ * appropriate timer value */
+ if (cmd->convert_arg < 65536000) {
+ base_clock = CLOCK_1_MHZ;
+ timer = cmd->convert_arg / 1000;
+ } else if (cmd->convert_arg < 655360000) {
+ base_clock = CLOCK_100_KHZ;
+ timer = cmd->convert_arg / 10000;
+ } else if (cmd->convert_arg <= 0xffffffff /* 6553600000 */ ) {
+ base_clock = CLOCK_10_KHZ;
+ timer = cmd->convert_arg / 100000;
+ } else if (cmd->convert_arg <= 0xffffffff /* 65536000000 */ ) {
+ base_clock = CLOCK_1_KHZ;
+ timer = cmd->convert_arg / 1000000;
+ }
+ outw(0xFF03, dev->iobase + AM9513A_COM_REG);
+ outw(base_clock, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0B, dev->iobase + AM9513A_COM_REG);
+ outw(0x2, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF44, dev->iobase + AM9513A_COM_REG);
+ outw(0xFFF3, dev->iobase + AM9513A_COM_REG);
+ outw(timer, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF24, dev->iobase + AM9513A_COM_REG);
+
+ /* Now figure out how many samples to get */
+ /* and program the sample counter */
+ sample_count = cmd->stop_arg * cmd->scan_end_arg;
+ outw(0xFF04, dev->iobase + AM9513A_COM_REG);
+ outw(0x1025, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0C, dev->iobase + AM9513A_COM_REG);
+ if (sample_count < 65536) {
+ /* use only Counter 4 */
+ outw(sample_count, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF48, dev->iobase + AM9513A_COM_REG);
+ outw(0xFFF4, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF28, dev->iobase + AM9513A_COM_REG);
+ devpriv->com_reg_1_state &= ~COMREG1_1632CNT;
+ outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
+ } else {
+ /* Counter 4 and 5 are needed */
+ if ((tmp = sample_count & 0xFFFF)) {
+ outw(tmp - 1, dev->iobase + AM9513A_DATA_REG);
+ } else {
+ outw(0xFFFF, dev->iobase + AM9513A_DATA_REG);
+ }
+ outw(0xFF48, dev->iobase + AM9513A_COM_REG);
+ outw(0, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF28, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF05, dev->iobase + AM9513A_COM_REG);
+ outw(0x25, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0D, dev->iobase + AM9513A_COM_REG);
+ tmp = sample_count & 0xFFFF;
+ if ((tmp == 0) || (tmp == 1)) {
+ outw((sample_count >> 16) & 0xFFFF,
+ dev->iobase + AM9513A_DATA_REG);
+ } else {
+ outw(((sample_count >> 16) & 0xFFFF) + 1,
+ dev->iobase + AM9513A_DATA_REG);
+ }
+ outw(0xFF70, dev->iobase + AM9513A_COM_REG);
+ devpriv->com_reg_1_state |= COMREG1_1632CNT;
+ outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
+ }
+
+ /* Program the scan interval timer ONLY IF SCANNING IS ENABLED */
+ /* Figure out which clock to use then get an
+ * appropriate timer value */
+ if (cmd->chanlist_len > 1) {
+ if (cmd->scan_begin_arg < 65536000) {
+ base_clock = CLOCK_1_MHZ;
+ timer = cmd->scan_begin_arg / 1000;
+ } else if (cmd->scan_begin_arg < 655360000) {
+ base_clock = CLOCK_100_KHZ;
+ timer = cmd->scan_begin_arg / 10000;
+ } else if (cmd->scan_begin_arg < 0xffffffff /* 6553600000 */ ) {
+ base_clock = CLOCK_10_KHZ;
+ timer = cmd->scan_begin_arg / 100000;
+ } else if (cmd->scan_begin_arg < 0xffffffff /* 65536000000 */ ) {
+ base_clock = CLOCK_1_KHZ;
+ timer = cmd->scan_begin_arg / 1000000;
+ }
+ outw(0xFF02, dev->iobase + AM9513A_COM_REG);
+ outw(base_clock, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0A, dev->iobase + AM9513A_COM_REG);
+ outw(0x2, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF42, dev->iobase + AM9513A_COM_REG);
+ outw(0xFFF2, dev->iobase + AM9513A_COM_REG);
+ outw(timer, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF22, dev->iobase + AM9513A_COM_REG);
+ }
+
+ /* Clear the A/D FIFO and reset the MUX counter */
+ outw(0, dev->iobase + AD_CLEAR_REG);
+ outw(0, dev->iobase + MUX_CNTR_REG);
+ outw(0, dev->iobase + INT2CLR_REG);
+ /* enable this acquisition operation */
+ devpriv->com_reg_1_state |= COMREG1_DAQEN;
+ outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
+ /* enable interrupts for conversion completion */
+ devpriv->com_reg_1_state |= COMREG1_CONVINTEN;
+ devpriv->com_reg_2_state |= COMREG2_INTEN;
+ outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
+ outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
+ /* apply a trigger. this starts the counters! */
+ outw(0, dev->iobase + START_DAQ_REG);
+
+ return 0;
+}
+
+/* This will cancel a running acquisition operation */
+static int atmio16d_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ reset_atmio16d(dev);
+
+ return 0;
+}
+
+/* Mode 0 is used to get a single conversion on demand */
+static int atmio16d_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i, t;
+ int chan;
+ int gain;
+ int status;
+
+#ifdef DEBUG1
+ printk("atmio16d_ai_insn_read\n");
+#endif
+ chan = CR_CHAN(insn->chanspec);
+ gain = CR_RANGE(insn->chanspec);
+
+ /* reset the Analog input circuitry */
+ //outw( 0, dev->iobase+AD_CLEAR_REG );
+ /* reset the Analog Input MUX Counter to 0 */
+ //outw( 0, dev->iobase+MUX_CNTR_REG );
+
+ /* set the Input MUX gain */
+ outw(chan | (gain << 6), dev->iobase + MUX_GAIN_REG);
+
+ for (i = 0; i < insn->n; i++) {
+ /* start the conversion */
+ outw(0, dev->iobase + START_CONVERT_REG);
+ /* wait for it to finish */
+ for (t = 0; t < ATMIO16D_TIMEOUT; t++) {
+ /* check conversion status */
+ status = inw(dev->iobase + STAT_REG);
+#ifdef DEBUG1
+ printk("status=%x\n", status);
+#endif
+ if (status & STAT_AD_CONVAVAIL) {
+ /* read the data now */
+ data[i] = inw(dev->iobase + AD_FIFO_REG);
+ /* change to two's complement if need be */
+ if (devpriv->adc_coding == adc_2comp) {
+ data[i] ^= 0x800;
+ }
+ break;
+ }
+ if (status & STAT_AD_OVERFLOW) {
+ printk("atmio16d: a/d FIFO overflow\n");
+ outw(0, dev->iobase + AD_CLEAR_REG);
+
+ return -ETIME;
+ }
+ }
+ /* end waiting, now check if it timed out */
+ if (t == ATMIO16D_TIMEOUT) {
+ rt_printk("atmio16d: timeout\n");
+
+ return -ETIME;
+ }
+ }
+
+ return i;
+}
+
+static int atmio16d_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+#ifdef DEBUG1
+ printk("atmio16d_ao_insn_read\n");
+#endif
+
+ for (i = 0; i < insn->n; i++) {
+ data[i] = devpriv->ao_readback[CR_CHAN(insn->chanspec)];
+ }
+
+ return i;
+}
+
+static int atmio16d_ao_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan;
+ int d;
+#ifdef DEBUG1
+ printk("atmio16d_ao_insn_write\n");
+#endif
+
+ chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++) {
+ d = data[i];
+ switch (chan) {
+ case 0:
+ if (devpriv->dac0_coding == dac_2comp) {
+ d ^= 0x800;
+ }
+ outw(d, dev->iobase + DAC0_REG);
+ break;
+ case 1:
+ if (devpriv->dac1_coding == dac_2comp) {
+ d ^= 0x800;
+ }
+ outw(d, dev->iobase + DAC1_REG);
+ break;
+ default:
+ return -EINVAL;
+ }
+ devpriv->ao_readback[chan] = data[i];
+ }
+ return i;
+}
+
+static int atmio16d_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] | data[1]);
+ outw(s->state, dev->iobase + MIO_16_DIG_OUT_REG);
+ }
+ data[1] = inw(dev->iobase + MIO_16_DIG_IN_REG);
+
+ return 2;
+}
+
+static int atmio16d_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int mask;
+
+ for (i = 0; i < insn->n; i++) {
+ mask = (CR_CHAN(insn->chanspec) < 4) ? 0x0f : 0xf0;
+ s->io_bits &= ~mask;
+ if (data[i])
+ s->io_bits |= mask;
+ }
+ devpriv->com_reg_2_state &= ~(COMREG2_DOUTEN0 | COMREG2_DOUTEN1);
+ if (s->io_bits & 0x0f)
+ devpriv->com_reg_2_state |= COMREG2_DOUTEN0;
+ if (s->io_bits & 0xf0)
+ devpriv->com_reg_2_state |= COMREG2_DOUTEN1;
+ outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
+
+ return i;
+}
+
+/*
+ options[0] - I/O port
+ options[1] - MIO irq
+ 0 == no irq
+ N == irq N {3,4,5,6,7,9,10,11,12,14,15}
+ options[2] - DIO irq
+ 0 == no irq
+ N == irq N {3,4,5,6,7,9}
+ options[3] - DMA1 channel
+ 0 == no DMA
+ N == DMA N {5,6,7}
+ options[4] - DMA2 channel
+ 0 == no DMA
+ N == DMA N {5,6,7}
+
+ options[5] - a/d mux
+ 0=differential, 1=single
+ options[6] - a/d range
+ 0=bipolar10, 1=bipolar5, 2=unipolar10
+
+ options[7] - dac0 range
+ 0=bipolar, 1=unipolar
+ options[8] - dac0 reference
+ 0=internal, 1=external
+ options[9] - dac0 coding
+ 0=2's comp, 1=straight binary
+
+ options[10] - dac1 range
+ options[11] - dac1 reference
+ options[12] - dac1 coding
+ */
+
+static int atmio16d_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ unsigned int irq;
+ unsigned long iobase;
+ int ret;
+
+ struct comedi_subdevice *s;
+
+ /* make sure the address range is free and allocate it */
+ iobase = it->options[0];
+ printk("comedi%d: atmio16d: 0x%04lx ", dev->minor, iobase);
+ if (!request_region(iobase, ATMIO16D_SIZE, "ni_atmio16d")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ /* board name */
+ dev->board_name = boardtype->name;
+
+ if ((ret = alloc_subdevices(dev, 4)) < 0)
+ return ret;
+ if ((ret = alloc_private(dev, sizeof(struct atmio16d_private))) < 0)
+ return ret;
+
+ /* reset the atmio16d hardware */
+ reset_atmio16d(dev);
+
+ /* check if our interrupt is available and get it */
+ irq = it->options[1];
+ if (irq) {
+ if ((ret = comedi_request_irq(irq, atmio16d_interrupt,
+ 0, "atmio16d", dev)) < 0) {
+ printk("failed to allocate irq %u\n", irq);
+ return ret;
+ }
+ dev->irq = irq;
+ printk("( irq = %u )\n", irq);
+ } else {
+ printk("( no irq )");
+ }
+
+ /* set device options */
+ devpriv->adc_mux = it->options[5];
+ devpriv->adc_range = it->options[6];
+
+ devpriv->dac0_range = it->options[7];
+ devpriv->dac0_reference = it->options[8];
+ devpriv->dac0_coding = it->options[9];
+ devpriv->dac1_range = it->options[10];
+ devpriv->dac1_reference = it->options[11];
+ devpriv->dac1_coding = it->options[12];
+
+ /* setup sub-devices */
+ s = dev->subdevices + 0;
+ dev->read_subdev = s;
+ /* ai subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
+ s->n_chan = (devpriv->adc_mux ? 16 : 8);
+ s->len_chanlist = 16;
+ s->insn_read = atmio16d_ai_insn_read;
+ s->do_cmdtest = atmio16d_ai_cmdtest;
+ s->do_cmd = atmio16d_ai_cmd;
+ s->cancel = atmio16d_ai_cancel;
+ s->maxdata = 0xfff; /* 4095 decimal */
+ switch (devpriv->adc_range) {
+ case adc_bipolar10:
+ s->range_table = &range_atmio16d_ai_10_bipolar;
+ break;
+ case adc_bipolar5:
+ s->range_table = &range_atmio16d_ai_5_bipolar;
+ break;
+ case adc_unipolar10:
+ s->range_table = &range_atmio16d_ai_unipolar;
+ break;
+ }
+
+ /* ao subdevice */
+ s++;
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->insn_read = atmio16d_ao_insn_read;
+ s->insn_write = atmio16d_ao_insn_write;
+ s->maxdata = 0xfff; /* 4095 decimal */
+ s->range_table_list = devpriv->ao_range_type_list;
+ switch (devpriv->dac0_range) {
+ case dac_bipolar:
+ devpriv->ao_range_type_list[0] = &range_bipolar10;
+ break;
+ case dac_unipolar:
+ devpriv->ao_range_type_list[0] = &range_unipolar10;
+ break;
+ }
+ switch (devpriv->dac1_range) {
+ case dac_bipolar:
+ devpriv->ao_range_type_list[1] = &range_bipolar10;
+ break;
+ case dac_unipolar:
+ devpriv->ao_range_type_list[1] = &range_unipolar10;
+ break;
+ }
+
+ /* Digital I/O */
+ s++;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 8;
+ s->insn_bits = atmio16d_dio_insn_bits;
+ s->insn_config = atmio16d_dio_insn_config;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+
+ /* 8255 subdevice */
+ s++;
+ if (boardtype->has_8255) {
+ subdev_8255_init(dev, s, NULL, dev->iobase);
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+/* don't yet know how to deal with counter/timers */
+#if 0
+ s++;
+ /* do */
+ s->type = COMEDI_SUBD_TIMER;
+ s->n_chan = 0;
+ s->maxdata = 0
+#endif
+ printk("\n");
+
+ return 0;
+}
+
+static int atmio16d_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: atmio16d: remove\n", dev->minor);
+
+ if (dev->subdevices && boardtype->has_8255)
+ subdev_8255_cleanup(dev, dev->subdevices + 3);
+
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+
+ reset_atmio16d(dev);
+
+ if (dev->iobase)
+ release_region(dev->iobase, ATMIO16D_SIZE);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/ni_daq_700.c b/drivers/staging/comedi/drivers/ni_daq_700.c
new file mode 100644
index 000000000000..61a31ad2d030
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_daq_700.c
@@ -0,0 +1,841 @@
+/*
+ * comedi/drivers/ni_daq_700.c
+ * Driver for DAQCard-700 DIO only
+ * copied from 8255
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+Driver: ni_daq_700
+Description: National Instruments PCMCIA DAQCard-700 DIO only
+Author: Fred Brooks <nsaspook@nsaspook.com>,
+ based on ni_daq_dio24 by Daniel Vecino Castel <dvecino@able.es>
+Devices: [National Instruments] PCMCIA DAQ-Card-700 (ni_daq_700)
+Status: works
+Updated: Thu, 21 Feb 2008 12:07:20 +0000
+
+The daqcard-700 appears in Comedi as a single digital I/O subdevice with
+16 channels. The channel 0 corresponds to the daqcard-700's output
+port, bit 0; channel 8 corresponds to the input port, bit 0.
+
+Direction configuration: channels 0-7 output, 8-15 input (8225 device
+emu as port A output, port B input, port C N/A).
+
+IRQ is assigned but not used.
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+static struct pcmcia_device *pcmcia_cur_dev = NULL;
+
+#define DIO700_SIZE 8 // size of io region used by board
+
+static int dio700_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int dio700_detach(struct comedi_device * dev);
+
+enum dio700_bustype { pcmcia_bustype };
+
+struct dio700_board {
+ const char *name;
+ int device_id; // device id for pcmcia board
+ enum dio700_bustype bustype; // PCMCIA
+ int have_dio; // have daqcard-700 dio
+ // function pointers so we can use inb/outb or readb/writeb
+ // as appropriate
+ unsigned int (*read_byte) (unsigned int address);
+ void (*write_byte) (unsigned int byte, unsigned int address);
+};
+
+static const struct dio700_board dio700_boards[] = {
+ {
+ name: "daqcard-700",
+ device_id:0x4743,// 0x10b is manufacturer id, 0x4743 is device id
+ bustype: pcmcia_bustype,
+ have_dio:1,
+ },
+ {
+ name: "ni_daq_700",
+ device_id:0x4743,// 0x10b is manufacturer id, 0x4743 is device id
+ bustype: pcmcia_bustype,
+ have_dio:1,
+ },
+};
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct dio700_board *)dev->board_ptr)
+
+struct dio700_private {
+
+ int data; /* number of data points left to be taken */
+};
+
+
+#define devpriv ((struct dio700_private *)dev->private)
+
+static struct comedi_driver driver_dio700 = {
+ driver_name:"ni_daq_700",
+ module:THIS_MODULE,
+ attach:dio700_attach,
+ detach:dio700_detach,
+ num_names:sizeof(dio700_boards) / sizeof(struct dio700_board),
+ board_name:&dio700_boards[0].name,
+ offset:sizeof(struct dio700_board),
+};
+
+/* the real driver routines */
+
+#define _700_SIZE 8
+
+#define _700_DATA 0
+
+#define DIO_W 0x04
+#define DIO_R 0x05
+
+struct subdev_700_struct {
+ unsigned long cb_arg;
+ int (*cb_func) (int, int, int, unsigned long);
+ int have_irq;
+};
+
+#define CALLBACK_ARG (((struct subdev_700_struct *)s->private)->cb_arg)
+#define CALLBACK_FUNC (((struct subdev_700_struct *)s->private)->cb_func)
+#define subdevpriv ((struct subdev_700_struct *)s->private)
+
+static void do_config(struct comedi_device * dev, struct comedi_subdevice * s);
+
+void subdev_700_interrupt(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ short d;
+
+ d = CALLBACK_FUNC(0, _700_DATA, 0, CALLBACK_ARG);
+
+ comedi_buf_put(s->async, d);
+ s->async->events |= COMEDI_CB_EOS;
+
+ comedi_event(dev, s);
+}
+
+static int subdev_700_cb(int dir, int port, int data, unsigned long arg)
+{
+ /* port is always A for output and B for input (8255 emu) */
+ unsigned long iobase = arg;
+
+ if (dir) {
+ outb(data, iobase + DIO_W);
+ return 0;
+ } else {
+ return inb(iobase + DIO_R);
+ }
+}
+
+static int subdev_700_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+
+ if (data[0] & 0xff)
+ CALLBACK_FUNC(1, _700_DATA, s->state & 0xff,
+ CALLBACK_ARG);
+ }
+
+ data[1] = s->state & 0xff;
+ data[1] |= CALLBACK_FUNC(0, _700_DATA, 0, CALLBACK_ARG) << 8;
+
+ return 2;
+}
+
+static int subdev_700_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_INPUT:
+ break;
+ case INSN_CONFIG_DIO_OUTPUT:
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (s->io_bits & (1 << CR_CHAN(insn->
+ chanspec))) ? COMEDI_OUTPUT :
+ COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+static void do_config(struct comedi_device * dev, struct comedi_subdevice * s)
+{ /* use powerup defaults */
+ return;
+}
+
+static int subdev_700_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ unsigned int tmp;
+
+ /* step 1 */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_FOLLOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2 */
+
+ if (err)
+ return 2;
+
+ /* step 3 */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ if (cmd->scan_end_arg != 1) {
+ cmd->scan_end_arg = 1;
+ err++;
+ }
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4 */
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int subdev_700_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ /* FIXME */
+
+ return 0;
+}
+
+static int subdev_700_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ /* FIXME */
+
+ return 0;
+}
+
+int subdev_700_init(struct comedi_device * dev, struct comedi_subdevice * s, int (*cb) (int,
+ int, int, unsigned long), unsigned long arg)
+{
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 16;
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+
+ s->private = kmalloc(sizeof(struct subdev_700_struct), GFP_KERNEL);
+ if (!s->private)
+ return -ENOMEM;
+
+ CALLBACK_ARG = arg;
+ if (cb == NULL) {
+ CALLBACK_FUNC = subdev_700_cb;
+ } else {
+ CALLBACK_FUNC = cb;
+ }
+ s->insn_bits = subdev_700_insn;
+ s->insn_config = subdev_700_insn_config;
+
+ s->state = 0;
+ s->io_bits = 0x00ff;
+ do_config(dev, s);
+
+ return 0;
+}
+
+int subdev_700_init_irq(struct comedi_device * dev, struct comedi_subdevice * s,
+ int (*cb) (int, int, int, unsigned long), unsigned long arg)
+{
+ int ret;
+
+ ret = subdev_700_init(dev, s, cb, arg);
+ if (ret < 0)
+ return ret;
+
+ s->do_cmdtest = subdev_700_cmdtest;
+ s->do_cmd = subdev_700_cmd;
+ s->cancel = subdev_700_cancel;
+
+ subdevpriv->have_irq = 1;
+
+ return 0;
+}
+
+void subdev_700_cleanup(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ if (s->private) {
+ if (subdevpriv->have_irq) {
+ }
+
+ kfree(s->private);
+ }
+}
+
+EXPORT_SYMBOL(subdev_700_init);
+EXPORT_SYMBOL(subdev_700_init_irq);
+EXPORT_SYMBOL(subdev_700_cleanup);
+EXPORT_SYMBOL(subdev_700_interrupt);
+
+static int dio700_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase = 0;
+#ifdef incomplete
+ unsigned int irq = 0;
+#endif
+ struct pcmcia_device *link;
+
+ /* allocate and initialize dev->private */
+ if (alloc_private(dev, sizeof(struct dio700_private)) < 0)
+ return -ENOMEM;
+
+ // get base address, irq etc. based on bustype
+ switch (thisboard->bustype) {
+ case pcmcia_bustype:
+ link = pcmcia_cur_dev; /* XXX hack */
+ if (!link)
+ return -EIO;
+ iobase = link->io.BasePort1;
+#ifdef incomplete
+ irq = link->irq.AssignedIRQ;
+#endif
+ break;
+ default:
+ printk("bug! couldn't determine board type\n");
+ return -EINVAL;
+ break;
+ }
+ printk("comedi%d: ni_daq_700: %s, io 0x%lx", dev->minor,
+ thisboard->name, iobase);
+#ifdef incomplete
+ if (irq) {
+ printk(", irq %u", irq);
+ }
+#endif
+
+ printk("\n");
+
+ if (iobase == 0) {
+ printk("io base address is zero!\n");
+ return -EINVAL;
+ }
+
+ dev->iobase = iobase;
+
+#ifdef incomplete
+ /* grab our IRQ */
+ dev->irq = irq;
+#endif
+
+ dev->board_name = thisboard->name;
+
+ if (alloc_subdevices(dev, 1) < 0)
+ return -ENOMEM;
+
+ /* DAQCard-700 dio */
+ s = dev->subdevices + 0;
+ subdev_700_init(dev, s, NULL, dev->iobase);
+
+ return 0;
+};
+
+static int dio700_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: ni_daq_700: cs-remove\n", dev->minor);
+
+ if (dev->subdevices)
+ subdev_700_cleanup(dev, dev->subdevices + 0);
+
+ if (thisboard->bustype != pcmcia_bustype && dev->iobase)
+ release_region(dev->iobase, DIO700_SIZE);
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+
+ return 0;
+};
+
+// PCMCIA crap
+
+/*
+ All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
+ you do not define PCMCIA_DEBUG at all, all the debug code will be
+ left out. If you compile with PCMCIA_DEBUG=0, the debug code will
+ be present but disabled -- but it can then be enabled for specific
+ modules at load time with a 'pc_debug=#' option to insmod.
+*/
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0644);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version = "ni_daq_700.c, based on dummy_cs.c";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+static void dio700_config(struct pcmcia_device *link);
+static void dio700_release(struct pcmcia_device *link);
+static int dio700_cs_suspend(struct pcmcia_device *p_dev);
+static int dio700_cs_resume(struct pcmcia_device *p_dev);
+
+/*
+ The attach() and detach() entry points are used to create and destroy
+ "instances" of the driver, where each instance represents everything
+ needed to manage one actual PCMCIA card.
+*/
+
+static int dio700_cs_attach(struct pcmcia_device *);
+static void dio700_cs_detach(struct pcmcia_device *);
+
+/*
+ You'll also need to prototype all the functions that will actually
+ be used to talk to your device. See 'memory_cs' for a good example
+ of a fully self-sufficient driver; the other drivers rely more or
+ less on other parts of the kernel.
+*/
+
+/*
+ The dev_info variable is the "key" that is used to match up this
+ device driver with appropriate cards, through the card configuration
+ database.
+*/
+
+static const dev_info_t dev_info = "ni_daq_700";
+
+struct local_info_t {
+ struct pcmcia_device *link;
+ dev_node_t node;
+ int stop;
+ struct bus_operations *bus;
+};
+
+/*======================================================================
+
+ dio700_cs_attach() creates an "instance" of the driver, allocating
+ local data structures for one device. The device is registered
+ with Card Services.
+
+ The dev_link structure is initialized, but we don't actually
+ configure the card at this point -- we wait until we receive a
+ card insertion event.
+
+======================================================================*/
+
+static int dio700_cs_attach(struct pcmcia_device *link)
+{
+ struct local_info_t *local;
+
+ printk(KERN_INFO "ni_daq_700: cs-attach\n");
+
+ DEBUG(0, "dio700_cs_attach()\n");
+
+ /* Allocate space for private device-specific data */
+ local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
+ if (!local)
+ return -ENOMEM;
+ local->link = link;
+ link->priv = local;
+
+ /* Interrupt setup */
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Handler = NULL;
+
+ /*
+ General socket configuration defaults can go here. In this
+ client, we assume very little, and rely on the CIS for almost
+ everything. In most clients, many details (i.e., number, sizes,
+ and attributes of IO windows) are fixed by the nature of the
+ device, and can be hard-wired here.
+ */
+ link->conf.Attributes = 0;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ pcmcia_cur_dev = link;
+
+ dio700_config(link);
+
+ return 0;
+} /* dio700_cs_attach */
+
+/*======================================================================
+
+ This deletes a driver "instance". The device is de-registered
+ with Card Services. If it has been released, all local data
+ structures are freed. Otherwise, the structures will be freed
+ when the device is released.
+
+======================================================================*/
+
+static void dio700_cs_detach(struct pcmcia_device *link)
+{
+
+ printk(KERN_INFO "ni_daq_700: cs-detach!\n");
+
+ DEBUG(0, "dio700_cs_detach(0x%p)\n", link);
+
+ if (link->dev_node) {
+ ((struct local_info_t *) link->priv)->stop = 1;
+ dio700_release(link);
+ }
+
+ /* This points to the parent struct local_info_t struct */
+ if (link->priv)
+ kfree(link->priv);
+
+} /* dio700_cs_detach */
+
+/*======================================================================
+
+ dio700_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ device available to the system.
+
+======================================================================*/
+
+static void dio700_config(struct pcmcia_device *link)
+{
+ struct local_info_t *dev = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ int last_ret;
+ u_char buf[64];
+ win_req_t req;
+ memreq_t map;
+ cistpl_cftable_entry_t dflt = { 0 };
+
+ printk(KERN_INFO "ni_daq_700: cs-config\n");
+
+ DEBUG(0, "dio700_config(0x%p)\n", link);
+
+ /*
+ This reads the card's CONFIG tuple to find its configuration
+ registers.
+ */
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ tuple.Attributes = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.TupleOffset = 0;
+ if ((last_ret = pcmcia_get_first_tuple(link, &tuple)) != 0) {
+ cs_error(link, GetFirstTuple, last_ret);
+ goto cs_failed;
+ }
+ if ((last_ret = pcmcia_get_tuple_data(link, &tuple)) != 0) {
+ cs_error(link, GetTupleData, last_ret);
+ goto cs_failed;
+ }
+ if ((last_ret = pcmcia_parse_tuple(&tuple, &parse)) != 0) {
+ cs_error(link, ParseTuple, last_ret);
+ goto cs_failed;
+ }
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+
+ /*
+ In this loop, we scan the CIS for configuration table entries,
+ each of which describes a valid card configuration, including
+ voltage, IO window, memory window, and interrupt settings.
+
+ We make no assumptions about the card to be configured: we use
+ just the information available in the CIS. In an ideal world,
+ this would work for any PCMCIA card, but it requires a complete
+ and accurate CIS. In practice, a driver usually "knows" most of
+ these things without consulting the CIS, and most client drivers
+ will only use the CIS to fill in implementation-defined details.
+ */
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ if ((last_ret = pcmcia_get_first_tuple(link, &tuple)) != 0) {
+ cs_error(link, GetFirstTuple, last_ret);
+ goto cs_failed;
+ }
+ while (1) {
+ cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
+ if (pcmcia_get_tuple_data(link, &tuple) != 0)
+ goto next_entry;
+ if (pcmcia_parse_tuple(&tuple, &parse) != 0)
+ goto next_entry;
+
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+ dflt = *cfg;
+ if (cfg->index == 0)
+ goto next_entry;
+ link->conf.ConfigIndex = cfg->index;
+
+ /* Does this card need audio output? */
+ if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ link->conf.Attributes |= CONF_ENABLE_SPKR;
+ link->conf.Status = CCSR_AUDIO_ENA;
+ }
+
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
+ link->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ link->io.NumPorts1 = link->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ link->io.BasePort1 = io->win[0].base;
+ link->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ link->io.Attributes2 = link->io.Attributes1;
+ link->io.BasePort2 = io->win[1].base;
+ link->io.NumPorts2 = io->win[1].len;
+ }
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(link, &link->io) != 0)
+ goto next_entry;
+ }
+
+ if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
+ cistpl_mem_t *mem =
+ (cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
+ req.Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM;
+ req.Attributes |= WIN_ENABLE;
+ req.Base = mem->win[0].host_addr;
+ req.Size = mem->win[0].len;
+ if (req.Size < 0x1000)
+ req.Size = 0x1000;
+ req.AccessSpeed = 0;
+ if (pcmcia_request_window(&link, &req, &link->win))
+ goto next_entry;
+ map.Page = 0;
+ map.CardOffset = mem->win[0].card_addr;
+ if (pcmcia_map_mem_page(link->win, &map))
+ goto next_entry;
+ }
+ /* If we got this far, we're cool! */
+ break;
+
+ next_entry:
+ if ((last_ret = pcmcia_get_next_tuple(link, &tuple)) != 0) {
+ cs_error(link, GetNextTuple, last_ret);
+ goto cs_failed;
+ }
+ }
+
+ /*
+ Allocate an interrupt line. Note that this does not assign a
+ handler to the interrupt, unless the 'Handler' member of the
+ irq structure is initialized.
+ */
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ if ((last_ret = pcmcia_request_irq(link, &link->irq)) != 0) {
+ cs_error(link, RequestIRQ, last_ret);
+ goto cs_failed;
+ }
+
+ /*
+ This actually configures the PCMCIA socket -- setting up
+ the I/O windows and the interrupt mapping, and putting the
+ card and host interface into "Memory and IO" mode.
+ */
+ if ((last_ret = pcmcia_request_configuration(link, &link->conf)) != 0) {
+ cs_error(link, RequestConfiguration, last_ret);
+ goto cs_failed;
+ }
+
+ /*
+ At this point, the dev_node_t structure(s) need to be
+ initialized and arranged in a linked list at link->dev.
+ */
+ sprintf(dev->node.dev_name, "ni_daq_700");
+ dev->node.major = dev->node.minor = 0;
+ link->dev_node = &dev->node;
+
+ /* Finally, report what we've done */
+ printk(KERN_INFO "%s: index 0x%02x",
+ dev->node.dev_name, link->conf.ConfigIndex);
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ printk(", irq %d", link->irq.AssignedIRQ);
+ if (link->io.NumPorts1)
+ printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+ link->io.BasePort1 + link->io.NumPorts1 - 1);
+ if (link->io.NumPorts2)
+ printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+ link->io.BasePort2 + link->io.NumPorts2 - 1);
+ if (link->win)
+ printk(", mem 0x%06lx-0x%06lx", req.Base,
+ req.Base + req.Size - 1);
+ printk("\n");
+
+ return;
+
+ cs_failed:
+ printk(KERN_INFO "ni_daq_700 cs failed");
+ dio700_release(link);
+
+} /* dio700_config */
+
+static void dio700_release(struct pcmcia_device *link)
+{
+ DEBUG(0, "dio700_release(0x%p)\n", link);
+
+ pcmcia_disable_device(link);
+} /* dio700_release */
+
+/*======================================================================
+
+ The card status event handler. Mostly, this schedules other
+ stuff to run after an event is received.
+
+ When a CARD_REMOVAL event is received, we immediately set a
+ private flag to block future accesses to this device. All the
+ functions that actually access the device should check this flag
+ to make sure the card is still present.
+
+======================================================================*/
+
+static int dio700_cs_suspend(struct pcmcia_device *link)
+{
+ struct local_info_t *local = link->priv;
+
+ /* Mark the device as stopped, to block IO until later */
+ local->stop = 1;
+ return 0;
+} /* dio700_cs_suspend */
+
+static int dio700_cs_resume(struct pcmcia_device *link)
+{
+ struct local_info_t *local = link->priv;
+
+ local->stop = 0;
+ return 0;
+} /* dio700_cs_resume */
+
+/*====================================================================*/
+
+static struct pcmcia_device_id dio700_cs_ids[] = {
+ /* N.B. These IDs should match those in dio700_boards */
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x4743), /* daqcard-700 */
+ PCMCIA_DEVICE_NULL
+};
+
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pcmcia, dio700_cs_ids);
+
+struct pcmcia_driver dio700_cs_driver = {
+ .probe = dio700_cs_attach,
+ .remove = dio700_cs_detach,
+ .suspend = dio700_cs_suspend,
+ .resume = dio700_cs_resume,
+ .id_table = dio700_cs_ids,
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = dev_info,
+ },
+};
+
+static int __init init_dio700_cs(void)
+{
+ printk("ni_daq_700: cs-init \n");
+ DEBUG(0, "%s\n", version);
+ pcmcia_register_driver(&dio700_cs_driver);
+ return 0;
+}
+
+static void __exit exit_dio700_cs(void)
+{
+ DEBUG(0, "ni_daq_700: unloading\n");
+ pcmcia_unregister_driver(&dio700_cs_driver);
+}
+int __init init_module(void)
+{
+ int ret;
+
+ ret = init_dio700_cs();
+ if (ret < 0)
+ return ret;
+
+ return comedi_driver_register(&driver_dio700);
+}
+
+void __exit cleanup_module(void)
+{
+ exit_dio700_cs();
+ comedi_driver_unregister(&driver_dio700);
+}
diff --git a/drivers/staging/comedi/drivers/ni_daq_dio24.c b/drivers/staging/comedi/drivers/ni_daq_dio24.c
new file mode 100644
index 000000000000..daec5c4b4c95
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_daq_dio24.c
@@ -0,0 +1,598 @@
+/*
+ comedi/drivers/ni_daq_dio24.c
+ Driver for National Instruments PCMCIA DAQ-Card DIO-24
+ Copyright (C) 2002 Daniel Vecino Castel <dvecino@able.es>
+
+ PCMCIA crap at end of file is adapted from dummy_cs.c 1.31 2001/08/24 12:13:13
+ from the pcmcia package.
+ The initial developer of the pcmcia dummy_cs.c code is David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+ 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.
+
+************************************************************************
+*/
+/*
+Driver: ni_daq_dio24
+Description: National Instruments PCMCIA DAQ-Card DIO-24
+Author: Daniel Vecino Castel <dvecino@able.es>
+Devices: [National Instruments] PCMCIA DAQ-Card DIO-24 (ni_daq_dio24)
+Status: ?
+Updated: Thu, 07 Nov 2002 21:53:06 -0800
+
+This is just a wrapper around the 8255.o driver to properly handle
+the PCMCIA interface.
+*/
+
+//#define LABPC_DEBUG // enable debugging messages
+#undef LABPC_DEBUG
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+#include "8255.h"
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+static struct pcmcia_device *pcmcia_cur_dev = NULL;
+
+#define DIO24_SIZE 4 // size of io region used by board
+
+static int dio24_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int dio24_detach(struct comedi_device * dev);
+
+enum dio24_bustype { pcmcia_bustype };
+
+typedef struct dio24_board_struct {
+ const char *name;
+ int device_id; // device id for pcmcia board
+ enum dio24_bustype bustype; // PCMCIA
+ int have_dio; // have 8255 chip
+ // function pointers so we can use inb/outb or readb/writeb as appropriate
+ unsigned int (*read_byte) (unsigned int address);
+ void (*write_byte) (unsigned int byte, unsigned int address);
+} dio24_board;
+
+static const dio24_board dio24_boards[] = {
+ {
+ name: "daqcard-dio24",
+ device_id:0x475c,// 0x10b is manufacturer id, 0x475c is device id
+ bustype: pcmcia_bustype,
+ have_dio:1,
+ },
+ {
+ name: "ni_daq_dio24",
+ device_id:0x475c,// 0x10b is manufacturer id, 0x475c is device id
+ bustype: pcmcia_bustype,
+ have_dio:1,
+ },
+};
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const dio24_board *)dev->board_ptr)
+
+struct dio24_private {
+
+ int data; /* number of data points left to be taken */
+};
+
+
+#define devpriv ((struct dio24_private *)dev->private)
+
+static struct comedi_driver driver_dio24 = {
+ driver_name:"ni_daq_dio24",
+ module:THIS_MODULE,
+ attach:dio24_attach,
+ detach:dio24_detach,
+ num_names:sizeof(dio24_boards) / sizeof(dio24_board),
+ board_name:&dio24_boards[0].name,
+ offset:sizeof(dio24_board),
+};
+
+static int dio24_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase = 0;
+#ifdef incomplete
+ unsigned int irq = 0;
+#endif
+ struct pcmcia_device *link;
+
+ /* allocate and initialize dev->private */
+ if (alloc_private(dev, sizeof(struct dio24_private)) < 0)
+ return -ENOMEM;
+
+ // get base address, irq etc. based on bustype
+ switch (thisboard->bustype) {
+ case pcmcia_bustype:
+ link = pcmcia_cur_dev; /* XXX hack */
+ if (!link)
+ return -EIO;
+ iobase = link->io.BasePort1;
+#ifdef incomplete
+ irq = link->irq.AssignedIRQ;
+#endif
+ break;
+ default:
+ printk("bug! couldn't determine board type\n");
+ return -EINVAL;
+ break;
+ }
+ printk("comedi%d: ni_daq_dio24: %s, io 0x%lx", dev->minor,
+ thisboard->name, iobase);
+#ifdef incomplete
+ if (irq) {
+ printk(", irq %u", irq);
+ }
+#endif
+
+ printk("\n");
+
+ if (iobase == 0) {
+ printk("io base address is zero!\n");
+ return -EINVAL;
+ }
+
+ dev->iobase = iobase;
+
+#ifdef incomplete
+ /* grab our IRQ */
+ dev->irq = irq;
+#endif
+
+ dev->board_name = thisboard->name;
+
+ if (alloc_subdevices(dev, 1) < 0)
+ return -ENOMEM;
+
+ /* 8255 dio */
+ s = dev->subdevices + 0;
+ subdev_8255_init(dev, s, NULL, dev->iobase);
+
+ return 0;
+};
+
+static int dio24_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: ni_daq_dio24: remove\n", dev->minor);
+
+ if (dev->subdevices)
+ subdev_8255_cleanup(dev, dev->subdevices + 0);
+
+ if (thisboard->bustype != pcmcia_bustype && dev->iobase)
+ release_region(dev->iobase, DIO24_SIZE);
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+
+ return 0;
+};
+
+// PCMCIA crap
+
+/*
+ All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
+ you do not define PCMCIA_DEBUG at all, all the debug code will be
+ left out. If you compile with PCMCIA_DEBUG=0, the debug code will
+ be present but disabled -- but it can then be enabled for specific
+ modules at load time with a 'pc_debug=#' option to insmod.
+*/
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0644);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version = "ni_daq_dio24.c, based on dummy_cs.c";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+static void dio24_config(struct pcmcia_device *link);
+static void dio24_release(struct pcmcia_device *link);
+static int dio24_cs_suspend(struct pcmcia_device *p_dev);
+static int dio24_cs_resume(struct pcmcia_device *p_dev);
+
+/*
+ The attach() and detach() entry points are used to create and destroy
+ "instances" of the driver, where each instance represents everything
+ needed to manage one actual PCMCIA card.
+*/
+
+static int dio24_cs_attach(struct pcmcia_device *);
+static void dio24_cs_detach(struct pcmcia_device *);
+
+/*
+ You'll also need to prototype all the functions that will actually
+ be used to talk to your device. See 'memory_cs' for a good example
+ of a fully self-sufficient driver; the other drivers rely more or
+ less on other parts of the kernel.
+*/
+
+/*
+ The dev_info variable is the "key" that is used to match up this
+ device driver with appropriate cards, through the card configuration
+ database.
+*/
+
+static const dev_info_t dev_info = "ni_daq_dio24";
+
+typedef struct local_info_t {
+ struct pcmcia_device *link;
+ dev_node_t node;
+ int stop;
+ struct bus_operations *bus;
+} local_info_t;
+
+/*======================================================================
+
+ dio24_cs_attach() creates an "instance" of the driver, allocating
+ local data structures for one device. The device is registered
+ with Card Services.
+
+ The dev_link structure is initialized, but we don't actually
+ configure the card at this point -- we wait until we receive a
+ card insertion event.
+
+======================================================================*/
+
+static int dio24_cs_attach(struct pcmcia_device *link)
+{
+ local_info_t *local;
+
+ printk(KERN_INFO "ni_daq_dio24: HOLA SOY YO - CS-attach!\n");
+
+ DEBUG(0, "dio24_cs_attach()\n");
+
+ /* Allocate space for private device-specific data */
+ local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
+ if (!local)
+ return -ENOMEM;
+ local->link = link;
+ link->priv = local;
+
+ /* Interrupt setup */
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Handler = NULL;
+
+ /*
+ General socket configuration defaults can go here. In this
+ client, we assume very little, and rely on the CIS for almost
+ everything. In most clients, many details (i.e., number, sizes,
+ and attributes of IO windows) are fixed by the nature of the
+ device, and can be hard-wired here.
+ */
+ link->conf.Attributes = 0;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ pcmcia_cur_dev = link;
+
+ dio24_config(link);
+
+ return 0;
+} /* dio24_cs_attach */
+
+/*======================================================================
+
+ This deletes a driver "instance". The device is de-registered
+ with Card Services. If it has been released, all local data
+ structures are freed. Otherwise, the structures will be freed
+ when the device is released.
+
+======================================================================*/
+
+static void dio24_cs_detach(struct pcmcia_device *link)
+{
+
+ printk(KERN_INFO "ni_daq_dio24: HOLA SOY YO - cs-detach!\n");
+
+ DEBUG(0, "dio24_cs_detach(0x%p)\n", link);
+
+ if (link->dev_node) {
+ ((local_info_t *) link->priv)->stop = 1;
+ dio24_release(link);
+ }
+
+ /* This points to the parent local_info_t struct */
+ if (link->priv)
+ kfree(link->priv);
+
+} /* dio24_cs_detach */
+
+/*======================================================================
+
+ dio24_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ device available to the system.
+
+======================================================================*/
+
+static void dio24_config(struct pcmcia_device *link)
+{
+ local_info_t *dev = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ int last_ret;
+ u_char buf[64];
+ win_req_t req;
+ memreq_t map;
+ cistpl_cftable_entry_t dflt = { 0 };
+
+ printk(KERN_INFO "ni_daq_dio24: HOLA SOY YO! - config\n");
+
+ DEBUG(0, "dio24_config(0x%p)\n", link);
+
+ /*
+ This reads the card's CONFIG tuple to find its configuration
+ registers.
+ */
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ tuple.Attributes = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.TupleOffset = 0;
+ if ((last_ret = pcmcia_get_first_tuple(link, &tuple)) != 0) {
+ cs_error(link, GetFirstTuple, last_ret);
+ goto cs_failed;
+ }
+ if ((last_ret = pcmcia_get_tuple_data(link, &tuple)) != 0) {
+ cs_error(link, GetTupleData, last_ret);
+ goto cs_failed;
+ }
+ if ((last_ret = pcmcia_parse_tuple(&tuple, &parse)) != 0) {
+ cs_error(link, ParseTuple, last_ret);
+ goto cs_failed;
+ }
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+
+ /*
+ In this loop, we scan the CIS for configuration table entries,
+ each of which describes a valid card configuration, including
+ voltage, IO window, memory window, and interrupt settings.
+
+ We make no assumptions about the card to be configured: we use
+ just the information available in the CIS. In an ideal world,
+ this would work for any PCMCIA card, but it requires a complete
+ and accurate CIS. In practice, a driver usually "knows" most of
+ these things without consulting the CIS, and most client drivers
+ will only use the CIS to fill in implementation-defined details.
+ */
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ if ((last_ret = pcmcia_get_first_tuple(link, &tuple)) != 0) {
+ cs_error(link, GetFirstTuple, last_ret);
+ goto cs_failed;
+ }
+ while (1) {
+ cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
+ if (pcmcia_get_tuple_data(link, &tuple) != 0)
+ goto next_entry;
+ if (pcmcia_parse_tuple(&tuple, &parse) != 0)
+ goto next_entry;
+
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+ dflt = *cfg;
+ if (cfg->index == 0)
+ goto next_entry;
+ link->conf.ConfigIndex = cfg->index;
+
+ /* Does this card need audio output? */
+ if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ link->conf.Attributes |= CONF_ENABLE_SPKR;
+ link->conf.Status = CCSR_AUDIO_ENA;
+ }
+
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
+ link->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ link->io.NumPorts1 = link->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ link->io.BasePort1 = io->win[0].base;
+ link->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ link->io.Attributes2 = link->io.Attributes1;
+ link->io.BasePort2 = io->win[1].base;
+ link->io.NumPorts2 = io->win[1].len;
+ }
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(link, &link->io) != 0)
+ goto next_entry;
+ }
+
+ if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
+ cistpl_mem_t *mem =
+ (cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
+ req.Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM;
+ req.Attributes |= WIN_ENABLE;
+ req.Base = mem->win[0].host_addr;
+ req.Size = mem->win[0].len;
+ if (req.Size < 0x1000)
+ req.Size = 0x1000;
+ req.AccessSpeed = 0;
+ if (pcmcia_request_window(&link, &req, &link->win))
+ goto next_entry;
+ map.Page = 0;
+ map.CardOffset = mem->win[0].card_addr;
+ if (pcmcia_map_mem_page(link->win, &map))
+ goto next_entry;
+ }
+ /* If we got this far, we're cool! */
+ break;
+
+ next_entry:
+ if ((last_ret = pcmcia_get_next_tuple(link, &tuple)) != 0) {
+ cs_error(link, GetNextTuple, last_ret);
+ goto cs_failed;
+ }
+ }
+
+ /*
+ Allocate an interrupt line. Note that this does not assign a
+ handler to the interrupt, unless the 'Handler' member of the
+ irq structure is initialized.
+ */
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ if ((last_ret = pcmcia_request_irq(link, &link->irq)) != 0) {
+ cs_error(link, RequestIRQ, last_ret);
+ goto cs_failed;
+ }
+
+ /*
+ This actually configures the PCMCIA socket -- setting up
+ the I/O windows and the interrupt mapping, and putting the
+ card and host interface into "Memory and IO" mode.
+ */
+ if ((last_ret = pcmcia_request_configuration(link, &link->conf)) != 0) {
+ cs_error(link, RequestConfiguration, last_ret);
+ goto cs_failed;
+ }
+
+ /*
+ At this point, the dev_node_t structure(s) need to be
+ initialized and arranged in a linked list at link->dev.
+ */
+ sprintf(dev->node.dev_name, "ni_daq_dio24");
+ dev->node.major = dev->node.minor = 0;
+ link->dev_node = &dev->node;
+
+ /* Finally, report what we've done */
+ printk(KERN_INFO "%s: index 0x%02x",
+ dev->node.dev_name, link->conf.ConfigIndex);
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ printk(", irq %d", link->irq.AssignedIRQ);
+ if (link->io.NumPorts1)
+ printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+ link->io.BasePort1 + link->io.NumPorts1 - 1);
+ if (link->io.NumPorts2)
+ printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+ link->io.BasePort2 + link->io.NumPorts2 - 1);
+ if (link->win)
+ printk(", mem 0x%06lx-0x%06lx", req.Base,
+ req.Base + req.Size - 1);
+ printk("\n");
+
+ return;
+
+ cs_failed:
+ printk(KERN_INFO "Fallo");
+ dio24_release(link);
+
+} /* dio24_config */
+
+static void dio24_release(struct pcmcia_device *link)
+{
+ DEBUG(0, "dio24_release(0x%p)\n", link);
+
+ pcmcia_disable_device(link);
+} /* dio24_release */
+
+/*======================================================================
+
+ The card status event handler. Mostly, this schedules other
+ stuff to run after an event is received.
+
+ When a CARD_REMOVAL event is received, we immediately set a
+ private flag to block future accesses to this device. All the
+ functions that actually access the device should check this flag
+ to make sure the card is still present.
+
+======================================================================*/
+
+static int dio24_cs_suspend(struct pcmcia_device *link)
+{
+ local_info_t *local = link->priv;
+
+ /* Mark the device as stopped, to block IO until later */
+ local->stop = 1;
+ return 0;
+} /* dio24_cs_suspend */
+
+static int dio24_cs_resume(struct pcmcia_device *link)
+{
+ local_info_t *local = link->priv;
+
+ local->stop = 0;
+ return 0;
+} /* dio24_cs_resume */
+
+/*====================================================================*/
+
+static struct pcmcia_device_id dio24_cs_ids[] = {
+ /* N.B. These IDs should match those in dio24_boards */
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x475c), /* daqcard-dio24 */
+ PCMCIA_DEVICE_NULL
+};
+
+MODULE_DEVICE_TABLE(pcmcia, dio24_cs_ids);
+
+struct pcmcia_driver dio24_cs_driver = {
+ .probe = dio24_cs_attach,
+ .remove = dio24_cs_detach,
+ .suspend = dio24_cs_suspend,
+ .resume = dio24_cs_resume,
+ .id_table = dio24_cs_ids,
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = dev_info,
+ },
+};
+
+static int __init init_dio24_cs(void)
+{
+ printk("ni_daq_dio24: HOLA SOY YO!\n");
+ DEBUG(0, "%s\n", version);
+ pcmcia_register_driver(&dio24_cs_driver);
+ return 0;
+}
+
+static void __exit exit_dio24_cs(void)
+{
+ DEBUG(0, "ni_dio24: unloading\n");
+ pcmcia_unregister_driver(&dio24_cs_driver);
+}
+
+int __init init_module(void)
+{
+ int ret;
+
+ ret = init_dio24_cs();
+ if (ret < 0)
+ return ret;
+
+ return comedi_driver_register(&driver_dio24);
+}
+
+void __exit cleanup_module(void)
+{
+ exit_dio24_cs();
+ comedi_driver_unregister(&driver_dio24);
+}
diff --git a/drivers/staging/comedi/drivers/ni_labpc.c b/drivers/staging/comedi/drivers/ni_labpc.c
new file mode 100644
index 000000000000..37898d8474cd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_labpc.c
@@ -0,0 +1,2005 @@
+/*
+ comedi/drivers/ni_labpc.c
+ Driver for National Instruments Lab-PC series boards and compatibles
+ Copyright (C) 2001, 2002, 2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ 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.
+
+************************************************************************
+*/
+/*
+Driver: ni_labpc
+Description: National Instruments Lab-PC (& compatibles)
+Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+Devices: [National Instruments] Lab-PC-1200 (labpc-1200),
+ Lab-PC-1200AI (labpc-1200ai), Lab-PC+ (lab-pc+), PCI-1200 (ni_labpc)
+Status: works
+
+Tested with lab-pc-1200. For the older Lab-PC+, not all input ranges
+and analog references will work, the available ranges/arefs will
+depend on how you have configured the jumpers on your board
+(see your owner's manual).
+
+Kernel-level ISA plug-and-play support for the lab-pc-1200
+boards has not
+yet been added to the driver, mainly due to the fact that
+I don't know the device id numbers. If you have one
+of these boards,
+please file a bug report at https://bugs.comedi.org/
+so I can get the necessary information from you.
+
+The 1200 series boards have onboard calibration dacs for correcting
+analog input/output offsets and gains. The proper settings for these
+caldacs are stored on the board's eeprom. To read the caldac values
+from the eeprom and store them into a file that can be then be used by
+comedilib, use the comedi_calibrate program.
+
+Configuration options - ISA boards:
+ [0] - I/O port base address
+ [1] - IRQ (optional, required for timed or externally triggered conversions)
+ [2] - DMA channel (optional)
+
+Configuration options - PCI boards:
+ [0] - bus (optional)
+ [1] - slot (optional)
+
+The Lab-pc+ has quirky chanlist requirements
+when scanning multiple channels. Multiple channel scan
+sequence must start at highest channel, then decrement down to
+channel 0. The rest of the cards can scan down like lab-pc+ or scan
+up from channel zero. Chanlists consisting of all one channel
+are also legal, and allow you to pace conversions in bursts.
+
+*/
+
+/*
+
+NI manuals:
+341309a (labpc-1200 register manual)
+340914a (pci-1200)
+320502b (lab-pc+)
+
+*/
+
+#undef LABPC_DEBUG
+//#define LABPC_DEBUG // enable debugging messages
+
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+#include <asm/dma.h>
+
+#include "8253.h"
+#include "8255.h"
+#include "mite.h"
+#include "comedi_fc.h"
+#include "ni_labpc.h"
+
+#define DRV_NAME "ni_labpc"
+
+#define LABPC_SIZE 32 // size of io region used by board
+#define LABPC_TIMER_BASE 500 // 2 MHz master clock
+
+/* Registers for the lab-pc+ */
+
+//write-only registers
+#define COMMAND1_REG 0x0
+#define ADC_GAIN_MASK (0x7 << 4)
+#define ADC_CHAN_BITS(x) ((x) & 0x7)
+#define ADC_SCAN_EN_BIT 0x80 // enables multi channel scans
+#define COMMAND2_REG 0x1
+#define PRETRIG_BIT 0x1 // enable pretriggering (used in conjunction with SWTRIG)
+#define HWTRIG_BIT 0x2 // enable paced conversions on external trigger
+#define SWTRIG_BIT 0x4 // enable paced conversions
+#define CASCADE_BIT 0x8 // use two cascaded counters for pacing
+#define DAC_PACED_BIT(channel) (0x40 << ((channel) & 0x1))
+#define COMMAND3_REG 0x2
+#define DMA_EN_BIT 0x1 // enable dma transfers
+#define DIO_INTR_EN_BIT 0x2 // enable interrupts for 8255
+#define DMATC_INTR_EN_BIT 0x4 // enable dma terminal count interrupt
+#define TIMER_INTR_EN_BIT 0x8 // enable timer interrupt
+#define ERR_INTR_EN_BIT 0x10 // enable error interrupt
+#define ADC_FNE_INTR_EN_BIT 0x20 // enable fifo not empty interrupt
+#define ADC_CONVERT_REG 0x3
+#define DAC_LSB_REG(channel) (0x4 + 2 * ((channel) & 0x1))
+#define DAC_MSB_REG(channel) (0x5 + 2 * ((channel) & 0x1))
+#define ADC_CLEAR_REG 0x8
+#define DMATC_CLEAR_REG 0xa
+#define TIMER_CLEAR_REG 0xc
+#define COMMAND6_REG 0xe // 1200 boards only
+#define ADC_COMMON_BIT 0x1 // select ground or common-mode reference
+#define ADC_UNIP_BIT 0x2 // adc unipolar
+#define DAC_UNIP_BIT(channel) (0x4 << ((channel) & 0x1)) // dac unipolar
+#define ADC_FHF_INTR_EN_BIT 0x20 // enable fifo half full interrupt
+#define A1_INTR_EN_BIT 0x40 // enable interrupt on end of hardware count
+#define ADC_SCAN_UP_BIT 0x80 // scan up from channel zero instead of down to zero
+#define COMMAND4_REG 0xf
+#define INTERVAL_SCAN_EN_BIT 0x1 // enables 'interval' scanning
+#define EXT_SCAN_EN_BIT 0x2 // enables external signal on counter b1 output to trigger scan
+#define EXT_CONVERT_OUT_BIT 0x4 // chooses direction (output or input) for EXTCONV* line
+#define ADC_DIFF_BIT 0x8 // chooses differential inputs for adc (in conjunction with board jumper)
+#define EXT_CONVERT_DISABLE_BIT 0x10
+#define COMMAND5_REG 0x1c // 1200 boards only, calibration stuff
+#define EEPROM_WRITE_UNPROTECT_BIT 0x4 // enable eeprom for write
+#define DITHER_EN_BIT 0x8 // enable dithering
+#define CALDAC_LOAD_BIT 0x10 // load calibration dac
+#define SCLOCK_BIT 0x20 // serial clock - rising edge writes, falling edge reads
+#define SDATA_BIT 0x40 // serial data bit for writing to eeprom or calibration dacs
+#define EEPROM_EN_BIT 0x80 // enable eeprom for read/write
+#define INTERVAL_COUNT_REG 0x1e
+#define INTERVAL_LOAD_REG 0x1f
+#define INTERVAL_LOAD_BITS 0x1
+
+// read-only registers
+#define STATUS1_REG 0x0
+#define DATA_AVAIL_BIT 0x1 // data is available in fifo
+#define OVERRUN_BIT 0x2 // overrun has occurred
+#define OVERFLOW_BIT 0x4 // fifo overflow
+#define TIMER_BIT 0x8 // timer interrupt has occured
+#define DMATC_BIT 0x10 // dma terminal count has occured
+#define EXT_TRIG_BIT 0x40 // external trigger has occured
+#define STATUS2_REG 0x1d // 1200 boards only
+#define EEPROM_OUT_BIT 0x1 // programmable eeprom serial output
+#define A1_TC_BIT 0x2 // counter A1 terminal count
+#define FNHF_BIT 0x4 // fifo not half full
+#define ADC_FIFO_REG 0xa
+
+#define DIO_BASE_REG 0x10
+#define COUNTER_A_BASE_REG 0x14
+#define COUNTER_A_CONTROL_REG (COUNTER_A_BASE_REG + 0x3)
+#define INIT_A0_BITS 0x14 // check modes put conversion pacer output in harmless state (a0 mode 2)
+#define INIT_A1_BITS 0x70 // put hardware conversion counter output in harmless state (a1 mode 0)
+#define COUNTER_B_BASE_REG 0x18
+
+static int labpc_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int labpc_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static irqreturn_t labpc_interrupt(int irq, void *d PT_REGS_ARG);
+static int labpc_drain_fifo(struct comedi_device * dev);
+static void labpc_drain_dma(struct comedi_device * dev);
+static void handle_isa_dma(struct comedi_device * dev);
+static void labpc_drain_dregs(struct comedi_device * dev);
+static int labpc_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int labpc_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int labpc_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int labpc_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int labpc_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int labpc_calib_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int labpc_calib_write_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int labpc_eeprom_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int labpc_eeprom_write_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static unsigned int labpc_suggest_transfer_size(struct comedi_cmd cmd);
+static void labpc_adc_timing(struct comedi_device * dev, struct comedi_cmd * cmd);
+#ifdef CONFIG_COMEDI_PCI
+static int labpc_find_device(struct comedi_device *dev, int bus, int slot);
+#endif
+static int labpc_dio_mem_callback(int dir, int port, int data,
+ unsigned long arg);
+static void labpc_serial_out(struct comedi_device * dev, unsigned int value,
+ unsigned int num_bits);
+static unsigned int labpc_serial_in(struct comedi_device * dev);
+static unsigned int labpc_eeprom_read(struct comedi_device * dev,
+ unsigned int address);
+static unsigned int labpc_eeprom_read_status(struct comedi_device * dev);
+static unsigned int labpc_eeprom_write(struct comedi_device * dev,
+ unsigned int address, unsigned int value);
+static void write_caldac(struct comedi_device * dev, unsigned int channel,
+ unsigned int value);
+
+enum scan_mode {
+ MODE_SINGLE_CHAN,
+ MODE_SINGLE_CHAN_INTERVAL,
+ MODE_MULT_CHAN_UP,
+ MODE_MULT_CHAN_DOWN,
+};
+
+//analog input ranges
+#define NUM_LABPC_PLUS_AI_RANGES 16
+// indicates unipolar ranges
+static const int labpc_plus_is_unipolar[NUM_LABPC_PLUS_AI_RANGES] = {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+};
+
+// map range index to gain bits
+static const int labpc_plus_ai_gain_bits[NUM_LABPC_PLUS_AI_RANGES] = {
+ 0x00,
+ 0x10,
+ 0x20,
+ 0x30,
+ 0x40,
+ 0x50,
+ 0x60,
+ 0x70,
+ 0x00,
+ 0x10,
+ 0x20,
+ 0x30,
+ 0x40,
+ 0x50,
+ 0x60,
+ 0x70,
+};
+static const struct comedi_lrange range_labpc_plus_ai = {
+ NUM_LABPC_PLUS_AI_RANGES,
+ {
+ BIP_RANGE(5),
+ BIP_RANGE(4),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.25),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.05),
+ UNI_RANGE(10),
+ UNI_RANGE(8),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1),
+ UNI_RANGE(0.5),
+ UNI_RANGE(0.2),
+ UNI_RANGE(0.1),
+ }
+};
+
+#define NUM_LABPC_1200_AI_RANGES 14
+// indicates unipolar ranges
+const int labpc_1200_is_unipolar[NUM_LABPC_1200_AI_RANGES] = {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+};
+
+// map range index to gain bits
+const int labpc_1200_ai_gain_bits[NUM_LABPC_1200_AI_RANGES] = {
+ 0x00,
+ 0x20,
+ 0x30,
+ 0x40,
+ 0x50,
+ 0x60,
+ 0x70,
+ 0x00,
+ 0x20,
+ 0x30,
+ 0x40,
+ 0x50,
+ 0x60,
+ 0x70,
+};
+const struct comedi_lrange range_labpc_1200_ai = {
+ NUM_LABPC_1200_AI_RANGES,
+ {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.25),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.05),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1),
+ UNI_RANGE(0.5),
+ UNI_RANGE(0.2),
+ UNI_RANGE(0.1),
+ }
+};
+
+//analog output ranges
+#define AO_RANGE_IS_UNIPOLAR 0x1
+static const struct comedi_lrange range_labpc_ao = {
+ 2,
+ {
+ BIP_RANGE(5),
+ UNI_RANGE(10),
+ }
+};
+
+/* functions that do inb/outb and readb/writeb so we can use
+ * function pointers to decide which to use */
+static inline unsigned int labpc_inb(unsigned long address)
+{
+ return inb(address);
+}
+static inline void labpc_outb(unsigned int byte, unsigned long address)
+{
+ outb(byte, address);
+}
+static inline unsigned int labpc_readb(unsigned long address)
+{
+ return readb((void *)address);
+}
+static inline void labpc_writeb(unsigned int byte, unsigned long address)
+{
+ writeb(byte, (void *)address);
+}
+
+static const labpc_board labpc_boards[] = {
+ {
+ name: "lab-pc-1200",
+ ai_speed:10000,
+ bustype: isa_bustype,
+ register_layout:labpc_1200_layout,
+ has_ao: 1,
+ ai_range_table:&range_labpc_1200_ai,
+ ai_range_code:labpc_1200_ai_gain_bits,
+ ai_range_is_unipolar:labpc_1200_is_unipolar,
+ ai_scan_up:1,
+ memory_mapped_io:0,
+ },
+ {
+ name: "lab-pc-1200ai",
+ ai_speed:10000,
+ bustype: isa_bustype,
+ register_layout:labpc_1200_layout,
+ has_ao: 0,
+ ai_range_table:&range_labpc_1200_ai,
+ ai_range_code:labpc_1200_ai_gain_bits,
+ ai_range_is_unipolar:labpc_1200_is_unipolar,
+ ai_scan_up:1,
+ memory_mapped_io:0,
+ },
+ {
+ name: "lab-pc+",
+ ai_speed:12000,
+ bustype: isa_bustype,
+ register_layout:labpc_plus_layout,
+ has_ao: 1,
+ ai_range_table:&range_labpc_plus_ai,
+ ai_range_code:labpc_plus_ai_gain_bits,
+ ai_range_is_unipolar:labpc_plus_is_unipolar,
+ ai_scan_up:0,
+ memory_mapped_io:0,
+ },
+#ifdef CONFIG_COMEDI_PCI
+ {
+ name: "pci-1200",
+ device_id:0x161,
+ ai_speed:10000,
+ bustype: pci_bustype,
+ register_layout:labpc_1200_layout,
+ has_ao: 1,
+ ai_range_table:&range_labpc_1200_ai,
+ ai_range_code:labpc_1200_ai_gain_bits,
+ ai_range_is_unipolar:labpc_1200_is_unipolar,
+ ai_scan_up:1,
+ memory_mapped_io:1,
+ },
+ // dummy entry so pci board works when comedi_config is passed driver name
+ {
+ .name = DRV_NAME,
+ .bustype = pci_bustype,
+ },
+#endif
+};
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((labpc_board *)dev->board_ptr)
+
+static const int dma_buffer_size = 0xff00; // size in bytes of dma buffer
+static const int sample_size = 2; // 2 bytes per sample
+
+#define devpriv ((labpc_private *)dev->private)
+
+static struct comedi_driver driver_labpc = {
+ .driver_name = DRV_NAME,
+ .module = THIS_MODULE,
+ .attach = labpc_attach,
+ .detach = labpc_common_detach,
+ .num_names = sizeof(labpc_boards) / sizeof(labpc_board),
+ .board_name = &labpc_boards[0].name,
+ .offset = sizeof(labpc_board),
+};
+
+#ifdef CONFIG_COMEDI_PCI
+static DEFINE_PCI_DEVICE_TABLE(labpc_pci_table) = {
+ {PCI_VENDOR_ID_NATINST, 0x161, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, labpc_pci_table);
+#endif /* CONFIG_COMEDI_PCI */
+
+static inline int labpc_counter_load(struct comedi_device * dev,
+ unsigned long base_address, unsigned int counter_number,
+ unsigned int count, unsigned int mode)
+{
+ if (thisboard->memory_mapped_io)
+ return i8254_mm_load((void *)base_address, 0, counter_number,
+ count, mode);
+ else
+ return i8254_load(base_address, 0, counter_number, count, mode);
+}
+
+int labpc_common_attach(struct comedi_device * dev, unsigned long iobase,
+ unsigned int irq, unsigned int dma_chan)
+{
+ struct comedi_subdevice *s;
+ int i;
+ unsigned long dma_flags, isr_flags;
+ short lsb, msb;
+
+ printk("comedi%d: ni_labpc: %s, io 0x%lx", dev->minor, thisboard->name,
+ iobase);
+ if (irq) {
+ printk(", irq %u", irq);
+ }
+ if (dma_chan) {
+ printk(", dma %u", dma_chan);
+ }
+ printk("\n");
+
+ if (iobase == 0) {
+ printk("io base address is zero!\n");
+ return -EINVAL;
+ }
+ // request io regions for isa boards
+ if (thisboard->bustype == isa_bustype) {
+ /* check if io addresses are available */
+ if (!request_region(iobase, LABPC_SIZE,
+ driver_labpc.driver_name)) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ }
+ dev->iobase = iobase;
+
+ if (thisboard->memory_mapped_io) {
+ devpriv->read_byte = labpc_readb;
+ devpriv->write_byte = labpc_writeb;
+ } else {
+ devpriv->read_byte = labpc_inb;
+ devpriv->write_byte = labpc_outb;
+ }
+ // initialize board's command registers
+ devpriv->write_byte(devpriv->command1_bits, dev->iobase + COMMAND1_REG);
+ devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
+ devpriv->write_byte(devpriv->command3_bits, dev->iobase + COMMAND3_REG);
+ devpriv->write_byte(devpriv->command4_bits, dev->iobase + COMMAND4_REG);
+ if (thisboard->register_layout == labpc_1200_layout) {
+ devpriv->write_byte(devpriv->command5_bits,
+ dev->iobase + COMMAND5_REG);
+ devpriv->write_byte(devpriv->command6_bits,
+ dev->iobase + COMMAND6_REG);
+ }
+
+ /* grab our IRQ */
+ if (irq) {
+ isr_flags = 0;
+ if (thisboard->bustype == pci_bustype)
+ isr_flags |= IRQF_SHARED;
+ if (comedi_request_irq(irq, labpc_interrupt, isr_flags,
+ driver_labpc.driver_name, dev)) {
+ printk("unable to allocate irq %u\n", irq);
+ return -EINVAL;
+ }
+ }
+ dev->irq = irq;
+
+ // grab dma channel
+ if (dma_chan > 3) {
+ printk(" invalid dma channel %u\n", dma_chan);
+ return -EINVAL;
+ } else if (dma_chan) {
+ // allocate dma buffer
+ devpriv->dma_buffer =
+ kmalloc(dma_buffer_size, GFP_KERNEL | GFP_DMA);
+ if (devpriv->dma_buffer == NULL) {
+ printk(" failed to allocate dma buffer\n");
+ return -ENOMEM;
+ }
+ if (request_dma(dma_chan, driver_labpc.driver_name)) {
+ printk(" failed to allocate dma channel %u\n",
+ dma_chan);
+ return -EINVAL;
+ }
+ devpriv->dma_chan = dma_chan;
+ dma_flags = claim_dma_lock();
+ disable_dma(devpriv->dma_chan);
+ set_dma_mode(devpriv->dma_chan, DMA_MODE_READ);
+ release_dma_lock(dma_flags);
+ }
+
+ dev->board_name = thisboard->name;
+
+ if (alloc_subdevices(dev, 5) < 0)
+ return -ENOMEM;
+
+ /* analog input subdevice */
+ s = dev->subdevices + 0;
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags =
+ SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF |
+ SDF_CMD_READ;
+ s->n_chan = 8;
+ s->len_chanlist = 8;
+ s->maxdata = (1 << 12) - 1; // 12 bit resolution
+ s->range_table = thisboard->ai_range_table;
+ s->do_cmd = labpc_ai_cmd;
+ s->do_cmdtest = labpc_ai_cmdtest;
+ s->insn_read = labpc_ai_rinsn;
+ s->cancel = labpc_cancel;
+
+ /* analog output */
+ s = dev->subdevices + 1;
+ if (thisboard->has_ao) {
+/* Could provide command support, except it only has a one sample
+ * hardware buffer for analog output and no underrun flag. */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = NUM_AO_CHAN;
+ s->maxdata = (1 << 12) - 1; // 12 bit resolution
+ s->range_table = &range_labpc_ao;
+ s->insn_read = labpc_ao_rinsn;
+ s->insn_write = labpc_ao_winsn;
+ /* initialize analog outputs to a known value */
+ for (i = 0; i < s->n_chan; i++) {
+ devpriv->ao_value[i] = s->maxdata / 2;
+ lsb = devpriv->ao_value[i] & 0xff;
+ msb = (devpriv->ao_value[i] >> 8) & 0xff;
+ devpriv->write_byte(lsb, dev->iobase + DAC_LSB_REG(i));
+ devpriv->write_byte(msb, dev->iobase + DAC_MSB_REG(i));
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* 8255 dio */
+ s = dev->subdevices + 2;
+ // if board uses io memory we have to give a custom callback function to the 8255 driver
+ if (thisboard->memory_mapped_io)
+ subdev_8255_init(dev, s, labpc_dio_mem_callback,
+ (unsigned long)(dev->iobase + DIO_BASE_REG));
+ else
+ subdev_8255_init(dev, s, NULL, dev->iobase + DIO_BASE_REG);
+
+ // calibration subdevices for boards that have one
+ s = dev->subdevices + 3;
+ if (thisboard->register_layout == labpc_1200_layout) {
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = 16;
+ s->maxdata = 0xff;
+ s->insn_read = labpc_calib_read_insn;
+ s->insn_write = labpc_calib_write_insn;
+
+ for (i = 0; i < s->n_chan; i++)
+ write_caldac(dev, i, s->maxdata / 2);
+ } else
+ s->type = COMEDI_SUBD_UNUSED;
+
+ /* EEPROM */
+ s = dev->subdevices + 4;
+ if (thisboard->register_layout == labpc_1200_layout) {
+ s->type = COMEDI_SUBD_MEMORY;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = EEPROM_SIZE;
+ s->maxdata = 0xff;
+ s->insn_read = labpc_eeprom_read_insn;
+ s->insn_write = labpc_eeprom_write_insn;
+
+ for (i = 0; i < EEPROM_SIZE; i++) {
+ devpriv->eeprom_data[i] = labpc_eeprom_read(dev, i);
+ }
+#ifdef LABPC_DEBUG
+ printk(" eeprom:");
+ for (i = 0; i < EEPROM_SIZE; i++) {
+ printk(" %i:0x%x ", i, devpriv->eeprom_data[i]);
+ }
+ printk("\n");
+#endif
+ } else
+ s->type = COMEDI_SUBD_UNUSED;
+
+ return 0;
+}
+
+static int labpc_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ unsigned long iobase = 0;
+ unsigned int irq = 0;
+ unsigned int dma_chan = 0;
+#ifdef CONFIG_COMEDI_PCI
+ int retval;
+#endif
+
+ /* allocate and initialize dev->private */
+ if (alloc_private(dev, sizeof(labpc_private)) < 0)
+ return -ENOMEM;
+
+ // get base address, irq etc. based on bustype
+ switch (thisboard->bustype) {
+ case isa_bustype:
+ iobase = it->options[0];
+ irq = it->options[1];
+ dma_chan = it->options[2];
+ break;
+ case pci_bustype:
+#ifdef CONFIG_COMEDI_PCI
+ retval = labpc_find_device(dev, it->options[0], it->options[1]);
+ if (retval < 0) {
+ return retval;
+ }
+ retval = mite_setup(devpriv->mite);
+ if (retval < 0)
+ return retval;
+ iobase = (unsigned long)devpriv->mite->daq_io_addr;
+ irq = mite_irq(devpriv->mite);
+#else
+ printk(" this driver has not been built with PCI support.\n");
+ return -EINVAL;
+#endif
+ break;
+ case pcmcia_bustype:
+ printk(" this driver does not support pcmcia cards, use ni_labpc_cs.o\n");
+ return -EINVAL;
+ break;
+ default:
+ printk("bug! couldn't determine board type\n");
+ return -EINVAL;
+ break;
+ }
+
+ return labpc_common_attach(dev, iobase, irq, dma_chan);
+}
+
+// adapted from ni_pcimio for finding mite based boards (pc-1200)
+#ifdef CONFIG_COMEDI_PCI
+static int labpc_find_device(struct comedi_device *dev, int bus, int slot)
+{
+ struct mite_struct *mite;
+ int i;
+ for (mite = mite_devices; mite; mite = mite->next) {
+ if (mite->used)
+ continue;
+ // if bus/slot are specified then make sure we have the right bus/slot
+ if (bus || slot) {
+ if (bus != mite->pcidev->bus->number
+ || slot != PCI_SLOT(mite->pcidev->devfn))
+ continue;
+ }
+ for (i = 0; i < driver_labpc.num_names; i++) {
+ if (labpc_boards[i].bustype != pci_bustype)
+ continue;
+ if (mite_device_id(mite) == labpc_boards[i].device_id) {
+ devpriv->mite = mite;
+ // fixup board pointer, in case we were using the dummy "ni_labpc" entry
+ dev->board_ptr = &labpc_boards[i];
+ return 0;
+ }
+ }
+ }
+ printk("no device found\n");
+ mite_list_devices();
+ return -EIO;
+}
+#endif
+
+int labpc_common_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: ni_labpc: detach\n", dev->minor);
+
+ if (dev->subdevices)
+ subdev_8255_cleanup(dev, dev->subdevices + 2);
+
+ /* only free stuff if it has been allocated by _attach */
+ if (devpriv->dma_buffer)
+ kfree(devpriv->dma_buffer);
+ if (devpriv->dma_chan)
+ free_dma(devpriv->dma_chan);
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+ if (thisboard->bustype == isa_bustype && dev->iobase)
+ release_region(dev->iobase, LABPC_SIZE);
+#ifdef CONFIG_COMEDI_PCI
+ if (devpriv->mite)
+ mite_unsetup(devpriv->mite);
+#endif
+
+ return 0;
+};
+
+static void labpc_clear_adc_fifo(const struct comedi_device * dev)
+{
+ devpriv->write_byte(0x1, dev->iobase + ADC_CLEAR_REG);
+ devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
+ devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
+}
+
+static int labpc_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->command2_bits &= ~SWTRIG_BIT & ~HWTRIG_BIT & ~PRETRIG_BIT;
+ devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ devpriv->command3_bits = 0;
+ devpriv->write_byte(devpriv->command3_bits, dev->iobase + COMMAND3_REG);
+
+ return 0;
+}
+
+static enum scan_mode labpc_ai_scan_mode(const struct comedi_cmd * cmd)
+{
+ if (cmd->chanlist_len == 1)
+ return MODE_SINGLE_CHAN;
+
+ /* chanlist may be NULL during cmdtest. */
+ if (cmd->chanlist == NULL)
+ return MODE_MULT_CHAN_UP;
+
+ if (CR_CHAN(cmd->chanlist[0]) == CR_CHAN(cmd->chanlist[1]))
+ return MODE_SINGLE_CHAN_INTERVAL;
+
+ if (CR_CHAN(cmd->chanlist[0]) < CR_CHAN(cmd->chanlist[1]))
+ return MODE_MULT_CHAN_UP;
+
+ if (CR_CHAN(cmd->chanlist[0]) > CR_CHAN(cmd->chanlist[1]))
+ return MODE_MULT_CHAN_DOWN;
+
+ rt_printk("ni_labpc: bug! this should never happen\n");
+
+ return 0;
+}
+
+static int labpc_ai_chanlist_invalid(const struct comedi_device * dev,
+ const struct comedi_cmd * cmd)
+{
+ int mode, channel, range, aref, i;
+
+ if (cmd->chanlist == NULL)
+ return 0;
+
+ mode = labpc_ai_scan_mode(cmd);
+
+ if (mode == MODE_SINGLE_CHAN)
+ return 0;
+
+ if (mode == MODE_SINGLE_CHAN_INTERVAL) {
+ if (cmd->chanlist_len > 0xff) {
+ comedi_error(dev,
+ "ni_labpc: chanlist too long for single channel interval mode\n");
+ return 1;
+ }
+ }
+
+ channel = CR_CHAN(cmd->chanlist[0]);
+ range = CR_RANGE(cmd->chanlist[0]);
+ aref = CR_AREF(cmd->chanlist[0]);
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+
+ switch (mode) {
+ case MODE_SINGLE_CHAN_INTERVAL:
+ if (CR_CHAN(cmd->chanlist[i]) != channel) {
+ comedi_error(dev,
+ "channel scanning order specified in chanlist is not supported by hardware.\n");
+ return 1;
+ }
+ break;
+ case MODE_MULT_CHAN_UP:
+ if (CR_CHAN(cmd->chanlist[i]) != i) {
+ comedi_error(dev,
+ "channel scanning order specified in chanlist is not supported by hardware.\n");
+ return 1;
+ }
+ break;
+ case MODE_MULT_CHAN_DOWN:
+ if (CR_CHAN(cmd->chanlist[i]) !=
+ cmd->chanlist_len - i - 1) {
+ comedi_error(dev,
+ "channel scanning order specified in chanlist is not supported by hardware.\n");
+ return 1;
+ }
+ break;
+ default:
+ rt_printk("ni_labpc: bug! in chanlist check\n");
+ return 1;
+ break;
+ }
+
+ if (CR_RANGE(cmd->chanlist[i]) != range) {
+ comedi_error(dev,
+ "entries in chanlist must all have the same range\n");
+ return 1;
+ }
+
+ if (CR_AREF(cmd->chanlist[i]) != aref) {
+ comedi_error(dev,
+ "entries in chanlist must all have the same reference\n");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int labpc_use_continuous_mode(const struct comedi_cmd * cmd)
+{
+ if (labpc_ai_scan_mode(cmd) == MODE_SINGLE_CHAN)
+ return 1;
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW)
+ return 1;
+
+ return 0;
+}
+
+static unsigned int labpc_ai_convert_period(const struct comedi_cmd * cmd)
+{
+ if (cmd->convert_src != TRIG_TIMER)
+ return 0;
+
+ if (labpc_ai_scan_mode(cmd) == MODE_SINGLE_CHAN &&
+ cmd->scan_begin_src == TRIG_TIMER)
+ return cmd->scan_begin_arg;
+
+ return cmd->convert_arg;
+}
+
+static void labpc_set_ai_convert_period(struct comedi_cmd * cmd, unsigned int ns)
+{
+ if (cmd->convert_src != TRIG_TIMER)
+ return;
+
+ if (labpc_ai_scan_mode(cmd) == MODE_SINGLE_CHAN &&
+ cmd->scan_begin_src == TRIG_TIMER) {
+ cmd->scan_begin_arg = ns;
+ if (cmd->convert_arg > cmd->scan_begin_arg)
+ cmd->convert_arg = cmd->scan_begin_arg;
+ } else
+ cmd->convert_arg = ns;
+}
+
+static unsigned int labpc_ai_scan_period(const struct comedi_cmd * cmd)
+{
+ if (cmd->scan_begin_src != TRIG_TIMER)
+ return 0;
+
+ if (labpc_ai_scan_mode(cmd) == MODE_SINGLE_CHAN &&
+ cmd->convert_src == TRIG_TIMER)
+ return 0;
+
+ return cmd->scan_begin_arg;
+}
+
+static void labpc_set_ai_scan_period(struct comedi_cmd * cmd, unsigned int ns)
+{
+ if (cmd->scan_begin_src != TRIG_TIMER)
+ return;
+
+ if (labpc_ai_scan_mode(cmd) == MODE_SINGLE_CHAN &&
+ cmd->convert_src == TRIG_TIMER)
+ return;
+
+ cmd->scan_begin_arg = ns;
+}
+
+static int labpc_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp, tmp2;
+ int stop_mask;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_EXT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ stop_mask = TRIG_COUNT | TRIG_NONE;
+ if (thisboard->register_layout == labpc_1200_layout)
+ stop_mask |= TRIG_EXT;
+ cmd->stop_src &= stop_mask;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
+ err++;
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_FOLLOW &&
+ cmd->scan_begin_src != TRIG_EXT)
+ err++;
+ if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT &&
+ cmd->stop_src != TRIG_EXT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ // can't have external stop and start triggers at once
+ if (cmd->start_src == TRIG_EXT && cmd->stop_src == TRIG_EXT)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg == TRIG_NOW && cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ if (!cmd->chanlist_len) {
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < thisboard->ai_speed) {
+ cmd->convert_arg = thisboard->ai_speed;
+ err++;
+ }
+ }
+ // make sure scan timing is not too fast
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->convert_src == TRIG_TIMER &&
+ cmd->scan_begin_arg <
+ cmd->convert_arg * cmd->chanlist_len) {
+ cmd->scan_begin_arg =
+ cmd->convert_arg * cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->scan_begin_arg <
+ thisboard->ai_speed * cmd->chanlist_len) {
+ cmd->scan_begin_arg =
+ thisboard->ai_speed * cmd->chanlist_len;
+ err++;
+ }
+ }
+ // stop source
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ if (!cmd->stop_arg) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ break;
+ case TRIG_NONE:
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ break;
+ // TRIG_EXT doesn't care since it doesn't trigger off a numbered channel
+ default:
+ break;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ tmp = cmd->convert_arg;
+ tmp2 = cmd->scan_begin_arg;
+ labpc_adc_timing(dev, cmd);
+ if (tmp != cmd->convert_arg || tmp2 != cmd->scan_begin_arg)
+ err++;
+
+ if (err)
+ return 4;
+
+ if (labpc_ai_chanlist_invalid(dev, cmd))
+ return 5;
+
+ return 0;
+}
+
+static int labpc_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int channel, range, aref;
+ unsigned long irq_flags;
+ int ret;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ enum transfer_type xfer;
+ unsigned long flags;
+
+ if (!dev->irq) {
+ comedi_error(dev, "no irq assigned, cannot perform command");
+ return -1;
+ }
+
+ range = CR_RANGE(cmd->chanlist[0]);
+ aref = CR_AREF(cmd->chanlist[0]);
+
+ // make sure board is disabled before setting up aquisition
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->command2_bits &= ~SWTRIG_BIT & ~HWTRIG_BIT & ~PRETRIG_BIT;
+ devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ devpriv->command3_bits = 0;
+ devpriv->write_byte(devpriv->command3_bits, dev->iobase + COMMAND3_REG);
+
+ // initialize software conversion count
+ if (cmd->stop_src == TRIG_COUNT) {
+ devpriv->count = cmd->stop_arg * cmd->chanlist_len;
+ }
+ // setup hardware conversion counter
+ if (cmd->stop_src == TRIG_EXT) {
+ // load counter a1 with count of 3 (pc+ manual says this is minimum allowed) using mode 0
+ ret = labpc_counter_load(dev, dev->iobase + COUNTER_A_BASE_REG,
+ 1, 3, 0);
+ if (ret < 0) {
+ comedi_error(dev, "error loading counter a1");
+ return -1;
+ }
+ } else // otherwise, just put a1 in mode 0 with no count to set its output low
+ devpriv->write_byte(INIT_A1_BITS,
+ dev->iobase + COUNTER_A_CONTROL_REG);
+
+ // figure out what method we will use to transfer data
+ if (devpriv->dma_chan && // need a dma channel allocated
+ // dma unsafe at RT priority, and too much setup time for TRIG_WAKE_EOS for
+ (cmd->flags & (TRIG_WAKE_EOS | TRIG_RT)) == 0 &&
+ // only available on the isa boards
+ thisboard->bustype == isa_bustype) {
+ xfer = isa_dma_transfer;
+ } else if (thisboard->register_layout == labpc_1200_layout && // pc-plus has no fifo-half full interrupt
+ // wake-end-of-scan should interrupt on fifo not empty
+ (cmd->flags & TRIG_WAKE_EOS) == 0 &&
+ // make sure we are taking more than just a few points
+ (cmd->stop_src != TRIG_COUNT || devpriv->count > 256)) {
+ xfer = fifo_half_full_transfer;
+ } else
+ xfer = fifo_not_empty_transfer;
+ devpriv->current_transfer = xfer;
+
+ // setup command6 register for 1200 boards
+ if (thisboard->register_layout == labpc_1200_layout) {
+ // reference inputs to ground or common?
+ if (aref != AREF_GROUND)
+ devpriv->command6_bits |= ADC_COMMON_BIT;
+ else
+ devpriv->command6_bits &= ~ADC_COMMON_BIT;
+ // bipolar or unipolar range?
+ if (thisboard->ai_range_is_unipolar[range])
+ devpriv->command6_bits |= ADC_UNIP_BIT;
+ else
+ devpriv->command6_bits &= ~ADC_UNIP_BIT;
+ // interrupt on fifo half full?
+ if (xfer == fifo_half_full_transfer)
+ devpriv->command6_bits |= ADC_FHF_INTR_EN_BIT;
+ else
+ devpriv->command6_bits &= ~ADC_FHF_INTR_EN_BIT;
+ // enable interrupt on counter a1 terminal count?
+ if (cmd->stop_src == TRIG_EXT)
+ devpriv->command6_bits |= A1_INTR_EN_BIT;
+ else
+ devpriv->command6_bits &= ~A1_INTR_EN_BIT;
+ // are we scanning up or down through channels?
+ if (labpc_ai_scan_mode(cmd) == MODE_MULT_CHAN_UP)
+ devpriv->command6_bits |= ADC_SCAN_UP_BIT;
+ else
+ devpriv->command6_bits &= ~ADC_SCAN_UP_BIT;
+ // write to register
+ devpriv->write_byte(devpriv->command6_bits,
+ dev->iobase + COMMAND6_REG);
+ }
+
+ /* setup channel list, etc (command1 register) */
+ devpriv->command1_bits = 0;
+ if (labpc_ai_scan_mode(cmd) == MODE_MULT_CHAN_UP)
+ channel = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]);
+ else
+ channel = CR_CHAN(cmd->chanlist[0]);
+ // munge channel bits for differential / scan disabled mode
+ if (labpc_ai_scan_mode(cmd) != MODE_SINGLE_CHAN && aref == AREF_DIFF)
+ channel *= 2;
+ devpriv->command1_bits |= ADC_CHAN_BITS(channel);
+ devpriv->command1_bits |= thisboard->ai_range_code[range];
+ devpriv->write_byte(devpriv->command1_bits, dev->iobase + COMMAND1_REG);
+ // manual says to set scan enable bit on second pass
+ if (labpc_ai_scan_mode(cmd) == MODE_MULT_CHAN_UP ||
+ labpc_ai_scan_mode(cmd) == MODE_MULT_CHAN_DOWN) {
+ devpriv->command1_bits |= ADC_SCAN_EN_BIT;
+ /* need a brief delay before enabling scan, or scan list will get screwed when you switch
+ * between scan up to scan down mode - dunno why */
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command1_bits,
+ dev->iobase + COMMAND1_REG);
+ }
+ // setup any external triggering/pacing (command4 register)
+ devpriv->command4_bits = 0;
+ if (cmd->convert_src != TRIG_EXT)
+ devpriv->command4_bits |= EXT_CONVERT_DISABLE_BIT;
+ /* XXX should discard first scan when using interval scanning
+ * since manual says it is not synced with scan clock */
+ if (labpc_use_continuous_mode(cmd) == 0) {
+ devpriv->command4_bits |= INTERVAL_SCAN_EN_BIT;
+ if (cmd->scan_begin_src == TRIG_EXT)
+ devpriv->command4_bits |= EXT_SCAN_EN_BIT;
+ }
+ // single-ended/differential
+ if (aref == AREF_DIFF)
+ devpriv->command4_bits |= ADC_DIFF_BIT;
+ devpriv->write_byte(devpriv->command4_bits, dev->iobase + COMMAND4_REG);
+
+ devpriv->write_byte(cmd->chanlist_len,
+ dev->iobase + INTERVAL_COUNT_REG);
+ // load count
+ devpriv->write_byte(INTERVAL_LOAD_BITS,
+ dev->iobase + INTERVAL_LOAD_REG);
+
+ if (cmd->convert_src == TRIG_TIMER || cmd->scan_begin_src == TRIG_TIMER) {
+ // set up pacing
+ labpc_adc_timing(dev, cmd);
+ // load counter b0 in mode 3
+ ret = labpc_counter_load(dev, dev->iobase + COUNTER_B_BASE_REG,
+ 0, devpriv->divisor_b0, 3);
+ if (ret < 0) {
+ comedi_error(dev, "error loading counter b0");
+ return -1;
+ }
+ }
+ // set up conversion pacing
+ if (labpc_ai_convert_period(cmd)) {
+ // load counter a0 in mode 2
+ ret = labpc_counter_load(dev, dev->iobase + COUNTER_A_BASE_REG,
+ 0, devpriv->divisor_a0, 2);
+ if (ret < 0) {
+ comedi_error(dev, "error loading counter a0");
+ return -1;
+ }
+ } else
+ devpriv->write_byte(INIT_A0_BITS,
+ dev->iobase + COUNTER_A_CONTROL_REG);
+
+ // set up scan pacing
+ if (labpc_ai_scan_period(cmd)) {
+ // load counter b1 in mode 2
+ ret = labpc_counter_load(dev, dev->iobase + COUNTER_B_BASE_REG,
+ 1, devpriv->divisor_b1, 2);
+ if (ret < 0) {
+ comedi_error(dev, "error loading counter b1");
+ return -1;
+ }
+ }
+
+ labpc_clear_adc_fifo(dev);
+
+ // set up dma transfer
+ if (xfer == isa_dma_transfer) {
+ irq_flags = claim_dma_lock();
+ disable_dma(devpriv->dma_chan);
+ /* clear flip-flop to make sure 2-byte registers for
+ * count and address get set correctly */
+ clear_dma_ff(devpriv->dma_chan);
+ set_dma_addr(devpriv->dma_chan,
+ virt_to_bus(devpriv->dma_buffer));
+ // set appropriate size of transfer
+ devpriv->dma_transfer_size = labpc_suggest_transfer_size(*cmd);
+ if (cmd->stop_src == TRIG_COUNT &&
+ devpriv->count * sample_size <
+ devpriv->dma_transfer_size) {
+ devpriv->dma_transfer_size =
+ devpriv->count * sample_size;
+ }
+ set_dma_count(devpriv->dma_chan, devpriv->dma_transfer_size);
+ enable_dma(devpriv->dma_chan);
+ release_dma_lock(irq_flags);
+ // enable board's dma
+ devpriv->command3_bits |= DMA_EN_BIT | DMATC_INTR_EN_BIT;
+ } else
+ devpriv->command3_bits &= ~DMA_EN_BIT & ~DMATC_INTR_EN_BIT;
+
+ // enable error interrupts
+ devpriv->command3_bits |= ERR_INTR_EN_BIT;
+ // enable fifo not empty interrupt?
+ if (xfer == fifo_not_empty_transfer)
+ devpriv->command3_bits |= ADC_FNE_INTR_EN_BIT;
+ else
+ devpriv->command3_bits &= ~ADC_FNE_INTR_EN_BIT;
+ devpriv->write_byte(devpriv->command3_bits, dev->iobase + COMMAND3_REG);
+
+ // startup aquisition
+
+ // command2 reg
+ // use 2 cascaded counters for pacing
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->command2_bits |= CASCADE_BIT;
+ switch (cmd->start_src) {
+ case TRIG_EXT:
+ devpriv->command2_bits |= HWTRIG_BIT;
+ devpriv->command2_bits &= ~PRETRIG_BIT & ~SWTRIG_BIT;
+ break;
+ case TRIG_NOW:
+ devpriv->command2_bits |= SWTRIG_BIT;
+ devpriv->command2_bits &= ~PRETRIG_BIT & ~HWTRIG_BIT;
+ break;
+ default:
+ comedi_error(dev, "bug with start_src");
+ return -1;
+ break;
+ }
+ switch (cmd->stop_src) {
+ case TRIG_EXT:
+ devpriv->command2_bits |= HWTRIG_BIT | PRETRIG_BIT;
+ break;
+ case TRIG_COUNT:
+ case TRIG_NONE:
+ break;
+ default:
+ comedi_error(dev, "bug with stop_src");
+ return -1;
+ }
+ devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return 0;
+}
+
+/* interrupt service routine */
+static irqreturn_t labpc_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async;
+ struct comedi_cmd *cmd;
+
+ if (dev->attached == 0) {
+ comedi_error(dev, "premature interrupt");
+ return IRQ_HANDLED;
+ }
+
+ async = s->async;
+ cmd = &async->cmd;
+ async->events = 0;
+
+ // read board status
+ devpriv->status1_bits = devpriv->read_byte(dev->iobase + STATUS1_REG);
+ if (thisboard->register_layout == labpc_1200_layout)
+ devpriv->status2_bits =
+ devpriv->read_byte(dev->iobase + STATUS2_REG);
+
+ if ((devpriv->status1_bits & (DMATC_BIT | TIMER_BIT | OVERFLOW_BIT |
+ OVERRUN_BIT | DATA_AVAIL_BIT)) == 0
+ && (devpriv->status2_bits & A1_TC_BIT) == 0
+ && (devpriv->status2_bits & FNHF_BIT)) {
+ return IRQ_NONE;
+ }
+
+ if (devpriv->status1_bits & OVERRUN_BIT) {
+ // clear error interrupt
+ devpriv->write_byte(0x1, dev->iobase + ADC_CLEAR_REG);
+ async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ comedi_error(dev, "overrun");
+ return IRQ_HANDLED;
+ }
+
+ if (devpriv->current_transfer == isa_dma_transfer) {
+ // if a dma terminal count of external stop trigger has occurred
+ if (devpriv->status1_bits & DMATC_BIT ||
+ (thisboard->register_layout == labpc_1200_layout
+ && devpriv->status2_bits & A1_TC_BIT)) {
+ handle_isa_dma(dev);
+ }
+ } else
+ labpc_drain_fifo(dev);
+
+ if (devpriv->status1_bits & TIMER_BIT) {
+ comedi_error(dev, "handled timer interrupt?");
+ // clear it
+ devpriv->write_byte(0x1, dev->iobase + TIMER_CLEAR_REG);
+ }
+
+ if (devpriv->status1_bits & OVERFLOW_BIT) {
+ // clear error interrupt
+ devpriv->write_byte(0x1, dev->iobase + ADC_CLEAR_REG);
+ async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ comedi_error(dev, "overflow");
+ return IRQ_HANDLED;
+ }
+ // handle external stop trigger
+ if (cmd->stop_src == TRIG_EXT) {
+ if (devpriv->status2_bits & A1_TC_BIT) {
+ labpc_drain_dregs(dev);
+ labpc_cancel(dev, s);
+ async->events |= COMEDI_CB_EOA;
+ }
+ }
+
+ /* TRIG_COUNT end of acquisition */
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (devpriv->count == 0) {
+ labpc_cancel(dev, s);
+ async->events |= COMEDI_CB_EOA;
+ }
+ }
+
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+}
+
+// read all available samples from ai fifo
+static int labpc_drain_fifo(struct comedi_device * dev)
+{
+ unsigned int lsb, msb;
+ short data;
+ struct comedi_async *async = dev->read_subdev->async;
+ const int timeout = 10000;
+ unsigned int i;
+
+ devpriv->status1_bits = devpriv->read_byte(dev->iobase + STATUS1_REG);
+
+ for (i = 0; (devpriv->status1_bits & DATA_AVAIL_BIT) && i < timeout;
+ i++) {
+ // quit if we have all the data we want
+ if (async->cmd.stop_src == TRIG_COUNT) {
+ if (devpriv->count == 0)
+ break;
+ devpriv->count--;
+ }
+ lsb = devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
+ msb = devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
+ data = (msb << 8) | lsb;
+ cfc_write_to_buffer(dev->read_subdev, data);
+ devpriv->status1_bits =
+ devpriv->read_byte(dev->iobase + STATUS1_REG);
+ }
+ if (i == timeout) {
+ comedi_error(dev, "ai timeout, fifo never empties");
+ async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ return -1;
+ }
+
+ return 0;
+}
+
+static void labpc_drain_dma(struct comedi_device * dev)
+{
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ int status;
+ unsigned long flags;
+ unsigned int max_points, num_points, residue, leftover;
+ int i;
+
+ status = devpriv->status1_bits;
+
+ flags = claim_dma_lock();
+ disable_dma(devpriv->dma_chan);
+ /* clear flip-flop to make sure 2-byte registers for
+ * count and address get set correctly */
+ clear_dma_ff(devpriv->dma_chan);
+
+ // figure out how many points to read
+ max_points = devpriv->dma_transfer_size / sample_size;
+ /* residue is the number of points left to be done on the dma
+ * transfer. It should always be zero at this point unless
+ * the stop_src is set to external triggering.
+ */
+ residue = get_dma_residue(devpriv->dma_chan) / sample_size;
+ num_points = max_points - residue;
+ if (devpriv->count < num_points && async->cmd.stop_src == TRIG_COUNT)
+ num_points = devpriv->count;
+
+ // figure out how many points will be stored next time
+ leftover = 0;
+ if (async->cmd.stop_src != TRIG_COUNT) {
+ leftover = devpriv->dma_transfer_size / sample_size;
+ } else if (devpriv->count > num_points) {
+ leftover = devpriv->count - num_points;
+ if (leftover > max_points)
+ leftover = max_points;
+ }
+
+ /* write data to comedi buffer */
+ for (i = 0; i < num_points; i++) {
+ cfc_write_to_buffer(s, devpriv->dma_buffer[i]);
+ }
+ if (async->cmd.stop_src == TRIG_COUNT)
+ devpriv->count -= num_points;
+
+ // set address and count for next transfer
+ set_dma_addr(devpriv->dma_chan, virt_to_bus(devpriv->dma_buffer));
+ set_dma_count(devpriv->dma_chan, leftover * sample_size);
+ release_dma_lock(flags);
+
+ async->events |= COMEDI_CB_BLOCK;
+}
+
+static void handle_isa_dma(struct comedi_device * dev)
+{
+ labpc_drain_dma(dev);
+
+ enable_dma(devpriv->dma_chan);
+
+ // clear dma tc interrupt
+ devpriv->write_byte(0x1, dev->iobase + DMATC_CLEAR_REG);
+}
+
+/* makes sure all data aquired by board is transfered to comedi (used
+ * when aquisition is terminated by stop_src == TRIG_EXT). */
+static void labpc_drain_dregs(struct comedi_device * dev)
+{
+ if (devpriv->current_transfer == isa_dma_transfer)
+ labpc_drain_dma(dev);
+
+ labpc_drain_fifo(dev);
+}
+
+static int labpc_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i, n;
+ int chan, range;
+ int lsb, msb;
+ int timeout = 1000;
+ unsigned long flags;
+
+ // disable timed conversions
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->command2_bits &= ~SWTRIG_BIT & ~HWTRIG_BIT & ~PRETRIG_BIT;
+ devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ // disable interrupt generation and dma
+ devpriv->command3_bits = 0;
+ devpriv->write_byte(devpriv->command3_bits, dev->iobase + COMMAND3_REG);
+
+ /* set gain and channel */
+ devpriv->command1_bits = 0;
+ chan = CR_CHAN(insn->chanspec);
+ range = CR_RANGE(insn->chanspec);
+ devpriv->command1_bits |= thisboard->ai_range_code[range];
+ // munge channel bits for differential/scan disabled mode
+ if (CR_AREF(insn->chanspec) == AREF_DIFF)
+ chan *= 2;
+ devpriv->command1_bits |= ADC_CHAN_BITS(chan);
+ devpriv->write_byte(devpriv->command1_bits, dev->iobase + COMMAND1_REG);
+
+ // setup command6 register for 1200 boards
+ if (thisboard->register_layout == labpc_1200_layout) {
+ // reference inputs to ground or common?
+ if (CR_AREF(insn->chanspec) != AREF_GROUND)
+ devpriv->command6_bits |= ADC_COMMON_BIT;
+ else
+ devpriv->command6_bits &= ~ADC_COMMON_BIT;
+ // bipolar or unipolar range?
+ if (thisboard->ai_range_is_unipolar[range])
+ devpriv->command6_bits |= ADC_UNIP_BIT;
+ else
+ devpriv->command6_bits &= ~ADC_UNIP_BIT;
+ // don't interrupt on fifo half full
+ devpriv->command6_bits &= ~ADC_FHF_INTR_EN_BIT;
+ // don't enable interrupt on counter a1 terminal count?
+ devpriv->command6_bits &= ~A1_INTR_EN_BIT;
+ // write to register
+ devpriv->write_byte(devpriv->command6_bits,
+ dev->iobase + COMMAND6_REG);
+ }
+ // setup command4 register
+ devpriv->command4_bits = 0;
+ devpriv->command4_bits |= EXT_CONVERT_DISABLE_BIT;
+ // single-ended/differential
+ if (CR_AREF(insn->chanspec) == AREF_DIFF)
+ devpriv->command4_bits |= ADC_DIFF_BIT;
+ devpriv->write_byte(devpriv->command4_bits, dev->iobase + COMMAND4_REG);
+
+ // initialize pacer counter output to make sure it doesn't cause any problems
+ devpriv->write_byte(INIT_A0_BITS, dev->iobase + COUNTER_A_CONTROL_REG);
+
+ labpc_clear_adc_fifo(dev);
+
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion */
+ devpriv->write_byte(0x1, dev->iobase + ADC_CONVERT_REG);
+
+ for (i = 0; i < timeout; i++) {
+ if (devpriv->read_byte(dev->iobase +
+ STATUS1_REG) & DATA_AVAIL_BIT)
+ break;
+ comedi_udelay(1);
+ }
+ if (i == timeout) {
+ comedi_error(dev, "timeout");
+ return -ETIME;
+ }
+ lsb = devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
+ msb = devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
+ data[n] = (msb << 8) | lsb;
+ }
+
+ return n;
+}
+
+// analog output insn
+static int labpc_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int channel, range;
+ unsigned long flags;
+ int lsb, msb;
+
+ channel = CR_CHAN(insn->chanspec);
+
+ // turn off pacing of analog output channel
+ /* note: hardware bug in daqcard-1200 means pacing cannot
+ * be independently enabled/disabled for its the two channels */
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->command2_bits &= ~DAC_PACED_BIT(channel);
+ devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ // set range
+ if (thisboard->register_layout == labpc_1200_layout) {
+ range = CR_RANGE(insn->chanspec);
+ if (range & AO_RANGE_IS_UNIPOLAR)
+ devpriv->command6_bits |= DAC_UNIP_BIT(channel);
+ else
+ devpriv->command6_bits &= ~DAC_UNIP_BIT(channel);
+ // write to register
+ devpriv->write_byte(devpriv->command6_bits,
+ dev->iobase + COMMAND6_REG);
+ }
+ // send data
+ lsb = data[0] & 0xff;
+ msb = (data[0] >> 8) & 0xff;
+ devpriv->write_byte(lsb, dev->iobase + DAC_LSB_REG(channel));
+ devpriv->write_byte(msb, dev->iobase + DAC_MSB_REG(channel));
+
+ // remember value for readback
+ devpriv->ao_value[channel] = data[0];
+
+ return 1;
+}
+
+// analog output readback insn
+static int labpc_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = devpriv->ao_value[CR_CHAN(insn->chanspec)];
+
+ return 1;
+}
+
+static int labpc_calib_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = devpriv->caldac[CR_CHAN(insn->chanspec)];
+
+ return 1;
+}
+
+static int labpc_calib_write_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int channel = CR_CHAN(insn->chanspec);
+
+ write_caldac(dev, channel, data[0]);
+ return 1;
+}
+
+static int labpc_eeprom_read_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = devpriv->eeprom_data[CR_CHAN(insn->chanspec)];
+
+ return 1;
+}
+
+static int labpc_eeprom_write_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int channel = CR_CHAN(insn->chanspec);
+ int ret;
+
+ // only allow writes to user area of eeprom
+ if (channel < 16 || channel > 127) {
+ printk("eeprom writes are only allowed to channels 16 through 127 (the pointer and user areas)");
+ return -EINVAL;
+ }
+
+ ret = labpc_eeprom_write(dev, channel, data[0]);
+ if (ret < 0)
+ return ret;
+
+ return 1;
+}
+
+// utility function that suggests a dma transfer size in bytes
+static unsigned int labpc_suggest_transfer_size(struct comedi_cmd cmd)
+{
+ unsigned int size;
+ unsigned int freq;
+
+ if (cmd.convert_src == TRIG_TIMER)
+ freq = 1000000000 / cmd.convert_arg;
+ // return some default value
+ else
+ freq = 0xffffffff;
+
+ // make buffer fill in no more than 1/3 second
+ size = (freq / 3) * sample_size;
+
+ // set a minimum and maximum size allowed
+ if (size > dma_buffer_size)
+ size = dma_buffer_size - dma_buffer_size % sample_size;
+ else if (size < sample_size)
+ size = sample_size;
+
+ return size;
+}
+
+// figures out what counter values to use based on command
+static void labpc_adc_timing(struct comedi_device * dev, struct comedi_cmd * cmd)
+{
+ const int max_counter_value = 0x10000; // max value for 16 bit counter in mode 2
+ const int min_counter_value = 2; // min value for 16 bit counter in mode 2
+ unsigned int base_period;
+
+ // if both convert and scan triggers are TRIG_TIMER, then they both rely on counter b0
+ if (labpc_ai_convert_period(cmd) && labpc_ai_scan_period(cmd)) {
+ // pick the lowest b0 divisor value we can (for maximum input clock speed on convert and scan counters)
+ devpriv->divisor_b0 = (labpc_ai_scan_period(cmd) - 1) /
+ (LABPC_TIMER_BASE * max_counter_value) + 1;
+ if (devpriv->divisor_b0 < min_counter_value)
+ devpriv->divisor_b0 = min_counter_value;
+ if (devpriv->divisor_b0 > max_counter_value)
+ devpriv->divisor_b0 = max_counter_value;
+
+ base_period = LABPC_TIMER_BASE * devpriv->divisor_b0;
+
+ // set a0 for conversion frequency and b1 for scan frequency
+ switch (cmd->flags & TRIG_ROUND_MASK) {
+ default:
+ case TRIG_ROUND_NEAREST:
+ devpriv->divisor_a0 =
+ (labpc_ai_convert_period(cmd) +
+ (base_period / 2)) / base_period;
+ devpriv->divisor_b1 =
+ (labpc_ai_scan_period(cmd) +
+ (base_period / 2)) / base_period;
+ break;
+ case TRIG_ROUND_UP:
+ devpriv->divisor_a0 =
+ (labpc_ai_convert_period(cmd) + (base_period -
+ 1)) / base_period;
+ devpriv->divisor_b1 =
+ (labpc_ai_scan_period(cmd) + (base_period -
+ 1)) / base_period;
+ break;
+ case TRIG_ROUND_DOWN:
+ devpriv->divisor_a0 =
+ labpc_ai_convert_period(cmd) / base_period;
+ devpriv->divisor_b1 =
+ labpc_ai_scan_period(cmd) / base_period;
+ break;
+ }
+ // make sure a0 and b1 values are acceptable
+ if (devpriv->divisor_a0 < min_counter_value)
+ devpriv->divisor_a0 = min_counter_value;
+ if (devpriv->divisor_a0 > max_counter_value)
+ devpriv->divisor_a0 = max_counter_value;
+ if (devpriv->divisor_b1 < min_counter_value)
+ devpriv->divisor_b1 = min_counter_value;
+ if (devpriv->divisor_b1 > max_counter_value)
+ devpriv->divisor_b1 = max_counter_value;
+ // write corrected timings to command
+ labpc_set_ai_convert_period(cmd,
+ base_period * devpriv->divisor_a0);
+ labpc_set_ai_scan_period(cmd,
+ base_period * devpriv->divisor_b1);
+ // if only one TRIG_TIMER is used, we can employ the generic cascaded timing functions
+ } else if (labpc_ai_scan_period(cmd)) {
+ unsigned int scan_period;
+
+ scan_period = labpc_ai_scan_period(cmd);
+ /* calculate cascaded counter values that give desired scan timing */
+ i8253_cascade_ns_to_timer_2div(LABPC_TIMER_BASE,
+ &(devpriv->divisor_b1), &(devpriv->divisor_b0),
+ &scan_period, cmd->flags & TRIG_ROUND_MASK);
+ labpc_set_ai_scan_period(cmd, scan_period);
+ } else if (labpc_ai_convert_period(cmd)) {
+ unsigned int convert_period;
+
+ convert_period = labpc_ai_convert_period(cmd);
+ /* calculate cascaded counter values that give desired conversion timing */
+ i8253_cascade_ns_to_timer_2div(LABPC_TIMER_BASE,
+ &(devpriv->divisor_a0), &(devpriv->divisor_b0),
+ &convert_period, cmd->flags & TRIG_ROUND_MASK);
+ labpc_set_ai_convert_period(cmd, convert_period);
+ }
+}
+
+static int labpc_dio_mem_callback(int dir, int port, int data,
+ unsigned long iobase)
+{
+ if (dir) {
+ writeb(data, (void *)(iobase + port));
+ return 0;
+ } else {
+ return readb((void *)(iobase + port));
+ }
+}
+
+// lowlevel write to eeprom/dac
+static void labpc_serial_out(struct comedi_device * dev, unsigned int value,
+ unsigned int value_width)
+{
+ int i;
+
+ for (i = 1; i <= value_width; i++) {
+ // clear serial clock
+ devpriv->command5_bits &= ~SCLOCK_BIT;
+ // send bits most significant bit first
+ if (value & (1 << (value_width - i)))
+ devpriv->command5_bits |= SDATA_BIT;
+ else
+ devpriv->command5_bits &= ~SDATA_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits,
+ dev->iobase + COMMAND5_REG);
+ // set clock to load bit
+ devpriv->command5_bits |= SCLOCK_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits,
+ dev->iobase + COMMAND5_REG);
+ }
+}
+
+// lowlevel read from eeprom
+static unsigned int labpc_serial_in(struct comedi_device * dev)
+{
+ unsigned int value = 0;
+ int i;
+ const int value_width = 8; // number of bits wide values are
+
+ for (i = 1; i <= value_width; i++) {
+ // set serial clock
+ devpriv->command5_bits |= SCLOCK_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits,
+ dev->iobase + COMMAND5_REG);
+ // clear clock bit
+ devpriv->command5_bits &= ~SCLOCK_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits,
+ dev->iobase + COMMAND5_REG);
+ // read bits most significant bit first
+ comedi_udelay(1);
+ devpriv->status2_bits =
+ devpriv->read_byte(dev->iobase + STATUS2_REG);
+ if (devpriv->status2_bits & EEPROM_OUT_BIT) {
+ value |= 1 << (value_width - i);
+ }
+ }
+
+ return value;
+}
+
+static unsigned int labpc_eeprom_read(struct comedi_device * dev, unsigned int address)
+{
+ unsigned int value;
+ const int read_instruction = 0x3; // bits to tell eeprom to expect a read
+ const int write_length = 8; // 8 bit write lengths to eeprom
+
+ // enable read/write to eeprom
+ devpriv->command5_bits &= ~EEPROM_EN_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+ devpriv->command5_bits |= EEPROM_EN_BIT | EEPROM_WRITE_UNPROTECT_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+ // send read instruction
+ labpc_serial_out(dev, read_instruction, write_length);
+ // send 8 bit address to read from
+ labpc_serial_out(dev, address, write_length);
+ // read result
+ value = labpc_serial_in(dev);
+
+ // disable read/write to eeprom
+ devpriv->command5_bits &= ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+ return value;
+}
+
+static unsigned int labpc_eeprom_write(struct comedi_device * dev,
+ unsigned int address, unsigned int value)
+{
+ const int write_enable_instruction = 0x6;
+ const int write_instruction = 0x2;
+ const int write_length = 8; // 8 bit write lengths to eeprom
+ const int write_in_progress_bit = 0x1;
+ const int timeout = 10000;
+ int i;
+
+ // make sure there isn't already a write in progress
+ for (i = 0; i < timeout; i++) {
+ if ((labpc_eeprom_read_status(dev) & write_in_progress_bit) ==
+ 0)
+ break;
+ }
+ if (i == timeout) {
+ comedi_error(dev, "eeprom write timed out");
+ return -ETIME;
+ }
+ // update software copy of eeprom
+ devpriv->eeprom_data[address] = value;
+
+ // enable read/write to eeprom
+ devpriv->command5_bits &= ~EEPROM_EN_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+ devpriv->command5_bits |= EEPROM_EN_BIT | EEPROM_WRITE_UNPROTECT_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+ // send write_enable instruction
+ labpc_serial_out(dev, write_enable_instruction, write_length);
+ devpriv->command5_bits &= ~EEPROM_EN_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+ // send write instruction
+ devpriv->command5_bits |= EEPROM_EN_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+ labpc_serial_out(dev, write_instruction, write_length);
+ // send 8 bit address to write to
+ labpc_serial_out(dev, address, write_length);
+ // write value
+ labpc_serial_out(dev, value, write_length);
+ devpriv->command5_bits &= ~EEPROM_EN_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+ // disable read/write to eeprom
+ devpriv->command5_bits &= ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+ return 0;
+}
+
+static unsigned int labpc_eeprom_read_status(struct comedi_device * dev)
+{
+ unsigned int value;
+ const int read_status_instruction = 0x5;
+ const int write_length = 8; // 8 bit write lengths to eeprom
+
+ // enable read/write to eeprom
+ devpriv->command5_bits &= ~EEPROM_EN_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+ devpriv->command5_bits |= EEPROM_EN_BIT | EEPROM_WRITE_UNPROTECT_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+ // send read status instruction
+ labpc_serial_out(dev, read_status_instruction, write_length);
+ // read result
+ value = labpc_serial_in(dev);
+
+ // disable read/write to eeprom
+ devpriv->command5_bits &= ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+ return value;
+}
+
+// writes to 8 bit calibration dacs
+static void write_caldac(struct comedi_device * dev, unsigned int channel,
+ unsigned int value)
+{
+ if (value == devpriv->caldac[channel])
+ return;
+ devpriv->caldac[channel] = value;
+
+ // clear caldac load bit and make sure we don't write to eeprom
+ devpriv->command5_bits &=
+ ~CALDAC_LOAD_BIT & ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+
+ // write 4 bit channel
+ labpc_serial_out(dev, channel, 4);
+ // write 8 bit caldac value
+ labpc_serial_out(dev, value, 8);
+
+ // set and clear caldac bit to load caldac value
+ devpriv->command5_bits |= CALDAC_LOAD_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+ devpriv->command5_bits &= ~CALDAC_LOAD_BIT;
+ comedi_udelay(1);
+ devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
+}
+
+#ifdef CONFIG_COMEDI_PCI
+COMEDI_PCI_INITCLEANUP(driver_labpc, labpc_pci_table);
+#else
+COMEDI_INITCLEANUP(driver_labpc);
+#endif
+
+EXPORT_SYMBOL_GPL(labpc_common_attach);
+EXPORT_SYMBOL_GPL(labpc_common_detach);
+EXPORT_SYMBOL_GPL(range_labpc_1200_ai);
+EXPORT_SYMBOL_GPL(labpc_1200_ai_gain_bits);
+EXPORT_SYMBOL_GPL(labpc_1200_is_unipolar);
diff --git a/drivers/staging/comedi/drivers/ni_labpc.h b/drivers/staging/comedi/drivers/ni_labpc.h
new file mode 100644
index 000000000000..b44b0d3d02f1
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_labpc.h
@@ -0,0 +1,85 @@
+/*
+ ni_labpc.h
+
+ Header for ni_labpc.c and ni_labpc_cs.c
+
+ Copyright (C) 2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ 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 _NI_LABPC_H
+#define _NI_LABPC_H
+
+#define EEPROM_SIZE 256 // 256 byte eeprom
+#define NUM_AO_CHAN 2 // boards have two analog output channels
+
+enum labpc_bustype { isa_bustype, pci_bustype, pcmcia_bustype };
+enum labpc_register_layout { labpc_plus_layout, labpc_1200_layout };
+enum transfer_type { fifo_not_empty_transfer, fifo_half_full_transfer,
+ isa_dma_transfer };
+
+typedef struct labpc_board_struct {
+ const char *name;
+ int device_id; // device id for pci and pcmcia boards
+ int ai_speed; // maximum input speed in nanoseconds
+ enum labpc_bustype bustype; // ISA/PCI/etc.
+ enum labpc_register_layout register_layout; // 1200 has extra registers compared to pc+
+ int has_ao; // has analog output true/false
+ const struct comedi_lrange *ai_range_table;
+ const int *ai_range_code;
+ const int *ai_range_is_unipolar;
+ unsigned ai_scan_up:1; // board can auto scan up in ai channels, not just down
+ unsigned memory_mapped_io:1; /* uses memory mapped io instead of ioports */
+} labpc_board;
+
+typedef struct {
+ struct mite_struct *mite; // for mite chip on pci-1200
+ volatile unsigned long long count; /* number of data points left to be taken */
+ unsigned int ao_value[NUM_AO_CHAN]; // software copy of analog output values
+ // software copys of bits written to command registers
+ volatile unsigned int command1_bits;
+ volatile unsigned int command2_bits;
+ volatile unsigned int command3_bits;
+ volatile unsigned int command4_bits;
+ volatile unsigned int command5_bits;
+ volatile unsigned int command6_bits;
+ // store last read of board status registers
+ volatile unsigned int status1_bits;
+ volatile unsigned int status2_bits;
+ unsigned int divisor_a0; /* value to load into board's counter a0 (conversion pacing) for timed conversions */
+ unsigned int divisor_b0; /* value to load into board's counter b0 (master) for timed conversions */
+ unsigned int divisor_b1; /* value to load into board's counter b1 (scan pacing) for timed conversions */
+ unsigned int dma_chan; // dma channel to use
+ u16 *dma_buffer; // buffer ai will dma into
+ unsigned int dma_transfer_size; // transfer size in bytes for current transfer
+ enum transfer_type current_transfer; // we are using dma/fifo-half-full/etc.
+ unsigned int eeprom_data[EEPROM_SIZE]; // stores contents of board's eeprom
+ unsigned int caldac[16]; // stores settings of calibration dacs
+ // function pointers so we can use inb/outb or readb/writeb as appropriate
+ unsigned int (*read_byte) (unsigned long address);
+ void (*write_byte) (unsigned int byte, unsigned long address);
+} labpc_private;
+
+int labpc_common_attach(struct comedi_device * dev, unsigned long iobase,
+ unsigned int irq, unsigned int dma);
+int labpc_common_detach(struct comedi_device * dev);
+
+extern const int labpc_1200_is_unipolar[];
+extern const int labpc_1200_ai_gain_bits[];
+extern const struct comedi_lrange range_labpc_1200_ai;
+
+#endif /* _NI_LABPC_H */
diff --git a/drivers/staging/comedi/drivers/ni_labpc_cs.c b/drivers/staging/comedi/drivers/ni_labpc_cs.c
new file mode 100644
index 000000000000..ac0ce2f58f5f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_labpc_cs.c
@@ -0,0 +1,573 @@
+/*
+ comedi/drivers/ni_labpc_cs.c
+ Driver for National Instruments daqcard-1200 boards
+ Copyright (C) 2001, 2002, 2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ PCMCIA crap is adapted from dummy_cs.c 1.31 2001/08/24 12:13:13
+ from the pcmcia package.
+ The initial developer of the pcmcia dummy_cs.c code is David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds.
+
+ 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.
+
+************************************************************************
+*/
+/*
+Driver: ni_labpc_cs
+Description: National Instruments Lab-PC (& compatibles)
+Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+Devices: [National Instruments] DAQCard-1200 (daqcard-1200)
+Status: works
+
+Thanks go to Fredrik Lingvall for much testing and perseverance in
+helping to debug daqcard-1200 support.
+
+The 1200 series boards have onboard calibration dacs for correcting
+analog input/output offsets and gains. The proper settings for these
+caldacs are stored on the board's eeprom. To read the caldac values
+from the eeprom and store them into a file that can be then be used by
+comedilib, use the comedi_calibrate program.
+
+Configuration options:
+ none
+
+The daqcard-1200 has quirky chanlist requirements
+when scanning multiple channels. Multiple channel scan
+sequence must start at highest channel, then decrement down to
+channel 0. Chanlists consisting of all one channel
+are also legal, and allow you to pace conversions in bursts.
+
+*/
+
+/*
+
+NI manuals:
+340988a (daqcard-1200)
+
+*/
+
+#undef LABPC_DEBUG
+//#define LABPC_DEBUG // enable debugging messages
+
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+
+#include "8253.h"
+#include "8255.h"
+#include "comedi_fc.h"
+#include "ni_labpc.h"
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+static struct pcmcia_device *pcmcia_cur_dev = NULL;
+
+static int labpc_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+
+static const labpc_board labpc_cs_boards[] = {
+ {
+ name: "daqcard-1200",
+ device_id:0x103, // 0x10b is manufacturer id, 0x103 is device id
+ ai_speed:10000,
+ bustype: pcmcia_bustype,
+ register_layout:labpc_1200_layout,
+ has_ao: 1,
+ ai_range_table:&range_labpc_1200_ai,
+ ai_range_code:labpc_1200_ai_gain_bits,
+ ai_range_is_unipolar:labpc_1200_is_unipolar,
+ ai_scan_up:0,
+ memory_mapped_io:0,
+ },
+ /* duplicate entry, to support using alternate name */
+ {
+ name: "ni_labpc_cs",
+ device_id:0x103,
+ ai_speed:10000,
+ bustype: pcmcia_bustype,
+ register_layout:labpc_1200_layout,
+ has_ao: 1,
+ ai_range_table:&range_labpc_1200_ai,
+ ai_range_code:labpc_1200_ai_gain_bits,
+ ai_range_is_unipolar:labpc_1200_is_unipolar,
+ ai_scan_up:0,
+ memory_mapped_io:0,
+ },
+};
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const labpc_board *)dev->board_ptr)
+
+static struct comedi_driver driver_labpc_cs = {
+ .driver_name = "ni_labpc_cs",
+ .module = THIS_MODULE,
+ .attach = &labpc_attach,
+ .detach = &labpc_common_detach,
+ .num_names = sizeof(labpc_cs_boards) / sizeof(labpc_board),
+ .board_name = &labpc_cs_boards[0].name,
+ .offset = sizeof(labpc_board),
+};
+
+static int labpc_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ unsigned long iobase = 0;
+ unsigned int irq = 0;
+ struct pcmcia_device *link;
+
+ /* allocate and initialize dev->private */
+ if (alloc_private(dev, sizeof(labpc_private)) < 0)
+ return -ENOMEM;
+
+ // get base address, irq etc. based on bustype
+ switch (thisboard->bustype) {
+ case pcmcia_bustype:
+ link = pcmcia_cur_dev; /* XXX hack */
+ if (!link)
+ return -EIO;
+ iobase = link->io.BasePort1;
+ irq = link->irq.AssignedIRQ;
+ break;
+ default:
+ printk("bug! couldn't determine board type\n");
+ return -EINVAL;
+ break;
+ }
+ return labpc_common_attach(dev, iobase, irq, 0);
+}
+
+/*
+ All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
+ you do not define PCMCIA_DEBUG at all, all the debug code will be
+ left out. If you compile with PCMCIA_DEBUG=0, the debug code will
+ be present but disabled -- but it can then be enabled for specific
+ modules at load time with a 'pc_debug=#' option to insmod.
+*/
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0644);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static const char *version =
+ "ni_labpc.c, based on dummy_cs.c 1.31 2001/08/24 12:13:13";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/*
+ The event() function is this driver's Card Services event handler.
+ It will be called by Card Services when an appropriate card status
+ event is received. The config() and release() entry points are
+ used to configure or release a socket, in response to card
+ insertion and ejection events. They are invoked from the dummy
+ event handler.
+
+ Kernel version 2.6.16 upwards uses suspend() and resume() functions
+ instead of an event() function.
+*/
+
+static void labpc_config(struct pcmcia_device *link);
+static void labpc_release(struct pcmcia_device *link);
+static int labpc_cs_suspend(struct pcmcia_device *p_dev);
+static int labpc_cs_resume(struct pcmcia_device *p_dev);
+
+/*
+ The attach() and detach() entry points are used to create and destroy
+ "instances" of the driver, where each instance represents everything
+ needed to manage one actual PCMCIA card.
+*/
+
+static int labpc_cs_attach(struct pcmcia_device *);
+static void labpc_cs_detach(struct pcmcia_device *);
+
+/*
+ You'll also need to prototype all the functions that will actually
+ be used to talk to your device. See 'memory_cs' for a good example
+ of a fully self-sufficient driver; the other drivers rely more or
+ less on other parts of the kernel.
+*/
+
+/*
+ The dev_info variable is the "key" that is used to match up this
+ device driver with appropriate cards, through the card configuration
+ database.
+*/
+
+static const dev_info_t dev_info = "daqcard-1200";
+
+typedef struct local_info_t {
+ struct pcmcia_device *link;
+ dev_node_t node;
+ int stop;
+ struct bus_operations *bus;
+} local_info_t;
+
+/*======================================================================
+
+ labpc_cs_attach() creates an "instance" of the driver, allocating
+ local data structures for one device. The device is registered
+ with Card Services.
+
+ The dev_link structure is initialized, but we don't actually
+ configure the card at this point -- we wait until we receive a
+ card insertion event.
+
+======================================================================*/
+
+static int labpc_cs_attach(struct pcmcia_device *link)
+{
+ local_info_t *local;
+
+ DEBUG(0, "labpc_cs_attach()\n");
+
+ /* Allocate space for private device-specific data */
+ local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
+ if (!local)
+ return -ENOMEM;
+ local->link = link;
+ link->priv = local;
+
+ /* Interrupt setup */
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_FORCED_PULSE;
+ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_PULSE_ID;
+ link->irq.Handler = NULL;
+
+ /*
+ General socket configuration defaults can go here. In this
+ client, we assume very little, and rely on the CIS for almost
+ everything. In most clients, many details (i.e., number, sizes,
+ and attributes of IO windows) are fixed by the nature of the
+ device, and can be hard-wired here.
+ */
+ link->conf.Attributes = 0;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ pcmcia_cur_dev = link;
+
+ labpc_config(link);
+
+ return 0;
+} /* labpc_cs_attach */
+
+/*======================================================================
+
+ This deletes a driver "instance". The device is de-registered
+ with Card Services. If it has been released, all local data
+ structures are freed. Otherwise, the structures will be freed
+ when the device is released.
+
+======================================================================*/
+
+static void labpc_cs_detach(struct pcmcia_device *link)
+{
+ DEBUG(0, "labpc_cs_detach(0x%p)\n", link);
+
+ /*
+ If the device is currently configured and active, we won't
+ actually delete it yet. Instead, it is marked so that when
+ the release() function is called, that will trigger a proper
+ detach().
+ */
+ if (link->dev_node) {
+ ((local_info_t *) link->priv)->stop = 1;
+ labpc_release(link);
+ }
+
+ /* This points to the parent local_info_t struct */
+ if (link->priv)
+ kfree(link->priv);
+
+} /* labpc_cs_detach */
+
+/*======================================================================
+
+ labpc_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ device available to the system.
+
+======================================================================*/
+
+static void labpc_config(struct pcmcia_device *link)
+{
+ local_info_t *dev = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ int last_ret;
+ u_char buf[64];
+ win_req_t req;
+ memreq_t map;
+ cistpl_cftable_entry_t dflt = { 0 };
+
+ DEBUG(0, "labpc_config(0x%p)\n", link);
+
+ /*
+ This reads the card's CONFIG tuple to find its configuration
+ registers.
+ */
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ tuple.Attributes = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.TupleOffset = 0;
+ if ((last_ret = pcmcia_get_first_tuple(link, &tuple))) {
+ cs_error(link, GetFirstTuple, last_ret);
+ goto cs_failed;
+ }
+ if ((last_ret = pcmcia_get_tuple_data(link, &tuple))) {
+ cs_error(link, GetTupleData, last_ret);
+ goto cs_failed;
+ }
+ if ((last_ret = pcmcia_parse_tuple(&tuple, &parse))) {
+ cs_error(link, ParseTuple, last_ret);
+ goto cs_failed;
+ }
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+
+ /*
+ In this loop, we scan the CIS for configuration table entries,
+ each of which describes a valid card configuration, including
+ voltage, IO window, memory window, and interrupt settings.
+
+ We make no assumptions about the card to be configured: we use
+ just the information available in the CIS. In an ideal world,
+ this would work for any PCMCIA card, but it requires a complete
+ and accurate CIS. In practice, a driver usually "knows" most of
+ these things without consulting the CIS, and most client drivers
+ will only use the CIS to fill in implementation-defined details.
+ */
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ if ((last_ret = pcmcia_get_first_tuple(link, &tuple))) {
+ cs_error(link, GetFirstTuple, last_ret);
+ goto cs_failed;
+ }
+ while (1) {
+ cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
+ if (pcmcia_get_tuple_data(link, &tuple))
+ goto next_entry;
+ if (pcmcia_parse_tuple(&tuple, &parse))
+ goto next_entry;
+
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+ dflt = *cfg;
+ if (cfg->index == 0)
+ goto next_entry;
+ link->conf.ConfigIndex = cfg->index;
+
+ /* Does this card need audio output? */
+ if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ link->conf.Attributes |= CONF_ENABLE_SPKR;
+ link->conf.Status = CCSR_AUDIO_ENA;
+ }
+
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
+ link->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ link->io.NumPorts1 = link->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ link->io.BasePort1 = io->win[0].base;
+ link->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ link->io.Attributes2 = link->io.Attributes1;
+ link->io.BasePort2 = io->win[1].base;
+ link->io.NumPorts2 = io->win[1].len;
+ }
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(link, &link->io))
+ goto next_entry;
+ }
+
+ if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
+ cistpl_mem_t *mem =
+ (cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
+ req.Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM;
+ req.Attributes |= WIN_ENABLE;
+ req.Base = mem->win[0].host_addr;
+ req.Size = mem->win[0].len;
+ if (req.Size < 0x1000)
+ req.Size = 0x1000;
+ req.AccessSpeed = 0;
+ link->win = (window_handle_t) link;
+ if (pcmcia_request_window(&link, &req, &link->win))
+ goto next_entry;
+ map.Page = 0;
+ map.CardOffset = mem->win[0].card_addr;
+ if (pcmcia_map_mem_page(link->win, &map))
+ goto next_entry;
+ }
+ /* If we got this far, we're cool! */
+ break;
+
+ next_entry:
+ if ((last_ret = pcmcia_get_next_tuple(link, &tuple))) {
+ cs_error(link, GetNextTuple, last_ret);
+ goto cs_failed;
+ }
+ }
+
+ /*
+ Allocate an interrupt line. Note that this does not assign a
+ handler to the interrupt, unless the 'Handler' member of the
+ irq structure is initialized.
+ */
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ if ((last_ret = pcmcia_request_irq(link, &link->irq))) {
+ cs_error(link, RequestIRQ, last_ret);
+ goto cs_failed;
+ }
+
+ /*
+ This actually configures the PCMCIA socket -- setting up
+ the I/O windows and the interrupt mapping, and putting the
+ card and host interface into "Memory and IO" mode.
+ */
+ if ((last_ret = pcmcia_request_configuration(link, &link->conf))) {
+ cs_error(link, RequestConfiguration, last_ret);
+ goto cs_failed;
+ }
+
+ /*
+ At this point, the dev_node_t structure(s) need to be
+ initialized and arranged in a linked list at link->dev.
+ */
+ sprintf(dev->node.dev_name, "daqcard-1200");
+ dev->node.major = dev->node.minor = 0;
+ link->dev_node = &dev->node;
+
+ /* Finally, report what we've done */
+ printk(KERN_INFO "%s: index 0x%02x",
+ dev->node.dev_name, link->conf.ConfigIndex);
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ printk(", irq %d", link->irq.AssignedIRQ);
+ if (link->io.NumPorts1)
+ printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+ link->io.BasePort1 + link->io.NumPorts1 - 1);
+ if (link->io.NumPorts2)
+ printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+ link->io.BasePort2 + link->io.NumPorts2 - 1);
+ if (link->win)
+ printk(", mem 0x%06lx-0x%06lx", req.Base,
+ req.Base + req.Size - 1);
+ printk("\n");
+
+ return;
+
+ cs_failed:
+ labpc_release(link);
+
+} /* labpc_config */
+
+static void labpc_release(struct pcmcia_device *link)
+{
+ DEBUG(0, "labpc_release(0x%p)\n", link);
+
+ pcmcia_disable_device(link);
+} /* labpc_release */
+
+/*======================================================================
+
+ The card status event handler. Mostly, this schedules other
+ stuff to run after an event is received.
+
+ When a CARD_REMOVAL event is received, we immediately set a
+ private flag to block future accesses to this device. All the
+ functions that actually access the device should check this flag
+ to make sure the card is still present.
+
+======================================================================*/
+
+static int labpc_cs_suspend(struct pcmcia_device *link)
+{
+ local_info_t *local = link->priv;
+
+ /* Mark the device as stopped, to block IO until later */
+ local->stop = 1;
+ return 0;
+} /* labpc_cs_suspend */
+
+static int labpc_cs_resume(struct pcmcia_device *link)
+{
+ local_info_t *local = link->priv;
+
+ local->stop = 0;
+ return 0;
+} /* labpc_cs_resume */
+
+/*====================================================================*/
+
+static struct pcmcia_device_id labpc_cs_ids[] = {
+ /* N.B. These IDs should match those in labpc_cs_boards (ni_labpc.c) */
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0103), /* daqcard-1200 */
+ PCMCIA_DEVICE_NULL
+};
+
+MODULE_DEVICE_TABLE(pcmcia, labpc_cs_ids);
+
+struct pcmcia_driver labpc_cs_driver = {
+ .probe = labpc_cs_attach,
+ .remove = labpc_cs_detach,
+ .suspend = labpc_cs_suspend,
+ .resume = labpc_cs_resume,
+ .id_table = labpc_cs_ids,
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = dev_info,
+ },
+};
+
+static int __init init_labpc_cs(void)
+{
+ DEBUG(0, "%s\n", version);
+ pcmcia_register_driver(&labpc_cs_driver);
+ return 0;
+}
+
+static void __exit exit_labpc_cs(void)
+{
+ DEBUG(0, "ni_labpc: unloading\n");
+ pcmcia_unregister_driver(&labpc_cs_driver);
+}
+
+int __init labpc_init_module(void)
+{
+ int ret;
+
+ ret = init_labpc_cs();
+ if (ret < 0)
+ return ret;
+
+ return comedi_driver_register(&driver_labpc_cs);
+}
+
+void __exit labpc_exit_module(void)
+{
+ exit_labpc_cs();
+ comedi_driver_unregister(&driver_labpc_cs);
+}
+
+MODULE_LICENSE("GPL");
+module_init(labpc_init_module);
+module_exit(labpc_exit_module);
diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c
new file mode 100644
index 000000000000..542bd0dcd44d
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_mio_common.c
@@ -0,0 +1,5862 @@
+/*
+ comedi/drivers/ni_mio_common.c
+ Hardware driver for DAQ-STC based boards
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2001 David A. Schleef <ds@schleef.org>
+ Copyright (C) 2002-2006 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ 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 is meant to be included by another file, e.g.,
+ ni_atmio.c or ni_pcimio.c.
+
+ Interrupt support originally added by Truxton Fulton
+ <trux@truxton.com>
+
+ References (from ftp://ftp.natinst.com/support/manuals):
+
+ 340747b.pdf AT-MIO E series Register Level Programmer Manual
+ 341079b.pdf PCI E Series RLPM
+ 340934b.pdf DAQ-STC reference manual
+ 67xx and 611x registers (from http://www.ni.com/pdf/daq/us)
+ release_ni611x.pdf
+ release_ni67xx.pdf
+ 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:
+
+ - the interrupt routine needs to be cleaned up
+
+ 2006-02-07: S-Series PCI-6143: Support has been added but is not
+ fully tested as yet. Terry Barnaby, BEAM Ltd.
+*/
+
+//#define DEBUG_INTERRUPT
+//#define DEBUG_STATUS_A
+//#define DEBUG_STATUS_B
+
+#include "8255.h"
+#include "mite.h"
+#include "comedi_fc.h"
+
+#ifndef MDPRINTK
+#define MDPRINTK(format,args...)
+#endif
+
+/* A timeout count */
+#define NI_TIMEOUT 1000
+static const unsigned old_RTSI_clock_channel = 7;
+
+/* Note: this table must match the ai_gain_* definitions */
+static const short ni_gainlkup[][16] = {
+ [ai_gain_16] = {0, 1, 2, 3, 4, 5, 6, 7,
+ 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107},
+ [ai_gain_8] = {1, 2, 4, 7, 0x101, 0x102, 0x104, 0x107},
+ [ai_gain_14] = {1, 2, 3, 4, 5, 6, 7,
+ 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107},
+ [ai_gain_4] = {0, 1, 4, 7},
+ [ai_gain_611x] = {0x00a, 0x00b, 0x001, 0x002,
+ 0x003, 0x004, 0x005, 0x006},
+ [ai_gain_622x] = {0, 1, 4, 5},
+ [ai_gain_628x] = {1, 2, 3, 4, 5, 6, 7},
+ [ai_gain_6143] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+};
+
+static const struct comedi_lrange range_ni_E_ai = { 16, {
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ RANGE(-2.5, 2.5),
+ RANGE(-1, 1),
+ RANGE(-0.5, 0.5),
+ RANGE(-0.25, 0.25),
+ RANGE(-0.1, 0.1),
+ RANGE(-0.05, 0.05),
+ RANGE(0, 20),
+ RANGE(0, 10),
+ RANGE(0, 5),
+ RANGE(0, 2),
+ RANGE(0, 1),
+ RANGE(0, 0.5),
+ RANGE(0, 0.2),
+ RANGE(0, 0.1),
+ }
+};
+static const struct comedi_lrange range_ni_E_ai_limited = { 8, {
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ RANGE(-1, 1),
+ RANGE(-0.1, 0.1),
+ RANGE(0, 10),
+ RANGE(0, 5),
+ RANGE(0, 1),
+ RANGE(0, 0.1),
+ }
+};
+static const struct comedi_lrange range_ni_E_ai_limited14 = { 14, {
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ RANGE(-2, 2),
+ RANGE(-1, 1),
+ RANGE(-0.5, 0.5),
+ RANGE(-0.2, 0.2),
+ RANGE(-0.1, 0.1),
+ RANGE(0, 10),
+ RANGE(0, 5),
+ RANGE(0, 2),
+ RANGE(0, 1),
+ RANGE(0, 0.5),
+ RANGE(0, 0.2),
+ RANGE(0, 0.1),
+ }
+};
+static const struct comedi_lrange range_ni_E_ai_bipolar4 = { 4, {
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ RANGE(-0.5, 0.5),
+ RANGE(-0.05, 0.05),
+ }
+};
+static const struct comedi_lrange range_ni_E_ai_611x = { 8, {
+ RANGE(-50, 50),
+ RANGE(-20, 20),
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ RANGE(-2, 2),
+ RANGE(-1, 1),
+ RANGE(-0.5, 0.5),
+ RANGE(-0.2, 0.2),
+ }
+};
+static const struct comedi_lrange range_ni_M_ai_622x = { 4, {
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ RANGE(-1, 1),
+ RANGE(-0.2, 0.2),
+ }
+};
+static const struct comedi_lrange range_ni_M_ai_628x = { 7, {
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ RANGE(-2, 2),
+ RANGE(-1, 1),
+ RANGE(-0.5, 0.5),
+ RANGE(-0.2, 0.2),
+ RANGE(-0.1, 0.1),
+ }
+};
+static const struct comedi_lrange range_ni_S_ai_6143 = { 1, {
+ RANGE(-5, +5),
+ }
+};
+static const struct comedi_lrange range_ni_E_ao_ext = { 4, {
+ RANGE(-10, 10),
+ RANGE(0, 10),
+ RANGE_ext(-1, 1),
+ RANGE_ext(0, 1),
+ }
+};
+
+static const struct comedi_lrange *const ni_range_lkup[] = {
+ [ai_gain_16] = &range_ni_E_ai,
+ [ai_gain_8] = &range_ni_E_ai_limited,
+ [ai_gain_14] = &range_ni_E_ai_limited14,
+ [ai_gain_4] = &range_ni_E_ai_bipolar4,
+ [ai_gain_611x] = &range_ni_E_ai_611x,
+ [ai_gain_622x] = &range_ni_M_ai_622x,
+ [ai_gain_628x] = &range_ni_M_ai_628x,
+ [ai_gain_6143] = &range_ni_S_ai_6143
+};
+
+static int ni_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ni_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ni_cdio_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int ni_cdio_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int ni_cdio_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static void handle_cdio_interrupt(struct comedi_device * dev);
+static int ni_cdo_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trignum);
+
+static int ni_serial_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ni_serial_hw_readwrite8(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned char data_out, unsigned char *data_in);
+static int ni_serial_sw_readwrite8(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned char data_out, unsigned char *data_in);
+
+static int ni_calib_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ni_calib_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int ni_eeprom_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ni_m_series_eeprom_insn_read(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data);
+
+static int ni_pfi_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ni_pfi_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static unsigned ni_old_get_pfi_routing(struct comedi_device * dev, unsigned chan);
+
+static void ni_rtsi_init(struct comedi_device * dev);
+static int ni_rtsi_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ni_rtsi_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static void caldac_setup(struct comedi_device * dev, struct comedi_subdevice * s);
+static int ni_read_eeprom(struct comedi_device * dev, int addr);
+
+#ifdef DEBUG_STATUS_A
+static void ni_mio_print_status_a(int status);
+#else
+#define ni_mio_print_status_a(a)
+#endif
+#ifdef DEBUG_STATUS_B
+static void ni_mio_print_status_b(int status);
+#else
+#define ni_mio_print_status_b(a)
+#endif
+
+static int ni_ai_reset(struct comedi_device * dev, struct comedi_subdevice * s);
+#ifndef PCIDMA
+static void ni_handle_fifo_half_full(struct comedi_device * dev);
+static int ni_ao_fifo_half_empty(struct comedi_device * dev, struct comedi_subdevice * s);
+#endif
+static void ni_handle_fifo_dregs(struct comedi_device * dev);
+static int ni_ai_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trignum);
+static void ni_load_channelgain_list(struct comedi_device * dev, unsigned int n_chan,
+ unsigned int *list);
+static void shutdown_ai_command(struct comedi_device * dev);
+
+static int ni_ao_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trignum);
+
+static int ni_ao_reset(struct comedi_device * dev, struct comedi_subdevice * s);
+
+static int ni_8255_callback(int dir, int port, int data, unsigned long arg);
+
+static int ni_gpct_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ni_gpct_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ni_gpct_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ni_gpct_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int ni_gpct_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int ni_gpct_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static void handle_gpct_interrupt(struct comedi_device * dev,
+ unsigned short counter_index);
+
+static int init_cs5529(struct comedi_device * dev);
+static int cs5529_do_conversion(struct comedi_device * dev, unsigned short *data);
+static int cs5529_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+#ifdef NI_CS5529_DEBUG
+static unsigned int cs5529_config_read(struct comedi_device * dev,
+ unsigned int reg_select_bits);
+#endif
+static void cs5529_config_write(struct comedi_device * dev, unsigned int value,
+ unsigned int reg_select_bits);
+
+static int ni_m_series_pwm_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ni_6143_pwm_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int ni_set_master_clock(struct comedi_device * dev, unsigned source,
+ unsigned period_ns);
+static void ack_a_interrupt(struct comedi_device * dev, unsigned short a_status);
+static void ack_b_interrupt(struct comedi_device * dev, unsigned short b_status);
+
+enum aimodes {
+ AIMODE_NONE = 0,
+ AIMODE_HALF_FULL = 1,
+ AIMODE_SCAN = 2,
+ AIMODE_SAMPLE = 3,
+};
+
+enum ni_common_subdevices {
+ NI_AI_SUBDEV,
+ NI_AO_SUBDEV,
+ NI_DIO_SUBDEV,
+ NI_8255_DIO_SUBDEV,
+ NI_UNUSED_SUBDEV,
+ NI_CALIBRATION_SUBDEV,
+ NI_EEPROM_SUBDEV,
+ NI_PFI_DIO_SUBDEV,
+ NI_CS5529_CALIBRATION_SUBDEV,
+ NI_SERIAL_SUBDEV,
+ NI_RTSI_SUBDEV,
+ NI_GPCT0_SUBDEV,
+ NI_GPCT1_SUBDEV,
+ NI_FREQ_OUT_SUBDEV,
+ NI_NUM_SUBDEVICES
+};
+static inline unsigned NI_GPCT_SUBDEV(unsigned counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NI_GPCT0_SUBDEV;
+ break;
+ case 1:
+ return NI_GPCT1_SUBDEV;
+ break;
+ default:
+ break;
+ }
+ BUG();
+ return NI_GPCT0_SUBDEV;
+}
+
+enum timebase_nanoseconds {
+ TIMEBASE_1_NS = 50,
+ TIMEBASE_2_NS = 10000
+};
+
+#define SERIAL_DISABLED 0
+#define SERIAL_600NS 600
+#define SERIAL_1_2US 1200
+#define SERIAL_10US 10000
+
+static const int num_adc_stages_611x = 3;
+
+static void handle_a_interrupt(struct comedi_device * dev, unsigned short status,
+ unsigned ai_mite_status);
+static void handle_b_interrupt(struct comedi_device * dev, unsigned short status,
+ unsigned ao_mite_status);
+static void get_last_sample_611x(struct comedi_device * dev);
+static void get_last_sample_6143(struct comedi_device * dev);
+
+static inline void ni_set_bitfield(struct comedi_device * dev, int reg,
+ unsigned bit_mask, unsigned bit_values)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
+ switch (reg) {
+ case Interrupt_A_Enable_Register:
+ devpriv->int_a_enable_reg &= ~bit_mask;
+ devpriv->int_a_enable_reg |= bit_values & bit_mask;
+ devpriv->stc_writew(dev, devpriv->int_a_enable_reg,
+ Interrupt_A_Enable_Register);
+ break;
+ case Interrupt_B_Enable_Register:
+ devpriv->int_b_enable_reg &= ~bit_mask;
+ devpriv->int_b_enable_reg |= bit_values & bit_mask;
+ devpriv->stc_writew(dev, devpriv->int_b_enable_reg,
+ Interrupt_B_Enable_Register);
+ break;
+ case IO_Bidirection_Pin_Register:
+ devpriv->io_bidirection_pin_reg &= ~bit_mask;
+ devpriv->io_bidirection_pin_reg |= bit_values & bit_mask;
+ devpriv->stc_writew(dev, devpriv->io_bidirection_pin_reg,
+ IO_Bidirection_Pin_Register);
+ break;
+ case AI_AO_Select:
+ devpriv->ai_ao_select_reg &= ~bit_mask;
+ devpriv->ai_ao_select_reg |= bit_values & bit_mask;
+ ni_writeb(devpriv->ai_ao_select_reg, AI_AO_Select);
+ break;
+ case G0_G1_Select:
+ devpriv->g0_g1_select_reg &= ~bit_mask;
+ devpriv->g0_g1_select_reg |= bit_values & bit_mask;
+ ni_writeb(devpriv->g0_g1_select_reg, G0_G1_Select);
+ break;
+ default:
+ rt_printk("Warning %s() called with invalid register\n",
+ __FUNCTION__);
+ rt_printk("reg is %d\n", reg);
+ break;
+ }
+ mmiowb();
+ comedi_spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
+}
+
+#ifdef PCIDMA
+static int ni_ai_drain_dma(struct comedi_device * dev);
+
+/* DMA channel setup */
+
+// negative channel means no channel
+static inline void ni_set_ai_dma_channel(struct comedi_device * dev, int channel)
+{
+ unsigned bitfield;
+
+ if (channel >= 0) {
+ bitfield =
+ (ni_stc_dma_channel_select_bitfield(channel) <<
+ AI_DMA_Select_Shift) & AI_DMA_Select_Mask;
+ } else {
+ bitfield = 0;
+ }
+ ni_set_bitfield(dev, AI_AO_Select, AI_DMA_Select_Mask, bitfield);
+}
+
+// negative channel means no channel
+static inline void ni_set_ao_dma_channel(struct comedi_device * dev, int channel)
+{
+ unsigned bitfield;
+
+ if (channel >= 0) {
+ bitfield =
+ (ni_stc_dma_channel_select_bitfield(channel) <<
+ AO_DMA_Select_Shift) & AO_DMA_Select_Mask;
+ } else {
+ bitfield = 0;
+ }
+ ni_set_bitfield(dev, AI_AO_Select, AO_DMA_Select_Mask, bitfield);
+}
+
+// negative mite_channel means no channel
+static inline void ni_set_gpct_dma_channel(struct comedi_device * dev,
+ unsigned gpct_index, int mite_channel)
+{
+ unsigned bitfield;
+
+ if (mite_channel >= 0) {
+ bitfield = GPCT_DMA_Select_Bits(gpct_index, mite_channel);
+ } else {
+ bitfield = 0;
+ }
+ ni_set_bitfield(dev, G0_G1_Select, GPCT_DMA_Select_Mask(gpct_index),
+ bitfield);
+}
+
+// negative mite_channel means no channel
+static inline void ni_set_cdo_dma_channel(struct comedi_device * dev, int mite_channel)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
+ devpriv->cdio_dma_select_reg &= ~CDO_DMA_Select_Mask;
+ if (mite_channel >= 0) {
+ /*XXX just guessing ni_stc_dma_channel_select_bitfield() returns the right bits,
+ under the assumption the cdio dma selection works just like ai/ao/gpct.
+ Definitely works for dma channels 0 and 1. */
+ devpriv->cdio_dma_select_reg |=
+ (ni_stc_dma_channel_select_bitfield(mite_channel) <<
+ CDO_DMA_Select_Shift) & CDO_DMA_Select_Mask;
+ }
+ ni_writeb(devpriv->cdio_dma_select_reg, M_Offset_CDIO_DMA_Select);
+ mmiowb();
+ comedi_spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
+}
+
+static int ni_request_ai_mite_channel(struct comedi_device * dev)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ BUG_ON(devpriv->ai_mite_chan);
+ devpriv->ai_mite_chan =
+ mite_request_channel(devpriv->mite, devpriv->ai_mite_ring);
+ if (devpriv->ai_mite_chan == NULL) {
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock,
+ flags);
+ comedi_error(dev,
+ "failed to reserve mite dma channel for analog input.");
+ return -EBUSY;
+ }
+ devpriv->ai_mite_chan->dir = COMEDI_INPUT;
+ ni_set_ai_dma_channel(dev, devpriv->ai_mite_chan->channel);
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ return 0;
+}
+
+static int ni_request_ao_mite_channel(struct comedi_device * dev)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ BUG_ON(devpriv->ao_mite_chan);
+ devpriv->ao_mite_chan =
+ mite_request_channel(devpriv->mite, devpriv->ao_mite_ring);
+ if (devpriv->ao_mite_chan == NULL) {
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock,
+ flags);
+ comedi_error(dev,
+ "failed to reserve mite dma channel for analog outut.");
+ return -EBUSY;
+ }
+ devpriv->ao_mite_chan->dir = COMEDI_OUTPUT;
+ ni_set_ao_dma_channel(dev, devpriv->ao_mite_chan->channel);
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ return 0;
+}
+
+static int ni_request_gpct_mite_channel(struct comedi_device * dev,
+ unsigned gpct_index, enum comedi_io_direction direction)
+{
+ unsigned long flags;
+ struct mite_channel *mite_chan;
+
+ BUG_ON(gpct_index >= NUM_GPCT);
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ BUG_ON(devpriv->counter_dev->counters[gpct_index].mite_chan);
+ mite_chan =
+ mite_request_channel(devpriv->mite,
+ devpriv->gpct_mite_ring[gpct_index]);
+ if (mite_chan == NULL) {
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock,
+ flags);
+ comedi_error(dev,
+ "failed to reserve mite dma channel for counter.");
+ return -EBUSY;
+ }
+ mite_chan->dir = direction;
+ ni_tio_set_mite_channel(&devpriv->counter_dev->counters[gpct_index],
+ mite_chan);
+ ni_set_gpct_dma_channel(dev, gpct_index, mite_chan->channel);
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ return 0;
+}
+
+#endif // PCIDMA
+
+static int ni_request_cdo_mite_channel(struct comedi_device * dev)
+{
+#ifdef PCIDMA
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ BUG_ON(devpriv->cdo_mite_chan);
+ devpriv->cdo_mite_chan =
+ mite_request_channel(devpriv->mite, devpriv->cdo_mite_ring);
+ if (devpriv->cdo_mite_chan == NULL) {
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock,
+ flags);
+ comedi_error(dev,
+ "failed to reserve mite dma channel for correlated digital outut.");
+ return -EBUSY;
+ }
+ devpriv->cdo_mite_chan->dir = COMEDI_OUTPUT;
+ ni_set_cdo_dma_channel(dev, devpriv->cdo_mite_chan->channel);
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+#endif // PCIDMA
+ return 0;
+}
+
+static void ni_release_ai_mite_channel(struct comedi_device * dev)
+{
+#ifdef PCIDMA
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->ai_mite_chan) {
+ ni_set_ai_dma_channel(dev, -1);
+ mite_release_channel(devpriv->ai_mite_chan);
+ devpriv->ai_mite_chan = NULL;
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+#endif // PCIDMA
+}
+
+static void ni_release_ao_mite_channel(struct comedi_device * dev)
+{
+#ifdef PCIDMA
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->ao_mite_chan) {
+ ni_set_ao_dma_channel(dev, -1);
+ mite_release_channel(devpriv->ao_mite_chan);
+ devpriv->ao_mite_chan = NULL;
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+#endif // PCIDMA
+}
+
+void ni_release_gpct_mite_channel(struct comedi_device * dev, unsigned gpct_index)
+{
+#ifdef PCIDMA
+ unsigned long flags;
+
+ BUG_ON(gpct_index >= NUM_GPCT);
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->counter_dev->counters[gpct_index].mite_chan) {
+ struct mite_channel *mite_chan =
+ devpriv->counter_dev->counters[gpct_index].mite_chan;
+
+ ni_set_gpct_dma_channel(dev, gpct_index, -1);
+ ni_tio_set_mite_channel(&devpriv->counter_dev->
+ counters[gpct_index], NULL);
+ mite_release_channel(mite_chan);
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+#endif // PCIDMA
+}
+
+static void ni_release_cdo_mite_channel(struct comedi_device * dev)
+{
+#ifdef PCIDMA
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->cdo_mite_chan) {
+ ni_set_cdo_dma_channel(dev, -1);
+ mite_release_channel(devpriv->cdo_mite_chan);
+ devpriv->cdo_mite_chan = NULL;
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+#endif // PCIDMA
+}
+
+// e-series boards use the second irq signals to generate dma requests for their counters
+#ifdef PCIDMA
+static void ni_e_series_enable_second_irq(struct comedi_device * dev,
+ unsigned gpct_index, short enable)
+{
+ if (boardtype.reg_type & ni_reg_m_series_mask)
+ return;
+ switch (gpct_index) {
+ case 0:
+ if (enable) {
+ devpriv->stc_writew(dev, G0_Gate_Second_Irq_Enable,
+ Second_IRQ_A_Enable_Register);
+ } else {
+ devpriv->stc_writew(dev, 0,
+ Second_IRQ_A_Enable_Register);
+ }
+ break;
+ case 1:
+ if (enable) {
+ devpriv->stc_writew(dev, G1_Gate_Second_Irq_Enable,
+ Second_IRQ_B_Enable_Register);
+ } else {
+ devpriv->stc_writew(dev, 0,
+ Second_IRQ_B_Enable_Register);
+ }
+ break;
+ default:
+ BUG();
+ break;
+ }
+}
+#endif // PCIDMA
+
+static void ni_clear_ai_fifo(struct comedi_device * dev)
+{
+ if (boardtype.reg_type == ni_reg_6143) {
+ // Flush the 6143 data FIFO
+ ni_writel(0x10, AIFIFO_Control_6143); // Flush fifo
+ ni_writel(0x00, AIFIFO_Control_6143); // Flush fifo
+ while (ni_readl(AIFIFO_Status_6143) & 0x10) ; // Wait for complete
+ } else {
+ devpriv->stc_writew(dev, 1, ADC_FIFO_Clear);
+ if (boardtype.reg_type == ni_reg_625x) {
+ ni_writeb(0, M_Offset_Static_AI_Control(0));
+ ni_writeb(1, M_Offset_Static_AI_Control(0));
+#if 0
+ /* the NI example code does 3 convert pulses for 625x boards,
+ but that appears to be wrong in practice. */
+ devpriv->stc_writew(dev, AI_CONVERT_Pulse,
+ AI_Command_1_Register);
+ devpriv->stc_writew(dev, AI_CONVERT_Pulse,
+ AI_Command_1_Register);
+ devpriv->stc_writew(dev, AI_CONVERT_Pulse,
+ AI_Command_1_Register);
+#endif
+ }
+ }
+}
+
+static void win_out2(struct comedi_device * dev, uint32_t data, int reg)
+{
+ devpriv->stc_writew(dev, data >> 16, reg);
+ devpriv->stc_writew(dev, data & 0xffff, reg + 1);
+}
+
+static uint32_t win_in2(struct comedi_device * dev, int reg)
+{
+ uint32_t bits;
+ bits = devpriv->stc_readw(dev, reg) << 16;
+ bits |= devpriv->stc_readw(dev, reg + 1);
+ return bits;
+}
+
+#define ao_win_out(data,addr) ni_ao_win_outw(dev,data,addr)
+static inline void ni_ao_win_outw(struct comedi_device * dev, uint16_t data, int addr)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->window_lock, flags);
+ ni_writew(addr, AO_Window_Address_611x);
+ ni_writew(data, AO_Window_Data_611x);
+ comedi_spin_unlock_irqrestore(&devpriv->window_lock, flags);
+}
+
+static inline void ni_ao_win_outl(struct comedi_device * dev, uint32_t data, int addr)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->window_lock, flags);
+ ni_writew(addr, AO_Window_Address_611x);
+ ni_writel(data, AO_Window_Data_611x);
+ comedi_spin_unlock_irqrestore(&devpriv->window_lock, flags);
+}
+
+static inline unsigned short ni_ao_win_inw(struct comedi_device * dev, int addr)
+{
+ unsigned long flags;
+ unsigned short data;
+
+ comedi_spin_lock_irqsave(&devpriv->window_lock, flags);
+ ni_writew(addr, AO_Window_Address_611x);
+ data = ni_readw(AO_Window_Data_611x);
+ comedi_spin_unlock_irqrestore(&devpriv->window_lock, flags);
+ return data;
+}
+
+/* ni_set_bits( ) allows different parts of the ni_mio_common driver to
+* share registers (such as Interrupt_A_Register) without interfering with
+* each other.
+*
+* NOTE: the switch/case statements are optimized out for a constant argument
+* so this is actually quite fast--- If you must wrap another function around this
+* make it inline to avoid a large speed penalty.
+*
+* value should only be 1 or 0.
+*/
+static inline void ni_set_bits(struct comedi_device * dev, int reg, unsigned bits,
+ unsigned value)
+{
+ unsigned bit_values;
+
+ if (value)
+ bit_values = bits;
+ else
+ bit_values = 0;
+ ni_set_bitfield(dev, reg, bits, bit_values);
+}
+
+static irqreturn_t ni_E_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ unsigned short a_status;
+ unsigned short b_status;
+ unsigned int ai_mite_status = 0;
+ unsigned int ao_mite_status = 0;
+ unsigned long flags;
+#ifdef PCIDMA
+ struct mite_struct *mite = devpriv->mite;
+#endif
+
+ if (dev->attached == 0)
+ return IRQ_NONE;
+ smp_mb(); // make sure dev->attached is checked before handler does anything else.
+
+ // lock to avoid race with comedi_poll
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+ a_status = devpriv->stc_readw(dev, AI_Status_1_Register);
+ b_status = devpriv->stc_readw(dev, AO_Status_1_Register);
+#ifdef PCIDMA
+ if (mite) {
+ unsigned long flags_too;
+
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock,
+ flags_too);
+ if (devpriv->ai_mite_chan) {
+ ai_mite_status = mite_get_status(devpriv->ai_mite_chan);
+ if (ai_mite_status & CHSR_LINKC)
+ writel(CHOR_CLRLC,
+ devpriv->mite->mite_io_addr +
+ MITE_CHOR(devpriv->ai_mite_chan->
+ channel));
+ }
+ if (devpriv->ao_mite_chan) {
+ ao_mite_status = mite_get_status(devpriv->ao_mite_chan);
+ if (ao_mite_status & CHSR_LINKC)
+ writel(CHOR_CLRLC,
+ mite->mite_io_addr +
+ MITE_CHOR(devpriv->ao_mite_chan->
+ channel));
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock,
+ flags_too);
+ }
+#endif
+ ack_a_interrupt(dev, a_status);
+ ack_b_interrupt(dev, b_status);
+ if ((a_status & Interrupt_A_St) || (ai_mite_status & CHSR_INT))
+ handle_a_interrupt(dev, a_status, ai_mite_status);
+ if ((b_status & Interrupt_B_St) || (ao_mite_status & CHSR_INT))
+ handle_b_interrupt(dev, b_status, ao_mite_status);
+ handle_gpct_interrupt(dev, 0);
+ handle_gpct_interrupt(dev, 1);
+ handle_cdio_interrupt(dev);
+
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ return IRQ_HANDLED;
+}
+
+#ifdef PCIDMA
+static void ni_sync_ai_dma(struct comedi_device * dev)
+{
+ struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->ai_mite_chan)
+ mite_sync_input_dma(devpriv->ai_mite_chan, s->async);
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+}
+
+static void mite_handle_b_linkc(struct mite_struct *mite, struct comedi_device * dev)
+{
+ struct comedi_subdevice *s = dev->subdevices + NI_AO_SUBDEV;
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->ao_mite_chan) {
+ mite_sync_output_dma(devpriv->ao_mite_chan, s->async);
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+}
+
+static int ni_ao_wait_for_dma_load(struct comedi_device * dev)
+{
+ static const int timeout = 10000;
+ int i;
+ for (i = 0; i < timeout; i++) {
+ unsigned short b_status;
+
+ b_status = devpriv->stc_readw(dev, AO_Status_1_Register);
+ if (b_status & AO_FIFO_Half_Full_St)
+ break;
+ /* if we poll too often, the pci bus activity seems
+ to slow the dma transfer down */
+ comedi_udelay(10);
+ }
+ if (i == timeout) {
+ comedi_error(dev, "timed out waiting for dma load");
+ return -EPIPE;
+ }
+ return 0;
+}
+
+#endif //PCIDMA
+static void ni_handle_eos(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ if (devpriv->aimode == AIMODE_SCAN) {
+#ifdef PCIDMA
+ static const int timeout = 10;
+ int i;
+
+ for (i = 0; i < timeout; i++) {
+ ni_sync_ai_dma(dev);
+ if ((s->async->events & COMEDI_CB_EOS))
+ break;
+ comedi_udelay(1);
+ }
+#else
+ ni_handle_fifo_dregs(dev);
+ s->async->events |= COMEDI_CB_EOS;
+#endif
+ }
+ /* handle special case of single scan using AI_End_On_End_Of_Scan */
+ if ((devpriv->ai_cmd2 & AI_End_On_End_Of_Scan)) {
+ shutdown_ai_command(dev);
+ }
+}
+
+static void shutdown_ai_command(struct comedi_device * dev)
+{
+ struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+
+#ifdef PCIDMA
+ ni_ai_drain_dma(dev);
+#endif
+ ni_handle_fifo_dregs(dev);
+ get_last_sample_611x(dev);
+ get_last_sample_6143(dev);
+
+ s->async->events |= COMEDI_CB_EOA;
+}
+
+static void ni_event(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ if (s->async->
+ events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW | COMEDI_CB_EOA))
+ {
+ switch (s - dev->subdevices) {
+ case NI_AI_SUBDEV:
+ ni_ai_reset(dev, s);
+ break;
+ case NI_AO_SUBDEV:
+ ni_ao_reset(dev, s);
+ break;
+ case NI_GPCT0_SUBDEV:
+ case NI_GPCT1_SUBDEV:
+ ni_gpct_cancel(dev, s);
+ break;
+ case NI_DIO_SUBDEV:
+ ni_cdio_cancel(dev, s);
+ break;
+ default:
+ break;
+ }
+ }
+ comedi_event(dev, s);
+}
+
+static void handle_gpct_interrupt(struct comedi_device * dev,
+ unsigned short counter_index)
+{
+#ifdef PCIDMA
+ struct comedi_subdevice *s = dev->subdevices + NI_GPCT_SUBDEV(counter_index);
+
+ ni_tio_handle_interrupt(&devpriv->counter_dev->counters[counter_index],
+ s);
+ if (s->async->events)
+ ni_event(dev, s);
+#endif
+}
+
+static void ack_a_interrupt(struct comedi_device * dev, unsigned short a_status)
+{
+ unsigned short ack = 0;
+
+ if (a_status & AI_SC_TC_St) {
+ ack |= AI_SC_TC_Interrupt_Ack;
+ }
+ if (a_status & AI_START1_St) {
+ ack |= AI_START1_Interrupt_Ack;
+ }
+ if (a_status & AI_START_St) {
+ ack |= AI_START_Interrupt_Ack;
+ }
+ if (a_status & AI_STOP_St) {
+ /* not sure why we used to ack the START here also, instead of doing it independently. Frank Hess 2007-07-06 */
+ ack |= AI_STOP_Interrupt_Ack /*| AI_START_Interrupt_Ack */ ;
+ }
+ if (ack)
+ devpriv->stc_writew(dev, ack, Interrupt_A_Ack_Register);
+}
+
+static void handle_a_interrupt(struct comedi_device * dev, unsigned short status,
+ unsigned ai_mite_status)
+{
+ struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+
+ //67xx boards don't have ai subdevice, but their gpct0 might generate an a interrupt
+ if (s->type == COMEDI_SUBD_UNUSED)
+ return;
+
+#ifdef DEBUG_INTERRUPT
+ rt_printk
+ ("ni_mio_common: interrupt: a_status=%04x ai_mite_status=%08x\n",
+ status, ai_mite_status);
+ ni_mio_print_status_a(status);
+#endif
+#ifdef PCIDMA
+ if (ai_mite_status & CHSR_LINKC) {
+ ni_sync_ai_dma(dev);
+ }
+
+ if (ai_mite_status & ~(CHSR_INT | CHSR_LINKC | CHSR_DONE | CHSR_MRDY |
+ CHSR_DRDY | CHSR_DRQ1 | CHSR_DRQ0 | CHSR_ERROR |
+ CHSR_SABORT | CHSR_XFERR | CHSR_LxERR_mask)) {
+ rt_printk
+ ("unknown mite interrupt, ack! (ai_mite_status=%08x)\n",
+ ai_mite_status);
+ //mite_print_chsr(ai_mite_status);
+ s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ //disable_irq(dev->irq);
+ }
+#endif
+
+ /* test for all uncommon interrupt events at the same time */
+ if (status & (AI_Overrun_St | AI_Overflow_St | AI_SC_TC_Error_St |
+ AI_SC_TC_St | AI_START1_St)) {
+ if (status == 0xffff) {
+ rt_printk
+ ("ni_mio_common: a_status=0xffff. Card removed?\n");
+ /* we probably aren't even running a command now,
+ * so it's a good idea to be careful. */
+ if (comedi_get_subdevice_runflags(s) & SRF_RUNNING) {
+ s->async->events |=
+ COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ ni_event(dev, s);
+ }
+ return;
+ }
+ if (status & (AI_Overrun_St | AI_Overflow_St |
+ AI_SC_TC_Error_St)) {
+ rt_printk("ni_mio_common: ai error a_status=%04x\n",
+ status);
+ ni_mio_print_status_a(status);
+
+ shutdown_ai_command(dev);
+
+ s->async->events |= COMEDI_CB_ERROR;
+ if (status & (AI_Overrun_St | AI_Overflow_St))
+ s->async->events |= COMEDI_CB_OVERFLOW;
+
+ ni_event(dev, s);
+
+ return;
+ }
+ if (status & AI_SC_TC_St) {
+#ifdef DEBUG_INTERRUPT
+ rt_printk("ni_mio_common: SC_TC interrupt\n");
+#endif
+ if (!devpriv->ai_continuous) {
+ shutdown_ai_command(dev);
+ }
+ }
+ }
+#ifndef PCIDMA
+ if (status & AI_FIFO_Half_Full_St) {
+ int i;
+ static const int timeout = 10;
+ /* pcmcia cards (at least 6036) seem to stop producing interrupts if we
+ *fail to get the fifo less than half full, so loop to be sure.*/
+ for (i = 0; i < timeout; ++i) {
+ ni_handle_fifo_half_full(dev);
+ if ((devpriv->stc_readw(dev,
+ AI_Status_1_Register) &
+ AI_FIFO_Half_Full_St) == 0)
+ break;
+ }
+ }
+#endif // !PCIDMA
+
+ if ((status & AI_STOP_St)) {
+ ni_handle_eos(dev, s);
+ }
+
+ ni_event(dev, s);
+
+#ifdef DEBUG_INTERRUPT
+ status = devpriv->stc_readw(dev, AI_Status_1_Register);
+ if (status & Interrupt_A_St) {
+ rt_printk
+ ("handle_a_interrupt: didn't clear interrupt? status=0x%x\n",
+ status);
+ }
+#endif
+}
+
+static void ack_b_interrupt(struct comedi_device * dev, unsigned short b_status)
+{
+ unsigned short ack = 0;
+ if (b_status & AO_BC_TC_St) {
+ ack |= AO_BC_TC_Interrupt_Ack;
+ }
+ if (b_status & AO_Overrun_St) {
+ ack |= AO_Error_Interrupt_Ack;
+ }
+ if (b_status & AO_START_St) {
+ ack |= AO_START_Interrupt_Ack;
+ }
+ if (b_status & AO_START1_St) {
+ ack |= AO_START1_Interrupt_Ack;
+ }
+ if (b_status & AO_UC_TC_St) {
+ ack |= AO_UC_TC_Interrupt_Ack;
+ }
+ if (b_status & AO_UI2_TC_St) {
+ ack |= AO_UI2_TC_Interrupt_Ack;
+ }
+ if (b_status & AO_UPDATE_St) {
+ ack |= AO_UPDATE_Interrupt_Ack;
+ }
+ if (ack)
+ devpriv->stc_writew(dev, ack, Interrupt_B_Ack_Register);
+}
+
+static void handle_b_interrupt(struct comedi_device * dev, unsigned short b_status,
+ unsigned ao_mite_status)
+{
+ struct comedi_subdevice *s = dev->subdevices + NI_AO_SUBDEV;
+ //unsigned short ack=0;
+#ifdef DEBUG_INTERRUPT
+ rt_printk("ni_mio_common: interrupt: b_status=%04x m1_status=%08x\n",
+ b_status, ao_mite_status);
+ ni_mio_print_status_b(b_status);
+#endif
+
+#ifdef PCIDMA
+ /* Currently, mite.c requires us to handle LINKC */
+ if (ao_mite_status & CHSR_LINKC) {
+ mite_handle_b_linkc(devpriv->mite, dev);
+ }
+
+ if (ao_mite_status & ~(CHSR_INT | CHSR_LINKC | CHSR_DONE | CHSR_MRDY |
+ CHSR_DRDY | CHSR_DRQ1 | CHSR_DRQ0 | CHSR_ERROR |
+ CHSR_SABORT | CHSR_XFERR | CHSR_LxERR_mask)) {
+ rt_printk
+ ("unknown mite interrupt, ack! (ao_mite_status=%08x)\n",
+ ao_mite_status);
+ //mite_print_chsr(ao_mite_status);
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ }
+#endif
+
+ if (b_status == 0xffff)
+ return;
+ if (b_status & AO_Overrun_St) {
+ rt_printk
+ ("ni_mio_common: AO FIFO underrun status=0x%04x status2=0x%04x\n",
+ b_status, devpriv->stc_readw(dev,
+ AO_Status_2_Register));
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ }
+
+ if (b_status & AO_BC_TC_St) {
+ MDPRINTK("ni_mio_common: AO BC_TC status=0x%04x status2=0x%04x\n", b_status, devpriv->stc_readw(dev, AO_Status_2_Register));
+ s->async->events |= COMEDI_CB_EOA;
+ }
+#ifndef PCIDMA
+ if (b_status & AO_FIFO_Request_St) {
+ int ret;
+
+ ret = ni_ao_fifo_half_empty(dev, s);
+ if (!ret) {
+ rt_printk("ni_mio_common: AO buffer underrun\n");
+ ni_set_bits(dev, Interrupt_B_Enable_Register,
+ AO_FIFO_Interrupt_Enable |
+ AO_Error_Interrupt_Enable, 0);
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ }
+ }
+#endif
+
+ ni_event(dev, s);
+}
+
+#ifdef DEBUG_STATUS_A
+static const char *const status_a_strings[] = {
+ "passthru0", "fifo", "G0_gate", "G0_TC",
+ "stop", "start", "sc_tc", "start1",
+ "start2", "sc_tc_error", "overflow", "overrun",
+ "fifo_empty", "fifo_half_full", "fifo_full", "interrupt_a"
+};
+
+static void ni_mio_print_status_a(int status)
+{
+ int i;
+
+ rt_printk("A status:");
+ for (i = 15; i >= 0; i--) {
+ if (status & (1 << i)) {
+ rt_printk(" %s", status_a_strings[i]);
+ }
+ }
+ rt_printk("\n");
+}
+#endif
+
+#ifdef DEBUG_STATUS_B
+static const char *const status_b_strings[] = {
+ "passthru1", "fifo", "G1_gate", "G1_TC",
+ "UI2_TC", "UPDATE", "UC_TC", "BC_TC",
+ "start1", "overrun", "start", "bc_tc_error",
+ "fifo_empty", "fifo_half_full", "fifo_full", "interrupt_b"
+};
+
+static void ni_mio_print_status_b(int status)
+{
+ int i;
+
+ rt_printk("B status:");
+ for (i = 15; i >= 0; i--) {
+ if (status & (1 << i)) {
+ rt_printk(" %s", status_b_strings[i]);
+ }
+ }
+ rt_printk("\n");
+}
+#endif
+
+#ifndef PCIDMA
+
+static void ni_ao_fifo_load(struct comedi_device * dev, struct comedi_subdevice * s, int n)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ int chan;
+ int i;
+ short d;
+ u32 packed_data;
+ int range;
+ int err = 1;
+
+ chan = async->cur_chan;
+ for (i = 0; i < n; i++) {
+ err &= comedi_buf_get(async, &d);
+ if (err == 0)
+ break;
+
+ range = CR_RANGE(cmd->chanlist[chan]);
+
+ if (boardtype.reg_type & ni_reg_6xxx_mask) {
+ packed_data = d & 0xffff;
+ /* 6711 only has 16 bit wide ao fifo */
+ if (boardtype.reg_type != ni_reg_6711) {
+ err &= comedi_buf_get(async, &d);
+ if (err == 0)
+ break;
+ chan++;
+ i++;
+ packed_data |= (d << 16) & 0xffff0000;
+ }
+ ni_writel(packed_data, DAC_FIFO_Data_611x);
+ } else {
+ ni_writew(d, DAC_FIFO_Data);
+ }
+ chan++;
+ chan %= cmd->chanlist_len;
+ }
+ async->cur_chan = chan;
+ if (err == 0) {
+ async->events |= COMEDI_CB_OVERFLOW;
+ }
+}
+
+/*
+ * There's a small problem if the FIFO gets really low and we
+ * don't have the data to fill it. Basically, if after we fill
+ * the FIFO with all the data available, the FIFO is _still_
+ * less than half full, we never clear the interrupt. If the
+ * IRQ is in edge mode, we never get another interrupt, because
+ * this one wasn't cleared. If in level mode, we get flooded
+ * with interrupts that we can't fulfill, because nothing ever
+ * gets put into the buffer.
+ *
+ * This kind of situation is recoverable, but it is easier to
+ * just pretend we had a FIFO underrun, since there is a good
+ * chance it will happen anyway. This is _not_ the case for
+ * RT code, as RT code might purposely be running close to the
+ * metal. Needs to be fixed eventually.
+ */
+static int ni_ao_fifo_half_empty(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int n;
+
+ n = comedi_buf_read_n_available(s->async);
+ if (n == 0) {
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ return 0;
+ }
+
+ n /= sizeof(short);
+ if (n > boardtype.ao_fifo_depth / 2)
+ n = boardtype.ao_fifo_depth / 2;
+
+ ni_ao_fifo_load(dev, s, n);
+
+ s->async->events |= COMEDI_CB_BLOCK;
+
+ return 1;
+}
+
+static int ni_ao_prep_fifo(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int n;
+
+ /* reset fifo */
+ devpriv->stc_writew(dev, 1, DAC_FIFO_Clear);
+ if (boardtype.reg_type & ni_reg_6xxx_mask)
+ ni_ao_win_outl(dev, 0x6, AO_FIFO_Offset_Load_611x);
+
+ /* load some data */
+ n = comedi_buf_read_n_available(s->async);
+ if (n == 0)
+ return 0;
+
+ n /= sizeof(short);
+ if (n > boardtype.ao_fifo_depth)
+ n = boardtype.ao_fifo_depth;
+
+ ni_ao_fifo_load(dev, s, n);
+
+ return n;
+}
+
+static void ni_ai_fifo_read(struct comedi_device * dev, struct comedi_subdevice * s, int n)
+{
+ struct comedi_async *async = s->async;
+ int i;
+
+ if (boardtype.reg_type == ni_reg_611x) {
+ short data[2];
+ u32 dl;
+
+ for (i = 0; i < n / 2; i++) {
+ dl = ni_readl(ADC_FIFO_Data_611x);
+ /* This may get the hi/lo data in the wrong order */
+ data[0] = (dl >> 16) & 0xffff;
+ data[1] = dl & 0xffff;
+ cfc_write_array_to_buffer(s, data, sizeof(data));
+ }
+ /* Check if there's a single sample stuck in the FIFO */
+ if (n % 2) {
+ dl = ni_readl(ADC_FIFO_Data_611x);
+ data[0] = dl & 0xffff;
+ cfc_write_to_buffer(s, data[0]);
+ }
+ } else if (boardtype.reg_type == ni_reg_6143) {
+ short data[2];
+ u32 dl;
+
+ // This just reads the FIFO assuming the data is present, no checks on the FIFO status are performed
+ for (i = 0; i < n / 2; i++) {
+ dl = ni_readl(AIFIFO_Data_6143);
+
+ data[0] = (dl >> 16) & 0xffff;
+ data[1] = dl & 0xffff;
+ cfc_write_array_to_buffer(s, data, sizeof(data));
+ }
+ if (n % 2) {
+ /* Assume there is a single sample stuck in the FIFO */
+ ni_writel(0x01, AIFIFO_Control_6143); // Get stranded sample into FIFO
+ dl = ni_readl(AIFIFO_Data_6143);
+ data[0] = (dl >> 16) & 0xffff;
+ cfc_write_to_buffer(s, data[0]);
+ }
+ } else {
+ if (n > sizeof(devpriv->ai_fifo_buffer) /
+ sizeof(devpriv->ai_fifo_buffer[0])) {
+ comedi_error(dev, "bug! ai_fifo_buffer too small");
+ async->events |= COMEDI_CB_ERROR;
+ return;
+ }
+ for (i = 0; i < n; i++) {
+ devpriv->ai_fifo_buffer[i] =
+ ni_readw(ADC_FIFO_Data_Register);
+ }
+ cfc_write_array_to_buffer(s, devpriv->ai_fifo_buffer,
+ n * sizeof(devpriv->ai_fifo_buffer[0]));
+ }
+}
+
+static void ni_handle_fifo_half_full(struct comedi_device * dev)
+{
+ int n;
+ struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+
+ n = boardtype.ai_fifo_depth / 2;
+
+ ni_ai_fifo_read(dev, s, n);
+}
+#endif
+
+#ifdef PCIDMA
+static int ni_ai_drain_dma(struct comedi_device * dev)
+{
+ int i;
+ static const int timeout = 10000;
+ unsigned long flags;
+ int retval = 0;
+
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->ai_mite_chan) {
+ for (i = 0; i < timeout; i++) {
+ if ((devpriv->stc_readw(dev,
+ AI_Status_1_Register) &
+ AI_FIFO_Empty_St)
+ && mite_bytes_in_transit(devpriv->
+ ai_mite_chan) == 0)
+ break;
+ comedi_udelay(5);
+ }
+ if (i == timeout) {
+ rt_printk
+ ("ni_mio_common: wait for dma drain timed out\n");
+ rt_printk
+ ("mite_bytes_in_transit=%i, AI_Status1_Register=0x%x\n",
+ mite_bytes_in_transit(devpriv->ai_mite_chan),
+ devpriv->stc_readw(dev, AI_Status_1_Register));
+ retval = -1;
+ }
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+
+ ni_sync_ai_dma(dev);
+
+ return retval;
+}
+#endif
+/*
+ Empties the AI fifo
+*/
+static void ni_handle_fifo_dregs(struct comedi_device * dev)
+{
+ struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+ short data[2];
+ u32 dl;
+ short fifo_empty;
+ int i;
+
+ if (boardtype.reg_type == ni_reg_611x) {
+ while ((devpriv->stc_readw(dev,
+ AI_Status_1_Register) &
+ AI_FIFO_Empty_St) == 0) {
+ dl = ni_readl(ADC_FIFO_Data_611x);
+
+ /* This may get the hi/lo data in the wrong order */
+ data[0] = (dl >> 16);
+ data[1] = (dl & 0xffff);
+ cfc_write_array_to_buffer(s, data, sizeof(data));
+ }
+ } else if (boardtype.reg_type == ni_reg_6143) {
+ i = 0;
+ while (ni_readl(AIFIFO_Status_6143) & 0x04) {
+ dl = ni_readl(AIFIFO_Data_6143);
+
+ /* This may get the hi/lo data in the wrong order */
+ data[0] = (dl >> 16);
+ data[1] = (dl & 0xffff);
+ cfc_write_array_to_buffer(s, data, sizeof(data));
+ i += 2;
+ }
+ // Check if stranded sample is present
+ if (ni_readl(AIFIFO_Status_6143) & 0x01) {
+ ni_writel(0x01, AIFIFO_Control_6143); // Get stranded sample into FIFO
+ dl = ni_readl(AIFIFO_Data_6143);
+ data[0] = (dl >> 16) & 0xffff;
+ cfc_write_to_buffer(s, data[0]);
+ }
+
+ } else {
+ fifo_empty =
+ devpriv->stc_readw(dev,
+ AI_Status_1_Register) & AI_FIFO_Empty_St;
+ while (fifo_empty == 0) {
+ for (i = 0;
+ i <
+ sizeof(devpriv->ai_fifo_buffer) /
+ sizeof(devpriv->ai_fifo_buffer[0]); i++) {
+ fifo_empty =
+ devpriv->stc_readw(dev,
+ AI_Status_1_Register) &
+ AI_FIFO_Empty_St;
+ if (fifo_empty)
+ break;
+ devpriv->ai_fifo_buffer[i] =
+ ni_readw(ADC_FIFO_Data_Register);
+ }
+ cfc_write_array_to_buffer(s, devpriv->ai_fifo_buffer,
+ i * sizeof(devpriv->ai_fifo_buffer[0]));
+ }
+ }
+}
+
+static void get_last_sample_611x(struct comedi_device * dev)
+{
+ struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+ short data;
+ u32 dl;
+
+ if (boardtype.reg_type != ni_reg_611x)
+ return;
+
+ /* Check if there's a single sample stuck in the FIFO */
+ if (ni_readb(XXX_Status) & 0x80) {
+ dl = ni_readl(ADC_FIFO_Data_611x);
+ data = (dl & 0xffff);
+ cfc_write_to_buffer(s, data);
+ }
+}
+
+static void get_last_sample_6143(struct comedi_device * dev)
+{
+ struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+ short data;
+ u32 dl;
+
+ if (boardtype.reg_type != ni_reg_6143)
+ return;
+
+ /* Check if there's a single sample stuck in the FIFO */
+ if (ni_readl(AIFIFO_Status_6143) & 0x01) {
+ ni_writel(0x01, AIFIFO_Control_6143); // Get stranded sample into FIFO
+ dl = ni_readl(AIFIFO_Data_6143);
+
+ /* This may get the hi/lo data in the wrong order */
+ data = (dl >> 16) & 0xffff;
+ cfc_write_to_buffer(s, data);
+ }
+}
+
+static void ni_ai_munge(struct comedi_device * dev, struct comedi_subdevice * s,
+ void *data, unsigned int num_bytes, unsigned int chan_index)
+{
+ struct comedi_async *async = s->async;
+ unsigned int i;
+ unsigned int length = num_bytes / bytes_per_sample(s);
+ short *array = data;
+ unsigned int *larray = data;
+ for (i = 0; i < length; i++) {
+#ifdef PCIDMA
+ if (s->subdev_flags & SDF_LSAMPL)
+ larray[i] = le32_to_cpu(larray[i]);
+ else
+ array[i] = le16_to_cpu(array[i]);
+#endif
+ if (s->subdev_flags & SDF_LSAMPL)
+ larray[i] += devpriv->ai_offset[chan_index];
+ else
+ array[i] += devpriv->ai_offset[chan_index];
+ chan_index++;
+ chan_index %= async->cmd.chanlist_len;
+ }
+}
+
+#ifdef PCIDMA
+
+static int ni_ai_setup_MITE_dma(struct comedi_device * dev)
+{
+ struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+ int retval;
+ unsigned long flags;
+
+ retval = ni_request_ai_mite_channel(dev);
+ if (retval)
+ return retval;
+// rt_printk("comedi_debug: using mite channel %i for ai.\n", devpriv->ai_mite_chan->channel);
+
+ /* write alloc the entire buffer */
+ comedi_buf_write_alloc(s->async, s->async->prealloc_bufsz);
+
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if(devpriv->ai_mite_chan == NULL)
+ {
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ return -EIO;
+ }
+
+ switch (boardtype.reg_type) {
+ case ni_reg_611x:
+ case ni_reg_6143:
+ mite_prep_dma(devpriv->ai_mite_chan, 32, 16);
+ break;
+ case ni_reg_628x:
+ mite_prep_dma(devpriv->ai_mite_chan, 32, 32);
+ break;
+ default:
+ mite_prep_dma(devpriv->ai_mite_chan, 16, 16);
+ break;
+ };
+ /*start the MITE */
+ mite_dma_arm(devpriv->ai_mite_chan);
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+
+ return 0;
+}
+
+static int ni_ao_setup_MITE_dma(struct comedi_device * dev)
+{
+ struct comedi_subdevice *s = dev->subdevices + NI_AO_SUBDEV;
+ int retval;
+ unsigned long flags;
+
+ retval = ni_request_ao_mite_channel(dev);
+ if (retval)
+ return retval;
+
+ /* read alloc the entire buffer */
+ comedi_buf_read_alloc(s->async, s->async->prealloc_bufsz);
+
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->ao_mite_chan) {
+ if (boardtype.reg_type & (ni_reg_611x | ni_reg_6713)) {
+ mite_prep_dma(devpriv->ao_mite_chan, 32, 32);
+ } else {
+ /* doing 32 instead of 16 bit wide transfers from memory
+ makes the mite do 32 bit pci transfers, doubling pci bandwidth. */
+ mite_prep_dma(devpriv->ao_mite_chan, 16, 32);
+ }
+ mite_dma_arm(devpriv->ao_mite_chan);
+ } else
+ retval = -EIO;
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+
+ return retval;
+}
+
+#endif // PCIDMA
+
+/*
+ used for both cancel ioctl and board initialization
+
+ this is pretty harsh for a cancel, but it works...
+ */
+
+static int ni_ai_reset(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ ni_release_ai_mite_channel(dev);
+ /* ai configuration */
+ devpriv->stc_writew(dev, AI_Configuration_Start | AI_Reset,
+ Joint_Reset_Register);
+
+ ni_set_bits(dev, Interrupt_A_Enable_Register,
+ AI_SC_TC_Interrupt_Enable | AI_START1_Interrupt_Enable |
+ AI_START2_Interrupt_Enable | AI_START_Interrupt_Enable |
+ AI_STOP_Interrupt_Enable | AI_Error_Interrupt_Enable |
+ AI_FIFO_Interrupt_Enable, 0);
+
+ ni_clear_ai_fifo(dev);
+
+ if (boardtype.reg_type != ni_reg_6143)
+ ni_writeb(0, Misc_Command);
+
+ devpriv->stc_writew(dev, AI_Disarm, AI_Command_1_Register); /* reset pulses */
+ devpriv->stc_writew(dev,
+ AI_Start_Stop | AI_Mode_1_Reserved /*| AI_Trigger_Once */ ,
+ AI_Mode_1_Register);
+ devpriv->stc_writew(dev, 0x0000, AI_Mode_2_Register);
+ /* generate FIFO interrupts on non-empty */
+ devpriv->stc_writew(dev, (0 << 6) | 0x0000, AI_Mode_3_Register);
+ if (boardtype.reg_type == ni_reg_611x) {
+ devpriv->stc_writew(dev, AI_SHIFTIN_Pulse_Width |
+ AI_SOC_Polarity |
+ AI_LOCALMUX_CLK_Pulse_Width, AI_Personal_Register);
+ devpriv->stc_writew(dev, AI_SCAN_IN_PROG_Output_Select(3) |
+ AI_EXTMUX_CLK_Output_Select(0) |
+ AI_LOCALMUX_CLK_Output_Select(2) |
+ AI_SC_TC_Output_Select(3) |
+ AI_CONVERT_Output_Select(AI_CONVERT_Output_Enable_High),
+ AI_Output_Control_Register);
+ } else if (boardtype.reg_type == ni_reg_6143) {
+ devpriv->stc_writew(dev, AI_SHIFTIN_Pulse_Width |
+ AI_SOC_Polarity |
+ AI_LOCALMUX_CLK_Pulse_Width, AI_Personal_Register);
+ devpriv->stc_writew(dev, AI_SCAN_IN_PROG_Output_Select(3) |
+ AI_EXTMUX_CLK_Output_Select(0) |
+ AI_LOCALMUX_CLK_Output_Select(2) |
+ AI_SC_TC_Output_Select(3) |
+ AI_CONVERT_Output_Select(AI_CONVERT_Output_Enable_Low),
+ AI_Output_Control_Register);
+ } else {
+ unsigned ai_output_control_bits;
+ devpriv->stc_writew(dev, AI_SHIFTIN_Pulse_Width |
+ AI_SOC_Polarity |
+ AI_CONVERT_Pulse_Width |
+ AI_LOCALMUX_CLK_Pulse_Width, AI_Personal_Register);
+ ai_output_control_bits = AI_SCAN_IN_PROG_Output_Select(3) |
+ AI_EXTMUX_CLK_Output_Select(0) |
+ AI_LOCALMUX_CLK_Output_Select(2) |
+ AI_SC_TC_Output_Select(3);
+ if (boardtype.reg_type == ni_reg_622x)
+ ai_output_control_bits |=
+ AI_CONVERT_Output_Select
+ (AI_CONVERT_Output_Enable_High);
+ else
+ ai_output_control_bits |=
+ AI_CONVERT_Output_Select
+ (AI_CONVERT_Output_Enable_Low);
+ devpriv->stc_writew(dev, ai_output_control_bits,
+ AI_Output_Control_Register);
+ }
+ /* the following registers should not be changed, because there
+ * are no backup registers in devpriv. If you want to change
+ * any of these, add a backup register and other appropriate code:
+ * AI_Mode_1_Register
+ * AI_Mode_3_Register
+ * AI_Personal_Register
+ * AI_Output_Control_Register
+ */
+ devpriv->stc_writew(dev, AI_SC_TC_Error_Confirm | AI_START_Interrupt_Ack | AI_START2_Interrupt_Ack | AI_START1_Interrupt_Ack | AI_SC_TC_Interrupt_Ack | AI_Error_Interrupt_Ack | AI_STOP_Interrupt_Ack, Interrupt_A_Ack_Register); /* clear interrupts */
+
+ devpriv->stc_writew(dev, AI_Configuration_End, Joint_Reset_Register);
+
+ return 0;
+}
+
+static int ni_ai_poll(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long flags = 0;
+ int count;
+
+ // lock to avoid race with interrupt handler
+ if (in_interrupt() == 0)
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+#ifndef PCIDMA
+ ni_handle_fifo_dregs(dev);
+#else
+ ni_sync_ai_dma(dev);
+#endif
+ count = s->async->buf_write_count - s->async->buf_read_count;
+ if (in_interrupt() == 0)
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return count;
+}
+
+static int ni_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i, n;
+ const unsigned int mask = (1 << boardtype.adbits) - 1;
+ unsigned signbits;
+ unsigned short d;
+ unsigned long dl;
+
+ ni_load_channelgain_list(dev, 1, &insn->chanspec);
+
+ ni_clear_ai_fifo(dev);
+
+ signbits = devpriv->ai_offset[0];
+ if (boardtype.reg_type == ni_reg_611x) {
+ for (n = 0; n < num_adc_stages_611x; n++) {
+ devpriv->stc_writew(dev, AI_CONVERT_Pulse,
+ AI_Command_1_Register);
+ comedi_udelay(1);
+ }
+ for (n = 0; n < insn->n; n++) {
+ devpriv->stc_writew(dev, AI_CONVERT_Pulse,
+ AI_Command_1_Register);
+ /* The 611x has screwy 32-bit FIFOs. */
+ d = 0;
+ for (i = 0; i < NI_TIMEOUT; i++) {
+ if (ni_readb(XXX_Status) & 0x80) {
+ d = (ni_readl(ADC_FIFO_Data_611x) >> 16)
+ & 0xffff;
+ break;
+ }
+ if (!(devpriv->stc_readw(dev,
+ AI_Status_1_Register) &
+ AI_FIFO_Empty_St)) {
+ d = ni_readl(ADC_FIFO_Data_611x) &
+ 0xffff;
+ break;
+ }
+ }
+ if (i == NI_TIMEOUT) {
+ rt_printk
+ ("ni_mio_common: timeout in 611x ni_ai_insn_read\n");
+ return -ETIME;
+ }
+ d += signbits;
+ data[n] = d;
+ }
+ } else if (boardtype.reg_type == ni_reg_6143) {
+ for (n = 0; n < insn->n; n++) {
+ devpriv->stc_writew(dev, AI_CONVERT_Pulse,
+ AI_Command_1_Register);
+
+ /* The 6143 has 32-bit FIFOs. You need to strobe a bit to move a single 16bit stranded sample into the FIFO */
+ dl = 0;
+ for (i = 0; i < NI_TIMEOUT; i++) {
+ if (ni_readl(AIFIFO_Status_6143) & 0x01) {
+ ni_writel(0x01, AIFIFO_Control_6143); // Get stranded sample into FIFO
+ dl = ni_readl(AIFIFO_Data_6143);
+ break;
+ }
+ }
+ if (i == NI_TIMEOUT) {
+ rt_printk
+ ("ni_mio_common: timeout in 6143 ni_ai_insn_read\n");
+ return -ETIME;
+ }
+ data[n] = (((dl >> 16) & 0xFFFF) + signbits) & 0xFFFF;
+ }
+ } else {
+ for (n = 0; n < insn->n; n++) {
+ devpriv->stc_writew(dev, AI_CONVERT_Pulse,
+ AI_Command_1_Register);
+ for (i = 0; i < NI_TIMEOUT; i++) {
+ if (!(devpriv->stc_readw(dev,
+ AI_Status_1_Register) &
+ AI_FIFO_Empty_St))
+ break;
+ }
+ if (i == NI_TIMEOUT) {
+ rt_printk
+ ("ni_mio_common: timeout in ni_ai_insn_read\n");
+ return -ETIME;
+ }
+ if (boardtype.reg_type & ni_reg_m_series_mask) {
+ data[n] =
+ ni_readl(M_Offset_AI_FIFO_Data) & mask;
+ } else {
+ d = ni_readw(ADC_FIFO_Data_Register);
+ d += signbits; /* subtle: needs to be short addition */
+ data[n] = d;
+ }
+ }
+ }
+ return insn->n;
+}
+
+void ni_prime_channelgain_list(struct comedi_device * dev)
+{
+ int i;
+ devpriv->stc_writew(dev, AI_CONVERT_Pulse, AI_Command_1_Register);
+ for (i = 0; i < NI_TIMEOUT; ++i) {
+ if (!(devpriv->stc_readw(dev,
+ AI_Status_1_Register) &
+ AI_FIFO_Empty_St)) {
+ devpriv->stc_writew(dev, 1, ADC_FIFO_Clear);
+ return;
+ }
+ comedi_udelay(1);
+ }
+ rt_printk("ni_mio_common: timeout loading channel/gain list\n");
+}
+
+static void ni_m_series_load_channelgain_list(struct comedi_device * dev,
+ unsigned int n_chan, unsigned int *list)
+{
+ unsigned int chan, range, aref;
+ unsigned int i;
+ unsigned offset;
+ unsigned int dither;
+ unsigned range_code;
+
+ devpriv->stc_writew(dev, 1, Configuration_Memory_Clear);
+
+// offset = 1 << (boardtype.adbits - 1);
+ if ((list[0] & CR_ALT_SOURCE)) {
+ unsigned bypass_bits;
+ chan = CR_CHAN(list[0]);
+ range = CR_RANGE(list[0]);
+ range_code = ni_gainlkup[boardtype.gainlkup][range];
+ dither = ((list[0] & CR_ALT_FILTER) != 0);
+ bypass_bits = MSeries_AI_Bypass_Config_FIFO_Bit;
+ bypass_bits |= chan;
+ bypass_bits |=
+ (devpriv->
+ ai_calib_source) & (MSeries_AI_Bypass_Cal_Sel_Pos_Mask |
+ MSeries_AI_Bypass_Cal_Sel_Neg_Mask |
+ MSeries_AI_Bypass_Mode_Mux_Mask |
+ MSeries_AO_Bypass_AO_Cal_Sel_Mask);
+ bypass_bits |= MSeries_AI_Bypass_Gain_Bits(range_code);
+ if (dither)
+ bypass_bits |= MSeries_AI_Bypass_Dither_Bit;
+ // don't use 2's complement encoding
+ bypass_bits |= MSeries_AI_Bypass_Polarity_Bit;
+ ni_writel(bypass_bits, M_Offset_AI_Config_FIFO_Bypass);
+ } else {
+ ni_writel(0, M_Offset_AI_Config_FIFO_Bypass);
+ }
+ offset = 0;
+ for (i = 0; i < n_chan; i++) {
+ unsigned config_bits = 0;
+ chan = CR_CHAN(list[i]);
+ aref = CR_AREF(list[i]);
+ range = CR_RANGE(list[i]);
+ dither = ((list[i] & CR_ALT_FILTER) != 0);
+
+ range_code = ni_gainlkup[boardtype.gainlkup][range];
+ devpriv->ai_offset[i] = offset;
+ switch (aref) {
+ case AREF_DIFF:
+ config_bits |=
+ MSeries_AI_Config_Channel_Type_Differential_Bits;
+ break;
+ case AREF_COMMON:
+ config_bits |=
+ MSeries_AI_Config_Channel_Type_Common_Ref_Bits;
+ break;
+ case AREF_GROUND:
+ config_bits |=
+ MSeries_AI_Config_Channel_Type_Ground_Ref_Bits;
+ break;
+ case AREF_OTHER:
+ break;
+ }
+ config_bits |= MSeries_AI_Config_Channel_Bits(chan);
+ config_bits |=
+ MSeries_AI_Config_Bank_Bits(boardtype.reg_type, chan);
+ config_bits |= MSeries_AI_Config_Gain_Bits(range_code);
+ if (i == n_chan - 1)
+ config_bits |= MSeries_AI_Config_Last_Channel_Bit;
+ if (dither)
+ config_bits |= MSeries_AI_Config_Dither_Bit;
+ // don't use 2's complement encoding
+ config_bits |= MSeries_AI_Config_Polarity_Bit;
+ ni_writew(config_bits, M_Offset_AI_Config_FIFO_Data);
+ }
+ ni_prime_channelgain_list(dev);
+}
+
+/*
+ * Notes on the 6110 and 6111:
+ * These boards a slightly different than the rest of the series, since
+ * they have multiple A/D converters.
+ * From the driver side, the configuration memory is a
+ * little different.
+ * Configuration Memory Low:
+ * bits 15-9: same
+ * bit 8: unipolar/bipolar (should be 0 for bipolar)
+ * bits 0-3: gain. This is 4 bits instead of 3 for the other boards
+ * 1001 gain=0.1 (+/- 50)
+ * 1010 0.2
+ * 1011 0.1
+ * 0001 1
+ * 0010 2
+ * 0011 5
+ * 0100 10
+ * 0101 20
+ * 0110 50
+ * Configuration Memory High:
+ * bits 12-14: Channel Type
+ * 001 for differential
+ * 000 for calibration
+ * bit 11: coupling (this is not currently handled)
+ * 1 AC coupling
+ * 0 DC coupling
+ * bits 0-2: channel
+ * valid channels are 0-3
+ */
+static void ni_load_channelgain_list(struct comedi_device * dev, unsigned int n_chan,
+ unsigned int *list)
+{
+ unsigned int chan, range, aref;
+ unsigned int i;
+ unsigned int hi, lo;
+ unsigned offset;
+ unsigned int dither;
+
+ if (boardtype.reg_type & ni_reg_m_series_mask) {
+ ni_m_series_load_channelgain_list(dev, n_chan, list);
+ return;
+ }
+ if (n_chan == 1 && (boardtype.reg_type != ni_reg_611x)
+ && (boardtype.reg_type != ni_reg_6143)) {
+ if (devpriv->changain_state
+ && devpriv->changain_spec == list[0]) {
+ // ready to go.
+ return;
+ }
+ devpriv->changain_state = 1;
+ devpriv->changain_spec = list[0];
+ } else {
+ devpriv->changain_state = 0;
+ }
+
+ devpriv->stc_writew(dev, 1, Configuration_Memory_Clear);
+
+ // Set up Calibration mode if required
+ if (boardtype.reg_type == ni_reg_6143) {
+ if ((list[0] & CR_ALT_SOURCE)
+ && !devpriv->ai_calib_source_enabled) {
+ // Strobe Relay enable bit
+ ni_writew(devpriv->
+ ai_calib_source |
+ Calibration_Channel_6143_RelayOn,
+ Calibration_Channel_6143);
+ ni_writew(devpriv->ai_calib_source,
+ Calibration_Channel_6143);
+ devpriv->ai_calib_source_enabled = 1;
+ msleep_interruptible(100); // Allow relays to change
+ } else if (!(list[0] & CR_ALT_SOURCE)
+ && devpriv->ai_calib_source_enabled) {
+ // Strobe Relay disable bit
+ ni_writew(devpriv->
+ ai_calib_source |
+ Calibration_Channel_6143_RelayOff,
+ Calibration_Channel_6143);
+ ni_writew(devpriv->ai_calib_source,
+ Calibration_Channel_6143);
+ devpriv->ai_calib_source_enabled = 0;
+ msleep_interruptible(100); // Allow relays to change
+ }
+ }
+
+ offset = 1 << (boardtype.adbits - 1);
+ for (i = 0; i < n_chan; i++) {
+ if ((boardtype.reg_type != ni_reg_6143)
+ && (list[i] & CR_ALT_SOURCE)) {
+ chan = devpriv->ai_calib_source;
+ } else {
+ chan = CR_CHAN(list[i]);
+ }
+ aref = CR_AREF(list[i]);
+ range = CR_RANGE(list[i]);
+ dither = ((list[i] & CR_ALT_FILTER) != 0);
+
+ /* fix the external/internal range differences */
+ range = ni_gainlkup[boardtype.gainlkup][range];
+ if (boardtype.reg_type == ni_reg_611x)
+ devpriv->ai_offset[i] = offset;
+ else
+ devpriv->ai_offset[i] = (range & 0x100) ? 0 : offset;
+
+ hi = 0;
+ if ((list[i] & CR_ALT_SOURCE)) {
+ if (boardtype.reg_type == ni_reg_611x)
+ ni_writew(CR_CHAN(list[i]) & 0x0003,
+ Calibration_Channel_Select_611x);
+ } else {
+ if (boardtype.reg_type == ni_reg_611x)
+ aref = AREF_DIFF;
+ else if (boardtype.reg_type == ni_reg_6143)
+ aref = AREF_OTHER;
+ switch (aref) {
+ case AREF_DIFF:
+ hi |= AI_DIFFERENTIAL;
+ break;
+ case AREF_COMMON:
+ hi |= AI_COMMON;
+ break;
+ case AREF_GROUND:
+ hi |= AI_GROUND;
+ break;
+ case AREF_OTHER:
+ break;
+ }
+ }
+ hi |= AI_CONFIG_CHANNEL(chan);
+
+ ni_writew(hi, Configuration_Memory_High);
+
+ if (boardtype.reg_type != ni_reg_6143) {
+ lo = range;
+ if (i == n_chan - 1)
+ lo |= AI_LAST_CHANNEL;
+ if (dither)
+ lo |= AI_DITHER;
+
+ ni_writew(lo, Configuration_Memory_Low);
+ }
+ }
+
+ /* prime the channel/gain list */
+ if ((boardtype.reg_type != ni_reg_611x)
+ && (boardtype.reg_type != ni_reg_6143)) {
+ ni_prime_channelgain_list(dev);
+ }
+}
+
+static int ni_ns_to_timer(const struct comedi_device * dev, unsigned nanosec,
+ int round_mode)
+{
+ int divider;
+ switch (round_mode) {
+ case TRIG_ROUND_NEAREST:
+ default:
+ divider = (nanosec + devpriv->clock_ns / 2) / devpriv->clock_ns;
+ break;
+ case TRIG_ROUND_DOWN:
+ divider = (nanosec) / devpriv->clock_ns;
+ break;
+ case TRIG_ROUND_UP:
+ divider = (nanosec + devpriv->clock_ns - 1) / devpriv->clock_ns;
+ break;
+ }
+ return divider - 1;
+}
+
+static unsigned ni_timer_to_ns(const struct comedi_device * dev, int timer)
+{
+ return devpriv->clock_ns * (timer + 1);
+}
+
+static unsigned ni_min_ai_scan_period_ns(struct comedi_device * dev,
+ unsigned num_channels)
+{
+ switch (boardtype.reg_type) {
+ case ni_reg_611x:
+ case ni_reg_6143:
+ // simultaneously-sampled inputs
+ return boardtype.ai_speed;
+ break;
+ default:
+ // multiplexed inputs
+ break;
+ };
+ return boardtype.ai_speed * num_channels;
+}
+
+static int ni_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+ int sources;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ if ((cmd->flags & CMDF_WRITE)) {
+ cmd->flags &= ~CMDF_WRITE;
+ }
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_INT | TRIG_EXT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ sources = TRIG_TIMER | TRIG_EXT;
+ if ((boardtype.reg_type == ni_reg_611x)
+ || (boardtype.reg_type == ni_reg_6143))
+ sources |= TRIG_NOW;
+ cmd->convert_src &= sources;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ /* note that mutual compatiblity is not an issue here */
+ if (cmd->start_src != TRIG_NOW &&
+ cmd->start_src != TRIG_INT && cmd->start_src != TRIG_EXT)
+ err++;
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_EXT &&
+ cmd->scan_begin_src != TRIG_OTHER)
+ err++;
+ if (cmd->convert_src != TRIG_TIMER &&
+ cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_src == TRIG_EXT) {
+ /* external trigger */
+ unsigned int tmp = CR_CHAN(cmd->start_arg);
+
+ if (tmp > 16)
+ tmp = 16;
+ tmp |= (cmd->start_arg & (CR_INVERT | CR_EDGE));
+ if (cmd->start_arg != tmp) {
+ cmd->start_arg = tmp;
+ err++;
+ }
+ } else {
+ if (cmd->start_arg != 0) {
+ /* true for both TRIG_NOW and TRIG_INT */
+ cmd->start_arg = 0;
+ err++;
+ }
+ }
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->scan_begin_arg < ni_min_ai_scan_period_ns(dev,
+ cmd->chanlist_len)) {
+ cmd->scan_begin_arg =
+ ni_min_ai_scan_period_ns(dev,
+ cmd->chanlist_len);
+ err++;
+ }
+ if (cmd->scan_begin_arg > devpriv->clock_ns * 0xffffff) {
+ cmd->scan_begin_arg = devpriv->clock_ns * 0xffffff;
+ err++;
+ }
+ } else if (cmd->scan_begin_src == TRIG_EXT) {
+ /* external trigger */
+ unsigned int tmp = CR_CHAN(cmd->scan_begin_arg);
+
+ if (tmp > 16)
+ tmp = 16;
+ tmp |= (cmd->scan_begin_arg & (CR_INVERT | CR_EDGE));
+ if (cmd->scan_begin_arg != tmp) {
+ cmd->scan_begin_arg = tmp;
+ err++;
+ }
+ } else { /* TRIG_OTHER */
+ if (cmd->scan_begin_arg) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if ((boardtype.reg_type == ni_reg_611x)
+ || (boardtype.reg_type == ni_reg_6143)) {
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ } else {
+ if (cmd->convert_arg < boardtype.ai_speed) {
+ cmd->convert_arg = boardtype.ai_speed;
+ err++;
+ }
+ if (cmd->convert_arg > devpriv->clock_ns * 0xffff) {
+ cmd->convert_arg = devpriv->clock_ns * 0xffff;
+ err++;
+ }
+ }
+ } else if (cmd->convert_src == TRIG_EXT) {
+ /* external trigger */
+ unsigned int tmp = CR_CHAN(cmd->convert_arg);
+
+ if (tmp > 16)
+ tmp = 16;
+ tmp |= (cmd->convert_arg & (CR_ALT_FILTER | CR_INVERT));
+ if (cmd->convert_arg != tmp) {
+ cmd->convert_arg = tmp;
+ err++;
+ }
+ } else if (cmd->convert_src == TRIG_NOW) {
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ unsigned int max_count = 0x01000000;
+
+ if (boardtype.reg_type == ni_reg_611x)
+ max_count -= num_adc_stages_611x;
+ if (cmd->stop_arg > max_count) {
+ cmd->stop_arg = max_count;
+ err++;
+ }
+ if (cmd->stop_arg < 1) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ cmd->scan_begin_arg =
+ ni_timer_to_ns(dev, ni_ns_to_timer(dev,
+ cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK));
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if ((boardtype.reg_type != ni_reg_611x)
+ && (boardtype.reg_type != ni_reg_6143)) {
+ tmp = cmd->convert_arg;
+ cmd->convert_arg =
+ ni_timer_to_ns(dev, ni_ns_to_timer(dev,
+ cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK));
+ if (tmp != cmd->convert_arg)
+ err++;
+ if (cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->scan_begin_arg <
+ cmd->convert_arg * cmd->scan_end_arg) {
+ cmd->scan_begin_arg =
+ cmd->convert_arg * cmd->scan_end_arg;
+ err++;
+ }
+ }
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int ni_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ const struct comedi_cmd *cmd = &s->async->cmd;
+ int timer;
+ int mode1 = 0; /* mode1 is needed for both stop and convert */
+ int mode2 = 0;
+ int start_stop_select = 0;
+ unsigned int stop_count;
+ int interrupt_a_enable = 0;
+
+ MDPRINTK("ni_ai_cmd\n");
+ if (dev->irq == 0) {
+ comedi_error(dev, "cannot run command without an irq");
+ return -EIO;
+ }
+ ni_clear_ai_fifo(dev);
+
+ ni_load_channelgain_list(dev, cmd->chanlist_len, cmd->chanlist);
+
+ /* start configuration */
+ devpriv->stc_writew(dev, AI_Configuration_Start, Joint_Reset_Register);
+
+ /* disable analog triggering for now, since it
+ * interferes with the use of pfi0 */
+ devpriv->an_trig_etc_reg &= ~Analog_Trigger_Enable;
+ devpriv->stc_writew(dev, devpriv->an_trig_etc_reg,
+ Analog_Trigger_Etc_Register);
+
+ switch (cmd->start_src) {
+ case TRIG_INT:
+ case TRIG_NOW:
+ devpriv->stc_writew(dev, AI_START2_Select(0) |
+ AI_START1_Sync | AI_START1_Edge | AI_START1_Select(0),
+ AI_Trigger_Select_Register);
+ break;
+ case TRIG_EXT:
+ {
+ int chan = CR_CHAN(cmd->start_arg);
+ unsigned int bits = AI_START2_Select(0) |
+ AI_START1_Sync | AI_START1_Select(chan + 1);
+
+ if (cmd->start_arg & CR_INVERT)
+ bits |= AI_START1_Polarity;
+ if (cmd->start_arg & CR_EDGE)
+ bits |= AI_START1_Edge;
+ devpriv->stc_writew(dev, bits,
+ AI_Trigger_Select_Register);
+ break;
+ }
+ }
+
+ mode2 &= ~AI_Pre_Trigger;
+ mode2 &= ~AI_SC_Initial_Load_Source;
+ mode2 &= ~AI_SC_Reload_Mode;
+ devpriv->stc_writew(dev, mode2, AI_Mode_2_Register);
+
+ if (cmd->chanlist_len == 1 || (boardtype.reg_type == ni_reg_611x)
+ || (boardtype.reg_type == ni_reg_6143)) {
+ start_stop_select |= AI_STOP_Polarity;
+ start_stop_select |= AI_STOP_Select(31); // logic low
+ start_stop_select |= AI_STOP_Sync;
+ } else {
+ start_stop_select |= AI_STOP_Select(19); // ai configuration memory
+ }
+ devpriv->stc_writew(dev, start_stop_select,
+ AI_START_STOP_Select_Register);
+
+ devpriv->ai_cmd2 = 0;
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ stop_count = cmd->stop_arg - 1;
+
+ if (boardtype.reg_type == ni_reg_611x) {
+ // have to take 3 stage adc pipeline into account
+ stop_count += num_adc_stages_611x;
+ }
+ /* stage number of scans */
+ devpriv->stc_writel(dev, stop_count, AI_SC_Load_A_Registers);
+
+ mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Trigger_Once;
+ devpriv->stc_writew(dev, mode1, AI_Mode_1_Register);
+ /* load SC (Scan Count) */
+ devpriv->stc_writew(dev, AI_SC_Load, AI_Command_1_Register);
+
+ devpriv->ai_continuous = 0;
+ if (stop_count == 0) {
+ devpriv->ai_cmd2 |= AI_End_On_End_Of_Scan;
+ interrupt_a_enable |= AI_STOP_Interrupt_Enable;
+ // this is required to get the last sample for chanlist_len > 1, not sure why
+ if (cmd->chanlist_len > 1)
+ start_stop_select |=
+ AI_STOP_Polarity | AI_STOP_Edge;
+ }
+ break;
+ case TRIG_NONE:
+ /* stage number of scans */
+ devpriv->stc_writel(dev, 0, AI_SC_Load_A_Registers);
+
+ mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Continuous;
+ devpriv->stc_writew(dev, mode1, AI_Mode_1_Register);
+
+ /* load SC (Scan Count) */
+ devpriv->stc_writew(dev, AI_SC_Load, AI_Command_1_Register);
+
+ devpriv->ai_continuous = 1;
+
+ break;
+ }
+
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ /*
+ stop bits for non 611x boards
+ 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)
+ */
+ start_stop_select |= AI_START_Edge | AI_START_Sync;
+ devpriv->stc_writew(dev, start_stop_select,
+ AI_START_STOP_Select_Register);
+
+ mode2 |= AI_SI_Reload_Mode(0);
+ /* AI_SI_Initial_Load_Source=A */
+ mode2 &= ~AI_SI_Initial_Load_Source;
+ //mode2 |= AI_SC_Reload_Mode;
+ devpriv->stc_writew(dev, mode2, AI_Mode_2_Register);
+
+ /* load SI */
+ timer = ni_ns_to_timer(dev, cmd->scan_begin_arg,
+ TRIG_ROUND_NEAREST);
+ devpriv->stc_writel(dev, timer, AI_SI_Load_A_Registers);
+ devpriv->stc_writew(dev, AI_SI_Load, AI_Command_1_Register);
+ break;
+ case TRIG_EXT:
+ if (cmd->scan_begin_arg & CR_EDGE)
+ start_stop_select |= AI_START_Edge;
+ /* AI_START_Polarity==1 is falling edge */
+ if (cmd->scan_begin_arg & CR_INVERT)
+ start_stop_select |= AI_START_Polarity;
+ if (cmd->scan_begin_src != cmd->convert_src ||
+ (cmd->scan_begin_arg & ~CR_EDGE) !=
+ (cmd->convert_arg & ~CR_EDGE))
+ start_stop_select |= AI_START_Sync;
+ start_stop_select |=
+ AI_START_Select(1 + CR_CHAN(cmd->scan_begin_arg));
+ devpriv->stc_writew(dev, start_stop_select,
+ AI_START_STOP_Select_Register);
+ break;
+ }
+
+ switch (cmd->convert_src) {
+ case TRIG_TIMER:
+ case TRIG_NOW:
+ if (cmd->convert_arg == 0 || cmd->convert_src == TRIG_NOW)
+ timer = 1;
+ else
+ timer = ni_ns_to_timer(dev, cmd->convert_arg,
+ TRIG_ROUND_NEAREST);
+ devpriv->stc_writew(dev, 1, AI_SI2_Load_A_Register); /* 0,0 does not work. */
+ devpriv->stc_writew(dev, timer, AI_SI2_Load_B_Register);
+
+ /* AI_SI2_Reload_Mode = alternate */
+ /* AI_SI2_Initial_Load_Source = A */
+ mode2 &= ~AI_SI2_Initial_Load_Source;
+ mode2 |= AI_SI2_Reload_Mode;
+ devpriv->stc_writew(dev, mode2, AI_Mode_2_Register);
+
+ /* AI_SI2_Load */
+ devpriv->stc_writew(dev, AI_SI2_Load, AI_Command_1_Register);
+
+ mode2 |= AI_SI2_Reload_Mode; // alternate
+ mode2 |= AI_SI2_Initial_Load_Source; // B
+
+ devpriv->stc_writew(dev, mode2, AI_Mode_2_Register);
+ break;
+ case TRIG_EXT:
+ mode1 |= AI_CONVERT_Source_Select(1 + cmd->convert_arg);
+ if ((cmd->convert_arg & CR_INVERT) == 0)
+ mode1 |= AI_CONVERT_Source_Polarity;
+ devpriv->stc_writew(dev, mode1, AI_Mode_1_Register);
+
+ mode2 |= AI_Start_Stop_Gate_Enable | AI_SC_Gate_Enable;
+ devpriv->stc_writew(dev, mode2, AI_Mode_2_Register);
+
+ break;
+ }
+
+ if (dev->irq) {
+
+ /* interrupt on FIFO, errors, SC_TC */
+ interrupt_a_enable |= AI_Error_Interrupt_Enable |
+ AI_SC_TC_Interrupt_Enable;
+
+#ifndef PCIDMA
+ interrupt_a_enable |= AI_FIFO_Interrupt_Enable;
+#endif
+
+ if (cmd->flags & TRIG_WAKE_EOS
+ || (devpriv->ai_cmd2 & AI_End_On_End_Of_Scan)) {
+ /* wake on end-of-scan */
+ devpriv->aimode = AIMODE_SCAN;
+ } else {
+ devpriv->aimode = AIMODE_HALF_FULL;
+ }
+
+ switch (devpriv->aimode) {
+ case AIMODE_HALF_FULL:
+ /*generate FIFO interrupts and DMA requests on half-full */
+#ifdef PCIDMA
+ devpriv->stc_writew(dev, AI_FIFO_Mode_HF_to_E,
+ AI_Mode_3_Register);
+#else
+ devpriv->stc_writew(dev, AI_FIFO_Mode_HF,
+ AI_Mode_3_Register);
+#endif
+ break;
+ case AIMODE_SAMPLE:
+ /*generate FIFO interrupts on non-empty */
+ devpriv->stc_writew(dev, AI_FIFO_Mode_NE,
+ AI_Mode_3_Register);
+ break;
+ case AIMODE_SCAN:
+#ifdef PCIDMA
+ devpriv->stc_writew(dev, AI_FIFO_Mode_NE,
+ AI_Mode_3_Register);
+#else
+ devpriv->stc_writew(dev, AI_FIFO_Mode_HF,
+ AI_Mode_3_Register);
+#endif
+ interrupt_a_enable |= AI_STOP_Interrupt_Enable;
+ break;
+ default:
+ break;
+ }
+
+ devpriv->stc_writew(dev, AI_Error_Interrupt_Ack | AI_STOP_Interrupt_Ack | AI_START_Interrupt_Ack | AI_START2_Interrupt_Ack | AI_START1_Interrupt_Ack | AI_SC_TC_Interrupt_Ack | AI_SC_TC_Error_Confirm, Interrupt_A_Ack_Register); /* clear interrupts */
+
+ ni_set_bits(dev, Interrupt_A_Enable_Register,
+ interrupt_a_enable, 1);
+
+ MDPRINTK("Interrupt_A_Enable_Register = 0x%04x\n",
+ devpriv->int_a_enable_reg);
+ } else {
+ /* interrupt on nothing */
+ ni_set_bits(dev, Interrupt_A_Enable_Register, ~0, 0);
+
+ /* XXX start polling if necessary */
+ MDPRINTK("interrupting on nothing\n");
+ }
+
+ /* end configuration */
+ devpriv->stc_writew(dev, AI_Configuration_End, Joint_Reset_Register);
+
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ devpriv->stc_writew(dev,
+ AI_SI2_Arm | AI_SI_Arm | AI_DIV_Arm | AI_SC_Arm,
+ AI_Command_1_Register);
+ break;
+ case TRIG_EXT:
+ /* XXX AI_SI_Arm? */
+ devpriv->stc_writew(dev,
+ AI_SI2_Arm | AI_SI_Arm | AI_DIV_Arm | AI_SC_Arm,
+ AI_Command_1_Register);
+ break;
+ }
+
+#ifdef PCIDMA
+ {
+ int retval = ni_ai_setup_MITE_dma(dev);
+ if (retval)
+ return retval;
+ }
+ //mite_dump_regs(devpriv->mite);
+#endif
+
+ switch (cmd->start_src) {
+ case TRIG_NOW:
+ /* AI_START1_Pulse */
+ devpriv->stc_writew(dev, AI_START1_Pulse | devpriv->ai_cmd2,
+ AI_Command_2_Register);
+ s->async->inttrig = NULL;
+ break;
+ case TRIG_EXT:
+ s->async->inttrig = NULL;
+ break;
+ case TRIG_INT:
+ s->async->inttrig = &ni_ai_inttrig;
+ break;
+ }
+
+ MDPRINTK("exit ni_ai_cmd\n");
+
+ return 0;
+}
+
+static int ni_ai_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trignum)
+{
+ if (trignum != 0)
+ return -EINVAL;
+
+ devpriv->stc_writew(dev, AI_START1_Pulse | devpriv->ai_cmd2,
+ AI_Command_2_Register);
+ s->async->inttrig = NULL;
+
+ return 1;
+}
+
+static int ni_ai_config_analog_trig(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int ni_ai_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n < 1)
+ return -EINVAL;
+
+ switch (data[0]) {
+ case INSN_CONFIG_ANALOG_TRIG:
+ return ni_ai_config_analog_trig(dev, s, insn, data);
+ case INSN_CONFIG_ALT_SOURCE:
+ if (boardtype.reg_type & ni_reg_m_series_mask) {
+ if (data[1] & ~(MSeries_AI_Bypass_Cal_Sel_Pos_Mask |
+ MSeries_AI_Bypass_Cal_Sel_Neg_Mask |
+ MSeries_AI_Bypass_Mode_Mux_Mask |
+ MSeries_AO_Bypass_AO_Cal_Sel_Mask)) {
+ return -EINVAL;
+ }
+ devpriv->ai_calib_source = data[1];
+ } else if (boardtype.reg_type == ni_reg_6143) {
+ unsigned int calib_source;
+
+ calib_source = data[1] & 0xf;
+
+ if (calib_source > 0xF)
+ return -EINVAL;
+
+ devpriv->ai_calib_source = calib_source;
+ ni_writew(calib_source, Calibration_Channel_6143);
+ } else {
+ unsigned int calib_source;
+ unsigned int calib_source_adjust;
+
+ calib_source = data[1] & 0xf;
+ calib_source_adjust = (data[1] >> 4) & 0xff;
+
+ if (calib_source >= 8)
+ return -EINVAL;
+ devpriv->ai_calib_source = calib_source;
+ if (boardtype.reg_type == ni_reg_611x) {
+ ni_writeb(calib_source_adjust,
+ Cal_Gain_Select_611x);
+ }
+ }
+ return 2;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ni_ai_config_analog_trig(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int a, b, modebits;
+ int err = 0;
+
+ /* data[1] is flags
+ * data[2] is analog line
+ * data[3] is set level
+ * data[4] is reset level */
+ if (!boardtype.has_analog_trig)
+ return -EINVAL;
+ if ((data[1] & 0xffff0000) != COMEDI_EV_SCAN_BEGIN) {
+ data[1] &= (COMEDI_EV_SCAN_BEGIN | 0xffff);
+ err++;
+ }
+ if (data[2] >= boardtype.n_adchan) {
+ data[2] = boardtype.n_adchan - 1;
+ err++;
+ }
+ if (data[3] > 255) { /* a */
+ data[3] = 255;
+ err++;
+ }
+ if (data[4] > 255) { /* b */
+ data[4] = 255;
+ err++;
+ }
+ /*
+ * 00 ignore
+ * 01 set
+ * 10 reset
+ *
+ * modes:
+ * 1 level: +b- +a-
+ * high mode 00 00 01 10
+ * low mode 00 00 10 01
+ * 2 level: (a<b)
+ * hysteresis low mode 10 00 00 01
+ * hysteresis high mode 01 00 00 10
+ * middle mode 10 01 01 10
+ */
+
+ a = data[3];
+ b = data[4];
+ modebits = data[1] & 0xff;
+ if (modebits & 0xf0) {
+ /* two level mode */
+ if (b < a) {
+ /* swap order */
+ a = data[4];
+ b = data[3];
+ modebits =
+ ((data[1] & 0xf) << 4) | ((data[1] & 0xf0) >>
+ 4);
+ }
+ devpriv->atrig_low = a;
+ devpriv->atrig_high = b;
+ switch (modebits) {
+ case 0x81: /* low hysteresis mode */
+ devpriv->atrig_mode = 6;
+ break;
+ case 0x42: /* high hysteresis mode */
+ devpriv->atrig_mode = 3;
+ break;
+ case 0x96: /* middle window mode */
+ devpriv->atrig_mode = 2;
+ break;
+ default:
+ data[1] &= ~0xff;
+ err++;
+ }
+ } else {
+ /* one level mode */
+ if (b != 0) {
+ data[4] = 0;
+ err++;
+ }
+ switch (modebits) {
+ case 0x06: /* high window mode */
+ devpriv->atrig_high = a;
+ devpriv->atrig_mode = 0;
+ break;
+ case 0x09: /* low window mode */
+ devpriv->atrig_low = a;
+ devpriv->atrig_mode = 1;
+ break;
+ default:
+ data[1] &= ~0xff;
+ err++;
+ }
+ }
+ if (err)
+ return -EAGAIN;
+ return 5;
+}
+
+/* munge data from unsigned to 2's complement for analog output bipolar modes */
+static void ni_ao_munge(struct comedi_device * dev, struct comedi_subdevice * s,
+ void *data, unsigned int num_bytes, unsigned int chan_index)
+{
+ struct comedi_async *async = s->async;
+ unsigned int range;
+ unsigned int i;
+ unsigned int offset;
+ unsigned int length = num_bytes / sizeof(short);
+ short *array = data;
+
+ offset = 1 << (boardtype.aobits - 1);
+ for (i = 0; i < length; i++) {
+ range = CR_RANGE(async->cmd.chanlist[chan_index]);
+ if (boardtype.ao_unipolar == 0 || (range & 1) == 0)
+ array[i] -= offset;
+#ifdef PCIDMA
+ array[i] = cpu_to_le16(array[i]);
+#endif
+ chan_index++;
+ chan_index %= async->cmd.chanlist_len;
+ }
+}
+
+static int ni_m_series_ao_config_chanlist(struct comedi_device * dev,
+ struct comedi_subdevice * s, unsigned int chanspec[], unsigned int n_chans,
+ int timed)
+{
+ unsigned int range;
+ unsigned int chan;
+ unsigned int conf;
+ int i;
+ int invert = 0;
+
+ if(timed) {
+ for (i = 0; i < boardtype.n_aochan; ++i) {
+ devpriv->ao_conf[i] &= ~MSeries_AO_Update_Timed_Bit;
+ ni_writeb(devpriv->ao_conf[i], M_Offset_AO_Config_Bank(i));
+ ni_writeb(0xf, M_Offset_AO_Waveform_Order(i));
+ }
+ }
+ for (i = 0; i < n_chans; i++) {
+ const struct comedi_krange *krange;
+ chan = CR_CHAN(chanspec[i]);
+ range = CR_RANGE(chanspec[i]);
+ krange = s->range_table->range + range;
+ invert = 0;
+ conf = 0;
+ switch (krange->max - krange->min) {
+ case 20000000:
+ conf |= MSeries_AO_DAC_Reference_10V_Internal_Bits;
+ ni_writeb(0, M_Offset_AO_Reference_Attenuation(chan));
+ break;
+ case 10000000:
+ conf |= MSeries_AO_DAC_Reference_5V_Internal_Bits;
+ ni_writeb(0, M_Offset_AO_Reference_Attenuation(chan));
+ break;
+ case 4000000:
+ conf |= MSeries_AO_DAC_Reference_10V_Internal_Bits;
+ ni_writeb(MSeries_Attenuate_x5_Bit,
+ M_Offset_AO_Reference_Attenuation(chan));
+ break;
+ case 2000000:
+ conf |= MSeries_AO_DAC_Reference_5V_Internal_Bits;
+ ni_writeb(MSeries_Attenuate_x5_Bit,
+ M_Offset_AO_Reference_Attenuation(chan));
+ break;
+ default:
+ rt_printk("%s: bug! unhandled ao reference voltage\n",
+ __FUNCTION__);
+ break;
+ }
+ switch (krange->max + krange->min) {
+ case 0:
+ conf |= MSeries_AO_DAC_Offset_0V_Bits;
+ break;
+ case 10000000:
+ conf |= MSeries_AO_DAC_Offset_5V_Bits;
+ break;
+ default:
+ rt_printk("%s: bug! unhandled ao offset voltage\n",
+ __FUNCTION__);
+ break;
+ }
+ if (timed)
+ conf |= MSeries_AO_Update_Timed_Bit;
+ ni_writeb(conf, M_Offset_AO_Config_Bank(chan));
+ devpriv->ao_conf[chan] = conf;
+ ni_writeb(i, M_Offset_AO_Waveform_Order(chan));
+ }
+ return invert;
+}
+
+static int ni_old_ao_config_chanlist(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int chanspec[], unsigned int n_chans)
+{
+ unsigned int range;
+ unsigned int chan;
+ unsigned int conf;
+ int i;
+ int invert = 0;
+
+ for (i = 0; i < n_chans; i++) {
+ chan = CR_CHAN(chanspec[i]);
+ range = CR_RANGE(chanspec[i]);
+ conf = AO_Channel(chan);
+
+ if (boardtype.ao_unipolar) {
+ if ((range & 1) == 0) {
+ conf |= AO_Bipolar;
+ invert = (1 << (boardtype.aobits - 1));
+ } else {
+ invert = 0;
+ }
+ if (range & 2)
+ conf |= AO_Ext_Ref;
+ } else {
+ conf |= AO_Bipolar;
+ invert = (1 << (boardtype.aobits - 1));
+ }
+
+ /* not all boards can deglitch, but this shouldn't hurt */
+ if (chanspec[i] & CR_DEGLITCH)
+ conf |= AO_Deglitch;
+
+ /* analog reference */
+ /* AREF_OTHER connects AO ground to AI ground, i think */
+ conf |= (CR_AREF(chanspec[i]) ==
+ AREF_OTHER) ? AO_Ground_Ref : 0;
+
+ ni_writew(conf, AO_Configuration);
+ devpriv->ao_conf[chan] = conf;
+ }
+ return invert;
+}
+
+static int ni_ao_config_chanlist(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int chanspec[], unsigned int n_chans, int timed)
+{
+ if (boardtype.reg_type & ni_reg_m_series_mask)
+ return ni_m_series_ao_config_chanlist(dev, s, chanspec, n_chans,
+ timed);
+ else
+ return ni_old_ao_config_chanlist(dev, s, chanspec, n_chans);
+}
+static int ni_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = devpriv->ao[CR_CHAN(insn->chanspec)];
+
+ return 1;
+}
+
+static int ni_ao_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int invert;
+
+ invert = ni_ao_config_chanlist(dev, s, &insn->chanspec, 1, 0);
+
+ devpriv->ao[chan] = data[0];
+
+ if (boardtype.reg_type & ni_reg_m_series_mask) {
+ ni_writew(data[0], M_Offset_DAC_Direct_Data(chan));
+ } else
+ ni_writew(data[0] ^ invert,
+ (chan) ? DAC1_Direct_Data : DAC0_Direct_Data);
+
+ return 1;
+}
+
+static int ni_ao_insn_write_671x(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int invert;
+
+ ao_win_out(1 << chan, AO_Immediate_671x);
+ invert = 1 << (boardtype.aobits - 1);
+
+ ni_ao_config_chanlist(dev, s, &insn->chanspec, 1, 0);
+
+ devpriv->ao[chan] = data[0];
+ ao_win_out(data[0] ^ invert, DACx_Direct_Data_671x(chan));
+
+ return 1;
+}
+
+static int ni_ao_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ switch (data[0]) {
+ case INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE:
+ switch(data[1])
+ {
+ case COMEDI_OUTPUT:
+ data[2] = 1 + boardtype.ao_fifo_depth * sizeof(short);
+ if(devpriv->mite) data[2] += devpriv->mite->fifo_size;
+ break;
+ case COMEDI_INPUT:
+ data[2] = 0;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return 0;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ni_ao_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trignum)
+{
+ int ret;
+ int interrupt_b_bits;
+ int i;
+ static const int timeout = 1000;
+
+ if (trignum != 0)
+ return -EINVAL;
+
+ /* Null trig at beginning prevent ao start trigger from executing more than
+ once per command (and doing things like trying to allocate the ao dma channel
+ multiple times) */
+ s->async->inttrig = NULL;
+
+ ni_set_bits(dev, Interrupt_B_Enable_Register,
+ AO_FIFO_Interrupt_Enable | AO_Error_Interrupt_Enable, 0);
+ interrupt_b_bits = AO_Error_Interrupt_Enable;
+#ifdef PCIDMA
+ devpriv->stc_writew(dev, 1, DAC_FIFO_Clear);
+ if (boardtype.reg_type & ni_reg_6xxx_mask)
+ ni_ao_win_outl(dev, 0x6, AO_FIFO_Offset_Load_611x);
+ ret = ni_ao_setup_MITE_dma(dev);
+ if (ret)
+ return ret;
+ ret = ni_ao_wait_for_dma_load(dev);
+ if (ret < 0)
+ return ret;
+#else
+ ret = ni_ao_prep_fifo(dev, s);
+ if (ret == 0)
+ return -EPIPE;
+
+ interrupt_b_bits |= AO_FIFO_Interrupt_Enable;
+#endif
+
+ devpriv->stc_writew(dev, devpriv->ao_mode3 | AO_Not_An_UPDATE,
+ AO_Mode_3_Register);
+ devpriv->stc_writew(dev, devpriv->ao_mode3, AO_Mode_3_Register);
+ /* wait for DACs to be loaded */
+ for (i = 0; i < timeout; i++) {
+ comedi_udelay(1);
+ if ((devpriv->stc_readw(dev,
+ Joint_Status_2_Register) &
+ AO_TMRDACWRs_In_Progress_St) == 0)
+ break;
+ }
+ if (i == timeout) {
+ comedi_error(dev,
+ "timed out waiting for AO_TMRDACWRs_In_Progress_St to clear");
+ return -EIO;
+ }
+ // stc manual says we are need to clear error interrupt after AO_TMRDACWRs_In_Progress_St clears
+ devpriv->stc_writew(dev, AO_Error_Interrupt_Ack,
+ Interrupt_B_Ack_Register);
+
+ ni_set_bits(dev, Interrupt_B_Enable_Register, interrupt_b_bits, 1);
+
+ devpriv->stc_writew(dev,
+ devpriv->
+ ao_cmd1 | AO_UI_Arm | AO_UC_Arm | AO_BC_Arm |
+ AO_DAC1_Update_Mode | AO_DAC0_Update_Mode,
+ AO_Command_1_Register);
+
+ devpriv->stc_writew(dev, devpriv->ao_cmd2 | AO_START1_Pulse,
+ AO_Command_2_Register);
+
+ return 0;
+}
+
+static int ni_ao_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ const struct comedi_cmd *cmd = &s->async->cmd;
+ int bits;
+ int i;
+ unsigned trigvar;
+
+ if (dev->irq == 0) {
+ comedi_error(dev, "cannot run command without an irq");
+ return -EIO;
+ }
+
+ devpriv->stc_writew(dev, AO_Configuration_Start, Joint_Reset_Register);
+
+ devpriv->stc_writew(dev, AO_Disarm, AO_Command_1_Register);
+
+ if (boardtype.reg_type & ni_reg_6xxx_mask) {
+ ao_win_out(CLEAR_WG, AO_Misc_611x);
+
+ bits = 0;
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ int chan;
+
+ chan = CR_CHAN(cmd->chanlist[i]);
+ bits |= 1 << chan;
+ ao_win_out(chan, AO_Waveform_Generation_611x);
+ }
+ ao_win_out(bits, AO_Timed_611x);
+ }
+
+ ni_ao_config_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len, 1);
+
+ if (cmd->stop_src == TRIG_NONE) {
+ devpriv->ao_mode1 |= AO_Continuous;
+ devpriv->ao_mode1 &= ~AO_Trigger_Once;
+ } else {
+ devpriv->ao_mode1 &= ~AO_Continuous;
+ devpriv->ao_mode1 |= AO_Trigger_Once;
+ }
+ devpriv->stc_writew(dev, devpriv->ao_mode1, AO_Mode_1_Register);
+ switch (cmd->start_src) {
+ case TRIG_INT:
+ case TRIG_NOW:
+ devpriv->ao_trigger_select &=
+ ~(AO_START1_Polarity | AO_START1_Select(-1));
+ devpriv->ao_trigger_select |= AO_START1_Edge | AO_START1_Sync;
+ devpriv->stc_writew(dev, devpriv->ao_trigger_select,
+ AO_Trigger_Select_Register);
+ break;
+ case TRIG_EXT:
+ devpriv->ao_trigger_select = AO_START1_Select(CR_CHAN(cmd->start_arg)+1);
+ if (cmd->start_arg & CR_INVERT)
+ devpriv->ao_trigger_select |= AO_START1_Polarity; // 0=active high, 1=active low. see daq-stc 3-24 (p186)
+ if (cmd->start_arg & CR_EDGE)
+ devpriv->ao_trigger_select |= AO_START1_Edge; // 0=edge detection disabled, 1=enabled
+ devpriv->stc_writew(dev, devpriv->ao_trigger_select, AO_Trigger_Select_Register);
+ break;
+ default:
+ BUG();
+ break;
+ }
+ devpriv->ao_mode3 &= ~AO_Trigger_Length;
+ devpriv->stc_writew(dev, devpriv->ao_mode3, AO_Mode_3_Register);
+
+ devpriv->stc_writew(dev, devpriv->ao_mode1, AO_Mode_1_Register);
+ devpriv->ao_mode2 &= ~AO_BC_Initial_Load_Source;
+ devpriv->stc_writew(dev, devpriv->ao_mode2, AO_Mode_2_Register);
+ if (cmd->stop_src == TRIG_NONE) {
+ devpriv->stc_writel(dev, 0xffffff, AO_BC_Load_A_Register);
+ } else {
+ devpriv->stc_writel(dev, 0, AO_BC_Load_A_Register);
+ }
+ devpriv->stc_writew(dev, AO_BC_Load, AO_Command_1_Register);
+ devpriv->ao_mode2 &= ~AO_UC_Initial_Load_Source;
+ devpriv->stc_writew(dev, devpriv->ao_mode2, AO_Mode_2_Register);
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ if(boardtype.reg_type & ni_reg_m_series_mask)
+ {
+ // this is how the NI example code does it for m-series boards, verified correct with 6259
+ devpriv->stc_writel(dev, cmd->stop_arg - 1, AO_UC_Load_A_Register);
+ devpriv->stc_writew(dev, AO_UC_Load, AO_Command_1_Register);
+ }else
+ {
+ devpriv->stc_writel(dev, cmd->stop_arg, AO_UC_Load_A_Register);
+ devpriv->stc_writew(dev, AO_UC_Load, AO_Command_1_Register);
+ devpriv->stc_writel(dev, cmd->stop_arg - 1,
+ AO_UC_Load_A_Register);
+ }
+ break;
+ case TRIG_NONE:
+ devpriv->stc_writel(dev, 0xffffff, AO_UC_Load_A_Register);
+ devpriv->stc_writew(dev, AO_UC_Load, AO_Command_1_Register);
+ devpriv->stc_writel(dev, 0xffffff, AO_UC_Load_A_Register);
+ break;
+ default:
+ devpriv->stc_writel(dev, 0, AO_UC_Load_A_Register);
+ devpriv->stc_writew(dev, AO_UC_Load, AO_Command_1_Register);
+ devpriv->stc_writel(dev, cmd->stop_arg, AO_UC_Load_A_Register);
+ }
+
+ devpriv->ao_mode1 &=
+ ~(AO_UI_Source_Select(0x1f) | AO_UI_Source_Polarity |
+ AO_UPDATE_Source_Select(0x1f) | AO_UPDATE_Source_Polarity);
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ devpriv->ao_cmd2 &= ~AO_BC_Gate_Enable;
+ trigvar =
+ ni_ns_to_timer(dev, cmd->scan_begin_arg,
+ TRIG_ROUND_NEAREST);
+ devpriv->stc_writel(dev, 1, AO_UI_Load_A_Register);
+ devpriv->stc_writew(dev, AO_UI_Load, AO_Command_1_Register);
+ devpriv->stc_writel(dev, trigvar, AO_UI_Load_A_Register);
+ break;
+ case TRIG_EXT:
+ devpriv->ao_mode1 |=
+ AO_UPDATE_Source_Select(cmd->scan_begin_arg);
+ if (cmd->scan_begin_arg & CR_INVERT)
+ devpriv->ao_mode1 |= AO_UPDATE_Source_Polarity;
+ devpriv->ao_cmd2 |= AO_BC_Gate_Enable;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ devpriv->stc_writew(dev, devpriv->ao_cmd2, AO_Command_2_Register);
+ devpriv->stc_writew(dev, devpriv->ao_mode1, AO_Mode_1_Register);
+ devpriv->ao_mode2 &=
+ ~(AO_UI_Reload_Mode(3) | AO_UI_Initial_Load_Source);
+ devpriv->stc_writew(dev, devpriv->ao_mode2, AO_Mode_2_Register);
+
+ if (cmd->scan_end_arg > 1) {
+ devpriv->ao_mode1 |= AO_Multiple_Channels;
+ devpriv->stc_writew(dev,
+ AO_Number_Of_Channels(cmd->scan_end_arg -
+ 1) |
+ AO_UPDATE_Output_Select
+ (AO_Update_Output_High_Z),
+ AO_Output_Control_Register);
+ } else {
+ unsigned bits;
+ devpriv->ao_mode1 &= ~AO_Multiple_Channels;
+ bits = AO_UPDATE_Output_Select(AO_Update_Output_High_Z);
+ if (boardtype.reg_type & (ni_reg_m_series_mask | ni_reg_6xxx_mask)) {
+ bits |= AO_Number_Of_Channels(0);
+ } else {
+ bits |= AO_Number_Of_Channels(CR_CHAN(cmd->
+ chanlist[0]));
+ }
+ devpriv->stc_writew(dev, bits,
+ AO_Output_Control_Register);
+ }
+ devpriv->stc_writew(dev, devpriv->ao_mode1, AO_Mode_1_Register);
+
+ devpriv->stc_writew(dev, AO_DAC0_Update_Mode | AO_DAC1_Update_Mode,
+ AO_Command_1_Register);
+
+ devpriv->ao_mode3 |= AO_Stop_On_Overrun_Error;
+ devpriv->stc_writew(dev, devpriv->ao_mode3, AO_Mode_3_Register);
+
+ devpriv->ao_mode2 &= ~AO_FIFO_Mode_Mask;
+#ifdef PCIDMA
+ devpriv->ao_mode2 |= AO_FIFO_Mode_HF_to_F;
+#else
+ devpriv->ao_mode2 |= AO_FIFO_Mode_HF;
+#endif
+ devpriv->ao_mode2 &= ~AO_FIFO_Retransmit_Enable;
+ devpriv->stc_writew(dev, devpriv->ao_mode2, AO_Mode_2_Register);
+
+ bits = AO_BC_Source_Select | AO_UPDATE_Pulse_Width |
+ AO_TMRDACWR_Pulse_Width;
+ if (boardtype.ao_fifo_depth)
+ bits |= AO_FIFO_Enable;
+ else
+ bits |= AO_DMA_PIO_Control;
+#if 0
+ /* F Hess: windows driver does not set AO_Number_Of_DAC_Packages bit for 6281,
+ verified with bus analyzer. */
+ if (boardtype.reg_type & ni_reg_m_series_mask)
+ bits |= AO_Number_Of_DAC_Packages;
+#endif
+ devpriv->stc_writew(dev, bits, AO_Personal_Register);
+ // enable sending of ao dma requests
+ devpriv->stc_writew(dev, AO_AOFREQ_Enable, AO_Start_Select_Register);
+
+ devpriv->stc_writew(dev, AO_Configuration_End, Joint_Reset_Register);
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ devpriv->stc_writew(dev, AO_BC_TC_Interrupt_Ack,
+ Interrupt_B_Ack_Register);
+ ni_set_bits(dev, Interrupt_B_Enable_Register,
+ AO_BC_TC_Interrupt_Enable, 1);
+ }
+
+ s->async->inttrig = &ni_ao_inttrig;
+
+ return 0;
+}
+
+static int ni_ao_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ if ((cmd->flags & CMDF_WRITE) == 0) {
+ cmd->flags |= CMDF_WRITE;
+ }
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_INT | TRIG_EXT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_src == TRIG_EXT) {
+ /* external trigger */
+ unsigned int tmp = CR_CHAN(cmd->start_arg);
+
+ if (tmp > 18)
+ tmp = 18;
+ tmp |= (cmd->start_arg & (CR_INVERT | CR_EDGE));
+ if (cmd->start_arg != tmp) {
+ cmd->start_arg = tmp;
+ err++;
+ }
+ } else {
+ if (cmd->start_arg != 0) {
+ /* true for both TRIG_NOW and TRIG_INT */
+ cmd->start_arg = 0;
+ err++;
+ }
+ }
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->scan_begin_arg < boardtype.ao_speed) {
+ cmd->scan_begin_arg = boardtype.ao_speed;
+ err++;
+ }
+ if (cmd->scan_begin_arg > devpriv->clock_ns * 0xffffff) { /* XXX check */
+ cmd->scan_begin_arg = devpriv->clock_ns * 0xffffff;
+ err++;
+ }
+ }
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) { /* XXX check */
+ if (cmd->stop_arg > 0x00ffffff) {
+ cmd->stop_arg = 0x00ffffff;
+ err++;
+ }
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ cmd->scan_begin_arg =
+ ni_timer_to_ns(dev, ni_ns_to_timer(dev,
+ cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK));
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+ if (err)
+ return 4;
+
+ /* step 5: fix up chanlist */
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int ni_ao_reset(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ //devpriv->ao0p=0x0000;
+ //ni_writew(devpriv->ao0p,AO_Configuration);
+
+ //devpriv->ao1p=AO_Channel(1);
+ //ni_writew(devpriv->ao1p,AO_Configuration);
+
+ ni_release_ao_mite_channel(dev);
+
+ devpriv->stc_writew(dev, AO_Configuration_Start, Joint_Reset_Register);
+ devpriv->stc_writew(dev, AO_Disarm, AO_Command_1_Register);
+ ni_set_bits(dev, Interrupt_B_Enable_Register, ~0, 0);
+ devpriv->stc_writew(dev, AO_BC_Source_Select, AO_Personal_Register);
+ devpriv->stc_writew(dev, 0x3f98, Interrupt_B_Ack_Register);
+ devpriv->stc_writew(dev, AO_BC_Source_Select | AO_UPDATE_Pulse_Width |
+ AO_TMRDACWR_Pulse_Width, AO_Personal_Register);
+ devpriv->stc_writew(dev, 0, AO_Output_Control_Register);
+ devpriv->stc_writew(dev, 0, AO_Start_Select_Register);
+ devpriv->ao_cmd1 = 0;
+ devpriv->stc_writew(dev, devpriv->ao_cmd1, AO_Command_1_Register);
+ devpriv->ao_cmd2 = 0;
+ devpriv->stc_writew(dev, devpriv->ao_cmd2, AO_Command_2_Register);
+ devpriv->ao_mode1 = 0;
+ devpriv->stc_writew(dev, devpriv->ao_mode1, AO_Mode_1_Register);
+ devpriv->ao_mode2 = 0;
+ devpriv->stc_writew(dev, devpriv->ao_mode2, AO_Mode_2_Register);
+ if (boardtype.reg_type & ni_reg_m_series_mask)
+ devpriv->ao_mode3 = AO_Last_Gate_Disable;
+ else
+ devpriv->ao_mode3 = 0;
+ devpriv->stc_writew(dev, devpriv->ao_mode3, AO_Mode_3_Register);
+ devpriv->ao_trigger_select = 0;
+ devpriv->stc_writew(dev, devpriv->ao_trigger_select,
+ AO_Trigger_Select_Register);
+ if (boardtype.reg_type & ni_reg_6xxx_mask) {
+ unsigned immediate_bits = 0;
+ unsigned i;
+ for(i = 0; i < s->n_chan; ++i)
+ {
+ immediate_bits |= 1 << i;
+ }
+ ao_win_out(immediate_bits, AO_Immediate_671x);
+ ao_win_out(CLEAR_WG, AO_Misc_611x);
+ }
+ devpriv->stc_writew(dev, AO_Configuration_End, Joint_Reset_Register);
+
+ return 0;
+}
+
+// digital io
+
+static int ni_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+#ifdef DEBUG_DIO
+ rt_printk("ni_dio_insn_config() chan=%d io=%d\n",
+ CR_CHAN(insn->chanspec), data[0]);
+#endif
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= 1 << CR_CHAN(insn->chanspec);
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~(1 << CR_CHAN(insn->chanspec));
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (s->io_bits & (1 << CR_CHAN(insn->
+ chanspec))) ? COMEDI_OUTPUT :
+ COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ devpriv->dio_control &= ~DIO_Pins_Dir_Mask;
+ devpriv->dio_control |= DIO_Pins_Dir(s->io_bits);
+ devpriv->stc_writew(dev, devpriv->dio_control, DIO_Control_Register);
+
+ return 1;
+}
+
+static int ni_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+#ifdef DEBUG_DIO
+ rt_printk("ni_dio_insn_bits() mask=0x%x bits=0x%x\n", data[0], data[1]);
+#endif
+ if (insn->n != 2)
+ return -EINVAL;
+ if (data[0]) {
+ /* Perform check to make sure we're not using the
+ serial part of the dio */
+ if ((data[0] & (DIO_SDIN | DIO_SDOUT))
+ && devpriv->serial_interval_ns)
+ return -EBUSY;
+
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ devpriv->dio_output &= ~DIO_Parallel_Data_Mask;
+ devpriv->dio_output |= DIO_Parallel_Data_Out(s->state);
+ devpriv->stc_writew(dev, devpriv->dio_output,
+ DIO_Output_Register);
+ }
+ data[1] = devpriv->stc_readw(dev, DIO_Parallel_Input_Register);
+
+ return 2;
+}
+
+static int ni_m_series_dio_insn_config(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+#ifdef DEBUG_DIO
+ rt_printk("ni_m_series_dio_insn_config() chan=%d io=%d\n",
+ CR_CHAN(insn->chanspec), data[0]);
+#endif
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= 1 << CR_CHAN(insn->chanspec);
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~(1 << CR_CHAN(insn->chanspec));
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (s->io_bits & (1 << CR_CHAN(insn->
+ chanspec))) ? COMEDI_OUTPUT :
+ COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ni_writel(s->io_bits, M_Offset_DIO_Direction);
+
+ return 1;
+}
+
+static int ni_m_series_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+#ifdef DEBUG_DIO
+ rt_printk("ni_m_series_dio_insn_bits() mask=0x%x bits=0x%x\n", data[0],
+ data[1]);
+#endif
+ if (insn->n != 2)
+ return -EINVAL;
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ ni_writel(s->state, M_Offset_Static_Digital_Output);
+ }
+ data[1] = ni_readl(M_Offset_Static_Digital_Input);
+
+ return 2;
+}
+
+static int ni_cdio_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+ int sources;
+ unsigned i;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ sources = TRIG_INT;
+ cmd->start_src &= sources;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique... */
+
+ if (cmd->start_src != TRIG_INT)
+ err++;
+ if (cmd->scan_begin_src != TRIG_EXT)
+ err++;
+ if (cmd->convert_src != TRIG_NOW)
+ err++;
+ if (cmd->stop_src != TRIG_NONE)
+ err++;
+ /* ... and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+ if (cmd->start_src == TRIG_INT) {
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ }
+ if (cmd->scan_begin_src == TRIG_EXT) {
+ tmp = cmd->scan_begin_arg;
+ tmp &= CR_PACK_FLAGS(CDO_Sample_Source_Select_Mask, 0, 0,
+ CR_INVERT);
+ if (tmp != cmd->scan_begin_arg) {
+ err++;
+ }
+ }
+ if (cmd->convert_src == TRIG_NOW) {
+ if (cmd->convert_arg) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ if (cmd->stop_src == TRIG_NONE) {
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (err)
+ return 4;
+
+ /* step 5: check chanlist */
+
+ for (i = 0; i < cmd->chanlist_len; ++i) {
+ if (cmd->chanlist[i] != i)
+ err = 1;
+ }
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int ni_cdio_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ const struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned cdo_mode_bits = CDO_FIFO_Mode_Bit | CDO_Halt_On_Error_Bit;
+ int retval;
+
+ ni_writel(CDO_Reset_Bit, M_Offset_CDIO_Command);
+ switch (cmd->scan_begin_src) {
+ case TRIG_EXT:
+ cdo_mode_bits |=
+ CR_CHAN(cmd->
+ scan_begin_arg) & CDO_Sample_Source_Select_Mask;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ if (cmd->scan_begin_arg & CR_INVERT)
+ cdo_mode_bits |= CDO_Polarity_Bit;
+ ni_writel(cdo_mode_bits, M_Offset_CDO_Mode);
+ if (s->io_bits) {
+ ni_writel(s->state, M_Offset_CDO_FIFO_Data);
+ ni_writel(CDO_SW_Update_Bit, M_Offset_CDIO_Command);
+ ni_writel(s->io_bits, M_Offset_CDO_Mask_Enable);
+ } else {
+ comedi_error(dev,
+ "attempted to run digital output command with no lines configured as outputs");
+ return -EIO;
+ }
+ retval = ni_request_cdo_mite_channel(dev);
+ if (retval < 0) {
+ return retval;
+ }
+ s->async->inttrig = &ni_cdo_inttrig;
+ return 0;
+}
+
+static int ni_cdo_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trignum)
+{
+#ifdef PCIDMA
+ unsigned long flags;
+#endif
+ int retval = 0;
+ unsigned i;
+ const unsigned timeout = 100;
+
+ s->async->inttrig = NULL;
+
+ /* read alloc the entire buffer */
+ comedi_buf_read_alloc(s->async, s->async->prealloc_bufsz);
+
+#ifdef PCIDMA
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->cdo_mite_chan) {
+ mite_prep_dma(devpriv->cdo_mite_chan, 32, 32);
+ mite_dma_arm(devpriv->cdo_mite_chan);
+ } else {
+ comedi_error(dev, "BUG: no cdo mite channel?");
+ retval = -EIO;
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ if (retval < 0)
+ return retval;
+#endif
+// XXX not sure what interrupt C group does
+// ni_writeb(Interrupt_Group_C_Enable_Bit, M_Offset_Interrupt_C_Enable);
+ //wait for dma to fill output fifo
+ for (i = 0; i < timeout; ++i) {
+ if (ni_readl(M_Offset_CDIO_Status) & CDO_FIFO_Full_Bit)
+ break;
+ comedi_udelay(10);
+ }
+ if (i == timeout) {
+ comedi_error(dev, "dma failed to fill cdo fifo!");
+ ni_cdio_cancel(dev, s);
+ return -EIO;
+ }
+ ni_writel(CDO_Arm_Bit | CDO_Error_Interrupt_Enable_Set_Bit |
+ CDO_Empty_FIFO_Interrupt_Enable_Set_Bit, M_Offset_CDIO_Command);
+ return retval;
+}
+
+static int ni_cdio_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ ni_writel(CDO_Disarm_Bit | CDO_Error_Interrupt_Enable_Clear_Bit |
+ CDO_Empty_FIFO_Interrupt_Enable_Clear_Bit |
+ CDO_FIFO_Request_Interrupt_Enable_Clear_Bit,
+ M_Offset_CDIO_Command);
+// XXX not sure what interrupt C group does
+// ni_writeb(0, M_Offset_Interrupt_C_Enable);
+ ni_writel(0, M_Offset_CDO_Mask_Enable);
+ ni_release_cdo_mite_channel(dev);
+ return 0;
+}
+
+static void handle_cdio_interrupt(struct comedi_device * dev)
+{
+ unsigned cdio_status;
+ struct comedi_subdevice *s = dev->subdevices + NI_DIO_SUBDEV;
+#ifdef PCIDMA
+ unsigned long flags;
+#endif
+
+ if ((boardtype.reg_type & ni_reg_m_series_mask) == 0) {
+ return;
+ }
+#ifdef PCIDMA
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->cdo_mite_chan) {
+ unsigned cdo_mite_status =
+ mite_get_status(devpriv->cdo_mite_chan);
+ if (cdo_mite_status & CHSR_LINKC) {
+ writel(CHOR_CLRLC,
+ devpriv->mite->mite_io_addr +
+ MITE_CHOR(devpriv->cdo_mite_chan->channel));
+ }
+ mite_sync_output_dma(devpriv->cdo_mite_chan, s->async);
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+#endif
+
+ cdio_status = ni_readl(M_Offset_CDIO_Status);
+ if (cdio_status & (CDO_Overrun_Bit | CDO_Underflow_Bit)) {
+// rt_printk("cdio error: statux=0x%x\n", cdio_status);
+ ni_writel(CDO_Error_Interrupt_Confirm_Bit, M_Offset_CDIO_Command); // XXX just guessing this is needed and does something useful
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ }
+ if (cdio_status & CDO_FIFO_Empty_Bit) {
+// rt_printk("cdio fifo empty\n");
+ ni_writel(CDO_Empty_FIFO_Interrupt_Enable_Clear_Bit,
+ M_Offset_CDIO_Command);
+// s->async->events |= COMEDI_CB_EOA;
+ }
+ ni_event(dev, s);
+}
+
+static int ni_serial_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int err = insn->n;
+ unsigned char byte_out, byte_in = 0;
+
+ if (insn->n != 2)
+ return -EINVAL;
+
+ switch (data[0]) {
+ case INSN_CONFIG_SERIAL_CLOCK:
+
+#ifdef DEBUG_DIO
+ rt_printk("SPI serial clock Config cd\n", data[1]);
+#endif
+ devpriv->serial_hw_mode = 1;
+ devpriv->dio_control |= DIO_HW_Serial_Enable;
+
+ if (data[1] == SERIAL_DISABLED) {
+ devpriv->serial_hw_mode = 0;
+ devpriv->dio_control &= ~(DIO_HW_Serial_Enable |
+ DIO_Software_Serial_Control);
+ data[1] = SERIAL_DISABLED;
+ devpriv->serial_interval_ns = data[1];
+ } else if (data[1] <= SERIAL_600NS) {
+ /* Warning: this clock speed is too fast to reliably
+ control SCXI. */
+ devpriv->dio_control &= ~DIO_HW_Serial_Timebase;
+ devpriv->clock_and_fout |= Slow_Internal_Timebase;
+ devpriv->clock_and_fout &= ~DIO_Serial_Out_Divide_By_2;
+ data[1] = SERIAL_600NS;
+ devpriv->serial_interval_ns = data[1];
+ } else if (data[1] <= SERIAL_1_2US) {
+ devpriv->dio_control &= ~DIO_HW_Serial_Timebase;
+ devpriv->clock_and_fout |= Slow_Internal_Timebase |
+ DIO_Serial_Out_Divide_By_2;
+ data[1] = SERIAL_1_2US;
+ devpriv->serial_interval_ns = data[1];
+ } else if (data[1] <= SERIAL_10US) {
+ devpriv->dio_control |= DIO_HW_Serial_Timebase;
+ devpriv->clock_and_fout |= Slow_Internal_Timebase |
+ DIO_Serial_Out_Divide_By_2;
+ /* Note: DIO_Serial_Out_Divide_By_2 only affects
+ 600ns/1.2us. If you turn divide_by_2 off with the
+ slow clock, you will still get 10us, except then
+ all your delays are wrong. */
+ data[1] = SERIAL_10US;
+ devpriv->serial_interval_ns = data[1];
+ } else {
+ devpriv->dio_control &= ~(DIO_HW_Serial_Enable |
+ DIO_Software_Serial_Control);
+ devpriv->serial_hw_mode = 0;
+ data[1] = (data[1] / 1000) * 1000;
+ devpriv->serial_interval_ns = data[1];
+ }
+
+ devpriv->stc_writew(dev, devpriv->dio_control,
+ DIO_Control_Register);
+ devpriv->stc_writew(dev, devpriv->clock_and_fout,
+ Clock_and_FOUT_Register);
+ return 1;
+
+ break;
+
+ case INSN_CONFIG_BIDIRECTIONAL_DATA:
+
+ if (devpriv->serial_interval_ns == 0) {
+ return -EINVAL;
+ }
+
+ byte_out = data[1] & 0xFF;
+
+ if (devpriv->serial_hw_mode) {
+ err = ni_serial_hw_readwrite8(dev, s, byte_out,
+ &byte_in);
+ } else if (devpriv->serial_interval_ns > 0) {
+ err = ni_serial_sw_readwrite8(dev, s, byte_out,
+ &byte_in);
+ } else {
+ rt_printk("ni_serial_insn_config: serial disabled!\n");
+ return -EINVAL;
+ }
+ if (err < 0)
+ return err;
+ data[1] = byte_in & 0xFF;
+ return insn->n;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+}
+
+static int ni_serial_hw_readwrite8(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned char data_out, unsigned char *data_in)
+{
+ unsigned int status1;
+ int err = 0, count = 20;
+
+#ifdef DEBUG_DIO
+ rt_printk("ni_serial_hw_readwrite8: outputting 0x%x\n", data_out);
+#endif
+
+ devpriv->dio_output &= ~DIO_Serial_Data_Mask;
+ devpriv->dio_output |= DIO_Serial_Data_Out(data_out);
+ devpriv->stc_writew(dev, devpriv->dio_output, DIO_Output_Register);
+
+ status1 = devpriv->stc_readw(dev, Joint_Status_1_Register);
+ if (status1 & DIO_Serial_IO_In_Progress_St) {
+ err = -EBUSY;
+ goto Error;
+ }
+
+ devpriv->dio_control |= DIO_HW_Serial_Start;
+ devpriv->stc_writew(dev, devpriv->dio_control, DIO_Control_Register);
+ devpriv->dio_control &= ~DIO_HW_Serial_Start;
+
+ /* Wait until STC says we're done, but don't loop infinitely. */
+ while ((status1 =
+ devpriv->stc_readw(dev,
+ Joint_Status_1_Register)) &
+ DIO_Serial_IO_In_Progress_St) {
+ /* Delay one bit per loop */
+ comedi_udelay((devpriv->serial_interval_ns + 999) / 1000);
+ if (--count < 0) {
+ rt_printk
+ ("ni_serial_hw_readwrite8: SPI serial I/O didn't finish in time!\n");
+ err = -ETIME;
+ goto Error;
+ }
+ }
+
+ /* Delay for last bit. This delay is absolutely necessary, because
+ DIO_Serial_IO_In_Progress_St goes high one bit too early. */
+ comedi_udelay((devpriv->serial_interval_ns + 999) / 1000);
+
+ if (data_in != NULL) {
+ *data_in = devpriv->stc_readw(dev, DIO_Serial_Input_Register);
+#ifdef DEBUG_DIO
+ rt_printk("ni_serial_hw_readwrite8: inputted 0x%x\n", *data_in);
+#endif
+ }
+
+ Error:
+ devpriv->stc_writew(dev, devpriv->dio_control, DIO_Control_Register);
+
+ return err;
+}
+
+static int ni_serial_sw_readwrite8(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned char data_out, unsigned char *data_in)
+{
+ unsigned char mask, input = 0;
+
+#ifdef DEBUG_DIO
+ rt_printk("ni_serial_sw_readwrite8: outputting 0x%x\n", data_out);
+#endif
+
+ /* Wait for one bit before transfer */
+ comedi_udelay((devpriv->serial_interval_ns + 999) / 1000);
+
+ for (mask = 0x80; mask; mask >>= 1) {
+ /* Output current bit; note that we cannot touch s->state
+ because it is a per-subdevice field, and serial is
+ a separate subdevice from DIO. */
+ devpriv->dio_output &= ~DIO_SDOUT;
+ if (data_out & mask) {
+ devpriv->dio_output |= DIO_SDOUT;
+ }
+ devpriv->stc_writew(dev, devpriv->dio_output,
+ DIO_Output_Register);
+
+ /* Assert SDCLK (active low, inverted), wait for half of
+ the delay, deassert SDCLK, and wait for the other half. */
+ devpriv->dio_control |= DIO_Software_Serial_Control;
+ devpriv->stc_writew(dev, devpriv->dio_control,
+ DIO_Control_Register);
+
+ comedi_udelay((devpriv->serial_interval_ns + 999) / 2000);
+
+ devpriv->dio_control &= ~DIO_Software_Serial_Control;
+ devpriv->stc_writew(dev, devpriv->dio_control,
+ DIO_Control_Register);
+
+ comedi_udelay((devpriv->serial_interval_ns + 999) / 2000);
+
+ /* Input current bit */
+ if (devpriv->stc_readw(dev,
+ DIO_Parallel_Input_Register) & DIO_SDIN) {
+/* rt_printk("DIO_P_I_R: 0x%x\n", devpriv->stc_readw(dev, DIO_Parallel_Input_Register)); */
+ input |= mask;
+ }
+ }
+#ifdef DEBUG_DIO
+ rt_printk("ni_serial_sw_readwrite8: inputted 0x%x\n", input);
+#endif
+ if (data_in)
+ *data_in = input;
+
+ return 0;
+}
+
+static void mio_common_detach(struct comedi_device * dev)
+{
+ if (dev->private) {
+ if (devpriv->counter_dev) {
+ ni_gpct_device_destroy(devpriv->counter_dev);
+ }
+ }
+ if (dev->subdevices && boardtype.has_8255)
+ subdev_8255_cleanup(dev, dev->subdevices + NI_8255_DIO_SUBDEV);
+}
+
+static void init_ao_67xx(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int i;
+
+ for (i = 0; i < s->n_chan; i++)
+ {
+ ni_ao_win_outw(dev, AO_Channel(i) | 0x0,
+ AO_Configuration_2_67xx);
+ }
+ ao_win_out(0x0, AO_Later_Single_Point_Updates);
+}
+
+static unsigned ni_gpct_to_stc_register(enum ni_gpct_register reg)
+{
+ unsigned stc_register;
+ switch (reg) {
+ case NITIO_G0_Autoincrement_Reg:
+ stc_register = G_Autoincrement_Register(0);
+ break;
+ case NITIO_G1_Autoincrement_Reg:
+ stc_register = G_Autoincrement_Register(1);
+ break;
+ case NITIO_G0_Command_Reg:
+ stc_register = G_Command_Register(0);
+ break;
+ case NITIO_G1_Command_Reg:
+ stc_register = G_Command_Register(1);
+ break;
+ case NITIO_G0_HW_Save_Reg:
+ stc_register = G_HW_Save_Register(0);
+ break;
+ case NITIO_G1_HW_Save_Reg:
+ stc_register = G_HW_Save_Register(1);
+ break;
+ case NITIO_G0_SW_Save_Reg:
+ stc_register = G_Save_Register(0);
+ break;
+ case NITIO_G1_SW_Save_Reg:
+ stc_register = G_Save_Register(1);
+ break;
+ case NITIO_G0_Mode_Reg:
+ stc_register = G_Mode_Register(0);
+ break;
+ case NITIO_G1_Mode_Reg:
+ stc_register = G_Mode_Register(1);
+ break;
+ case NITIO_G0_LoadA_Reg:
+ stc_register = G_Load_A_Register(0);
+ break;
+ case NITIO_G1_LoadA_Reg:
+ stc_register = G_Load_A_Register(1);
+ break;
+ case NITIO_G0_LoadB_Reg:
+ stc_register = G_Load_B_Register(0);
+ break;
+ case NITIO_G1_LoadB_Reg:
+ stc_register = G_Load_B_Register(1);
+ break;
+ case NITIO_G0_Input_Select_Reg:
+ stc_register = G_Input_Select_Register(0);
+ break;
+ case NITIO_G1_Input_Select_Reg:
+ stc_register = G_Input_Select_Register(1);
+ break;
+ case NITIO_G01_Status_Reg:
+ stc_register = G_Status_Register;
+ break;
+ case NITIO_G01_Joint_Reset_Reg:
+ stc_register = Joint_Reset_Register;
+ break;
+ case NITIO_G01_Joint_Status1_Reg:
+ stc_register = Joint_Status_1_Register;
+ break;
+ case NITIO_G01_Joint_Status2_Reg:
+ stc_register = Joint_Status_2_Register;
+ break;
+ case NITIO_G0_Interrupt_Acknowledge_Reg:
+ stc_register = Interrupt_A_Ack_Register;
+ break;
+ case NITIO_G1_Interrupt_Acknowledge_Reg:
+ stc_register = Interrupt_B_Ack_Register;
+ break;
+ case NITIO_G0_Status_Reg:
+ stc_register = AI_Status_1_Register;
+ break;
+ case NITIO_G1_Status_Reg:
+ stc_register = AO_Status_1_Register;
+ break;
+ case NITIO_G0_Interrupt_Enable_Reg:
+ stc_register = Interrupt_A_Enable_Register;
+ break;
+ case NITIO_G1_Interrupt_Enable_Reg:
+ stc_register = Interrupt_B_Enable_Register;
+ break;
+ default:
+ rt_printk("%s: unhandled register 0x%x in switch.\n",
+ __FUNCTION__, reg);
+ BUG();
+ return 0;
+ break;
+ }
+ return stc_register;
+}
+
+static void ni_gpct_write_register(struct ni_gpct *counter, unsigned bits,
+ enum ni_gpct_register reg)
+{
+ struct comedi_device *dev = counter->counter_dev->dev;
+ unsigned stc_register;
+ /* bits in the join reset register which are relevant to counters */
+ static const unsigned gpct_joint_reset_mask = G0_Reset | G1_Reset;
+ static const unsigned gpct_interrupt_a_enable_mask =
+ G0_Gate_Interrupt_Enable | G0_TC_Interrupt_Enable;
+ static const unsigned gpct_interrupt_b_enable_mask =
+ G1_Gate_Interrupt_Enable | G1_TC_Interrupt_Enable;
+
+ switch (reg) {
+ /* m-series-only registers */
+ case NITIO_G0_Counting_Mode_Reg:
+ ni_writew(bits, M_Offset_G0_Counting_Mode);
+ break;
+ case NITIO_G1_Counting_Mode_Reg:
+ ni_writew(bits, M_Offset_G1_Counting_Mode);
+ break;
+ case NITIO_G0_Second_Gate_Reg:
+ ni_writew(bits, M_Offset_G0_Second_Gate);
+ break;
+ case NITIO_G1_Second_Gate_Reg:
+ ni_writew(bits, M_Offset_G1_Second_Gate);
+ break;
+ case NITIO_G0_DMA_Config_Reg:
+ ni_writew(bits, M_Offset_G0_DMA_Config);
+ break;
+ case NITIO_G1_DMA_Config_Reg:
+ ni_writew(bits, M_Offset_G1_DMA_Config);
+ break;
+ case NITIO_G0_ABZ_Reg:
+ ni_writew(bits, M_Offset_G0_MSeries_ABZ);
+ break;
+ case NITIO_G1_ABZ_Reg:
+ ni_writew(bits, M_Offset_G1_MSeries_ABZ);
+ break;
+
+ /* 32 bit registers */
+ case NITIO_G0_LoadA_Reg:
+ case NITIO_G1_LoadA_Reg:
+ case NITIO_G0_LoadB_Reg:
+ case NITIO_G1_LoadB_Reg:
+ stc_register = ni_gpct_to_stc_register(reg);
+ devpriv->stc_writel(dev, bits, stc_register);
+ break;
+
+ /* 16 bit registers */
+ case NITIO_G0_Interrupt_Enable_Reg:
+ BUG_ON(bits & ~gpct_interrupt_a_enable_mask);
+ ni_set_bitfield(dev, Interrupt_A_Enable_Register,
+ gpct_interrupt_a_enable_mask, bits);
+ break;
+ case NITIO_G1_Interrupt_Enable_Reg:
+ BUG_ON(bits & ~gpct_interrupt_b_enable_mask);
+ ni_set_bitfield(dev, Interrupt_B_Enable_Register,
+ gpct_interrupt_b_enable_mask, bits);
+ break;
+ case NITIO_G01_Joint_Reset_Reg:
+ BUG_ON(bits & ~gpct_joint_reset_mask);
+ /* fall-through */
+ default:
+ stc_register = ni_gpct_to_stc_register(reg);
+ devpriv->stc_writew(dev, bits, stc_register);
+ }
+}
+
+static unsigned ni_gpct_read_register(struct ni_gpct *counter,
+ enum ni_gpct_register reg)
+{
+ struct comedi_device *dev = counter->counter_dev->dev;
+ unsigned stc_register;
+ switch (reg) {
+ /* m-series only registers */
+ case NITIO_G0_DMA_Status_Reg:
+ return ni_readw(M_Offset_G0_DMA_Status);
+ break;
+ case NITIO_G1_DMA_Status_Reg:
+ return ni_readw(M_Offset_G1_DMA_Status);
+ break;
+
+ /* 32 bit registers */
+ case NITIO_G0_HW_Save_Reg:
+ case NITIO_G1_HW_Save_Reg:
+ case NITIO_G0_SW_Save_Reg:
+ case NITIO_G1_SW_Save_Reg:
+ stc_register = ni_gpct_to_stc_register(reg);
+ return devpriv->stc_readl(dev, stc_register);
+ break;
+
+ /* 16 bit registers */
+ default:
+ stc_register = ni_gpct_to_stc_register(reg);
+ return devpriv->stc_readw(dev, stc_register);
+ break;
+ }
+ return 0;
+}
+
+static int ni_freq_out_insn_read(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = devpriv->clock_and_fout & FOUT_Divider_mask;
+ return 1;
+}
+
+static int ni_freq_out_insn_write(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ devpriv->clock_and_fout &= ~FOUT_Enable;
+ devpriv->stc_writew(dev, devpriv->clock_and_fout,
+ Clock_and_FOUT_Register);
+ devpriv->clock_and_fout &= ~FOUT_Divider_mask;
+ devpriv->clock_and_fout |= FOUT_Divider(data[0]);
+ devpriv->clock_and_fout |= FOUT_Enable;
+ devpriv->stc_writew(dev, devpriv->clock_and_fout,
+ Clock_and_FOUT_Register);
+ return insn->n;
+}
+
+static int ni_set_freq_out_clock(struct comedi_device * dev, unsigned int clock_source)
+{
+ switch (clock_source) {
+ case NI_FREQ_OUT_TIMEBASE_1_DIV_2_CLOCK_SRC:
+ devpriv->clock_and_fout &= ~FOUT_Timebase_Select;
+ break;
+ case NI_FREQ_OUT_TIMEBASE_2_CLOCK_SRC:
+ devpriv->clock_and_fout |= FOUT_Timebase_Select;
+ break;
+ default:
+ return -EINVAL;
+ }
+ devpriv->stc_writew(dev, devpriv->clock_and_fout,
+ Clock_and_FOUT_Register);
+ return 3;
+}
+
+static void ni_get_freq_out_clock(struct comedi_device * dev, unsigned int * clock_source,
+ unsigned int * clock_period_ns)
+{
+ if (devpriv->clock_and_fout & FOUT_Timebase_Select) {
+ *clock_source = NI_FREQ_OUT_TIMEBASE_2_CLOCK_SRC;
+ *clock_period_ns = TIMEBASE_2_NS;
+ } else {
+ *clock_source = NI_FREQ_OUT_TIMEBASE_1_DIV_2_CLOCK_SRC;
+ *clock_period_ns = TIMEBASE_1_NS * 2;
+ }
+}
+
+static int ni_freq_out_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ switch (data[0]) {
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ return ni_set_freq_out_clock(dev, data[1]);
+ break;
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ ni_get_freq_out_clock(dev, &data[1], &data[2]);
+ return 3;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int ni_alloc_private(struct comedi_device * dev)
+{
+ int ret;
+
+ ret = alloc_private(dev, sizeof(ni_private));
+ if (ret < 0)
+ return ret;
+
+ spin_lock_init(&devpriv->window_lock);
+ spin_lock_init(&devpriv->soft_reg_copy_lock);
+ spin_lock_init(&devpriv->mite_channel_lock);
+
+ return 0;
+};
+
+static int ni_E_init(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned j;
+ enum ni_gpct_variant counter_variant;
+
+ if (boardtype.n_aochan > MAX_N_AO_CHAN) {
+ printk("bug! boardtype.n_aochan > MAX_N_AO_CHAN\n");
+ return -EINVAL;
+ }
+
+ if (alloc_subdevices(dev, NI_NUM_SUBDEVICES) < 0)
+ return -ENOMEM;
+
+ /* analog input subdevice */
+
+ s = dev->subdevices + NI_AI_SUBDEV;
+ dev->read_subdev = s;
+ if (boardtype.n_adchan) {
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags =
+ SDF_READABLE | SDF_DIFF | SDF_DITHER | SDF_CMD_READ;
+ if (boardtype.reg_type != ni_reg_611x)
+ s->subdev_flags |= SDF_GROUND | SDF_COMMON | SDF_OTHER;
+ if (boardtype.adbits > 16)
+ s->subdev_flags |= SDF_LSAMPL;
+ if (boardtype.reg_type & ni_reg_m_series_mask)
+ s->subdev_flags |= SDF_SOFT_CALIBRATED;
+ s->n_chan = boardtype.n_adchan;
+ s->len_chanlist = 512;
+ s->maxdata = (1 << boardtype.adbits) - 1;
+ s->range_table = ni_range_lkup[boardtype.gainlkup];
+ s->insn_read = &ni_ai_insn_read;
+ s->insn_config = &ni_ai_insn_config;
+ s->do_cmdtest = &ni_ai_cmdtest;
+ s->do_cmd = &ni_ai_cmd;
+ s->cancel = &ni_ai_reset;
+ s->poll = &ni_ai_poll;
+ s->munge = &ni_ai_munge;
+#ifdef PCIDMA
+ s->async_dma_dir = DMA_FROM_DEVICE;
+#endif
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* analog output subdevice */
+
+ s = dev->subdevices + NI_AO_SUBDEV;
+ if (boardtype.n_aochan) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_DEGLITCH | SDF_GROUND;
+ if (boardtype.reg_type & ni_reg_m_series_mask)
+ s->subdev_flags |= SDF_SOFT_CALIBRATED;
+ s->n_chan = boardtype.n_aochan;
+ s->maxdata = (1 << boardtype.aobits) - 1;
+ s->range_table = boardtype.ao_range_table;
+ s->insn_read = &ni_ao_insn_read;
+ if (boardtype.reg_type & ni_reg_6xxx_mask) {
+ s->insn_write = &ni_ao_insn_write_671x;
+ } else {
+ s->insn_write = &ni_ao_insn_write;
+ }
+ s->insn_config = &ni_ao_insn_config;
+#ifdef PCIDMA
+ if (boardtype.n_aochan) {
+ s->async_dma_dir = DMA_TO_DEVICE;
+#else
+ if (boardtype.ao_fifo_depth) {
+#endif
+ dev->write_subdev = s;
+ s->subdev_flags |= SDF_CMD_WRITE;
+ s->do_cmd = &ni_ao_cmd;
+ s->do_cmdtest = &ni_ao_cmdtest;
+ s->len_chanlist = boardtype.n_aochan;
+ if ((boardtype.reg_type & ni_reg_m_series_mask) == 0)
+ s->munge = ni_ao_munge;
+ }
+ s->cancel = &ni_ao_reset;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+ if ((boardtype.reg_type & ni_reg_67xx_mask))
+ init_ao_67xx(dev, s);
+
+ /* digital i/o subdevice */
+
+ s = dev->subdevices + NI_DIO_SUBDEV;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->maxdata = 1;
+ s->io_bits = 0; /* all bits input */
+ s->range_table = &range_digital;
+ s->n_chan = boardtype.num_p0_dio_channels;
+ if (boardtype.reg_type & ni_reg_m_series_mask) {
+ s->subdev_flags |=
+ SDF_LSAMPL | SDF_CMD_WRITE /* | SDF_CMD_READ */ ;
+ s->insn_bits = &ni_m_series_dio_insn_bits;
+ s->insn_config = &ni_m_series_dio_insn_config;
+ s->do_cmd = &ni_cdio_cmd;
+ s->do_cmdtest = &ni_cdio_cmdtest;
+ s->cancel = &ni_cdio_cancel;
+ s->async_dma_dir = DMA_BIDIRECTIONAL;
+ s->len_chanlist = s->n_chan;
+
+ ni_writel(CDO_Reset_Bit | CDI_Reset_Bit, M_Offset_CDIO_Command);
+ ni_writel(s->io_bits, M_Offset_DIO_Direction);
+ } else {
+ s->insn_bits = &ni_dio_insn_bits;
+ s->insn_config = &ni_dio_insn_config;
+ devpriv->dio_control = DIO_Pins_Dir(s->io_bits);
+ ni_writew(devpriv->dio_control, DIO_Control_Register);
+ }
+
+ /* 8255 device */
+ s = dev->subdevices + NI_8255_DIO_SUBDEV;
+ if (boardtype.has_8255) {
+ subdev_8255_init(dev, s, ni_8255_callback, (unsigned long)dev);
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* formerly general purpose counter/timer device, but no longer used */
+ s = dev->subdevices + NI_UNUSED_SUBDEV;
+ s->type = COMEDI_SUBD_UNUSED;
+
+ /* calibration subdevice -- ai and ao */
+ s = dev->subdevices + NI_CALIBRATION_SUBDEV;
+ s->type = COMEDI_SUBD_CALIB;
+ if (boardtype.reg_type & ni_reg_m_series_mask) {
+ // internal PWM analog output used for AI nonlinearity calibration
+ s->subdev_flags = SDF_INTERNAL;
+ s->insn_config = &ni_m_series_pwm_config;
+ s->n_chan = 1;
+ s->maxdata = 0;
+ ni_writel(0x0, M_Offset_Cal_PWM);
+ } else if (boardtype.reg_type == ni_reg_6143) {
+ // internal PWM analog output used for AI nonlinearity calibration
+ s->subdev_flags = SDF_INTERNAL;
+ s->insn_config = &ni_6143_pwm_config;
+ s->n_chan = 1;
+ s->maxdata = 0;
+ } else {
+ s->subdev_flags = SDF_WRITABLE | SDF_INTERNAL;
+ s->insn_read = &ni_calib_insn_read;
+ s->insn_write = &ni_calib_insn_write;
+ caldac_setup(dev, s);
+ }
+
+ /* EEPROM */
+ s = dev->subdevices + NI_EEPROM_SUBDEV;
+ s->type = COMEDI_SUBD_MEMORY;
+ s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
+ s->maxdata = 0xff;
+ if (boardtype.reg_type & ni_reg_m_series_mask) {
+ s->n_chan = M_SERIES_EEPROM_SIZE;
+ s->insn_read = &ni_m_series_eeprom_insn_read;
+ } else {
+ s->n_chan = 512;
+ s->insn_read = &ni_eeprom_insn_read;
+ }
+
+ /* PFI */
+ s = dev->subdevices + NI_PFI_DIO_SUBDEV;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ if (boardtype.reg_type & ni_reg_m_series_mask) {
+ unsigned i;
+ s->n_chan = 16;
+ ni_writew(s->state, M_Offset_PFI_DO);
+ for (i = 0; i < NUM_PFI_OUTPUT_SELECT_REGS; ++i) {
+ ni_writew(devpriv->pfi_output_select_reg[i],
+ M_Offset_PFI_Output_Select(i + 1));
+ }
+ } else {
+ s->n_chan = 10;
+ }
+ s->maxdata = 1;
+ if (boardtype.reg_type & ni_reg_m_series_mask) {
+ s->insn_bits = &ni_pfi_insn_bits;
+ }
+ s->insn_config = &ni_pfi_insn_config;
+ ni_set_bits(dev, IO_Bidirection_Pin_Register, ~0, 0);
+
+ /* cs5529 calibration adc */
+ s = dev->subdevices + NI_CS5529_CALIBRATION_SUBDEV;
+ if (boardtype.reg_type & ni_reg_67xx_mask) {
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_INTERNAL;
+ // one channel for each analog output channel
+ s->n_chan = boardtype.n_aochan;
+ s->maxdata = (1 << 16) - 1;
+ s->range_table = &range_unknown; /* XXX */
+ s->insn_read = cs5529_ai_insn_read;
+ s->insn_config = NULL;
+ init_cs5529(dev);
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* Serial */
+ s = dev->subdevices + NI_SERIAL_SUBDEV;
+ s->type = COMEDI_SUBD_SERIAL;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = 1;
+ s->maxdata = 0xff;
+ s->insn_config = ni_serial_insn_config;
+ devpriv->serial_interval_ns = 0;
+ devpriv->serial_hw_mode = 0;
+
+ /* RTSI */
+ s = dev->subdevices + NI_RTSI_SUBDEV;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->insn_bits = ni_rtsi_insn_bits;
+ s->insn_config = ni_rtsi_insn_config;
+ ni_rtsi_init(dev);
+
+ if (boardtype.reg_type & ni_reg_m_series_mask) {
+ counter_variant = ni_gpct_variant_m_series;
+ } else {
+ counter_variant = ni_gpct_variant_e_series;
+ }
+ devpriv->counter_dev = ni_gpct_device_construct(dev,
+ &ni_gpct_write_register, &ni_gpct_read_register,
+ counter_variant, NUM_GPCT);
+ /* General purpose counters */
+ for (j = 0; j < NUM_GPCT; ++j) {
+ s = dev->subdevices + NI_GPCT_SUBDEV(j);
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags =
+ SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL | SDF_CMD_READ
+ /* | SDF_CMD_WRITE */ ;
+ s->n_chan = 3;
+ if (boardtype.reg_type & ni_reg_m_series_mask)
+ s->maxdata = 0xffffffff;
+ else
+ s->maxdata = 0xffffff;
+ s->insn_read = &ni_gpct_insn_read;
+ s->insn_write = &ni_gpct_insn_write;
+ s->insn_config = &ni_gpct_insn_config;
+ s->do_cmd = &ni_gpct_cmd;
+ s->len_chanlist = 1;
+ s->do_cmdtest = &ni_gpct_cmdtest;
+ s->cancel = &ni_gpct_cancel;
+ s->async_dma_dir = DMA_BIDIRECTIONAL;
+ s->private = &devpriv->counter_dev->counters[j];
+
+ devpriv->counter_dev->counters[j].chip_index = 0;
+ devpriv->counter_dev->counters[j].counter_index = j;
+ ni_tio_init_counter(&devpriv->counter_dev->counters[j]);
+ }
+
+ /* Frequency output */
+ s = dev->subdevices + NI_FREQ_OUT_SUBDEV;
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 1;
+ s->maxdata = 0xf;
+ s->insn_read = &ni_freq_out_insn_read;
+ s->insn_write = &ni_freq_out_insn_write;
+ s->insn_config = &ni_freq_out_insn_config;
+
+ /* ai configuration */
+ ni_ai_reset(dev, dev->subdevices + NI_AI_SUBDEV);
+ if ((boardtype.reg_type & ni_reg_6xxx_mask) == 0) {
+ // BEAM is this needed for PCI-6143 ??
+ devpriv->clock_and_fout =
+ Slow_Internal_Time_Divide_By_2 |
+ Slow_Internal_Timebase |
+ Clock_To_Board_Divide_By_2 |
+ Clock_To_Board |
+ AI_Output_Divide_By_2 | AO_Output_Divide_By_2;
+ } else {
+ devpriv->clock_and_fout =
+ Slow_Internal_Time_Divide_By_2 |
+ Slow_Internal_Timebase |
+ Clock_To_Board_Divide_By_2 | Clock_To_Board;
+ }
+ devpriv->stc_writew(dev, devpriv->clock_and_fout,
+ Clock_and_FOUT_Register);
+
+ /* analog output configuration */
+ ni_ao_reset(dev, dev->subdevices + NI_AO_SUBDEV);
+
+ if (dev->irq) {
+ devpriv->stc_writew(dev,
+ (IRQ_POLARITY ? Interrupt_Output_Polarity : 0) |
+ (Interrupt_Output_On_3_Pins & 0) | Interrupt_A_Enable |
+ Interrupt_B_Enable |
+ Interrupt_A_Output_Select(interrupt_pin(dev->
+ irq)) |
+ Interrupt_B_Output_Select(interrupt_pin(dev->irq)),
+ Interrupt_Control_Register);
+ }
+
+ /* DMA setup */
+ ni_writeb(devpriv->ai_ao_select_reg, AI_AO_Select);
+ ni_writeb(devpriv->g0_g1_select_reg, G0_G1_Select);
+
+ if (boardtype.reg_type & ni_reg_6xxx_mask) {
+ ni_writeb(0, Magic_611x);
+ } else if (boardtype.reg_type & ni_reg_m_series_mask) {
+ int channel;
+ for (channel = 0; channel < boardtype.n_aochan; ++channel) {
+ ni_writeb(0xf, M_Offset_AO_Waveform_Order(channel));
+ ni_writeb(0x0,
+ M_Offset_AO_Reference_Attenuation(channel));
+ }
+ ni_writeb(0x0, M_Offset_AO_Calibration);
+ }
+
+ printk("\n");
+ return 0;
+}
+
+static int ni_8255_callback(int dir, int port, int data, unsigned long arg)
+{
+ struct comedi_device *dev = (struct comedi_device *) arg;
+
+ if (dir) {
+ ni_writeb(data, Port_A + 2 * port);
+ return 0;
+ } else {
+ return ni_readb(Port_A + 2 * port);
+ }
+}
+
+/*
+ presents the EEPROM as a subdevice
+*/
+
+static int ni_eeprom_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = ni_read_eeprom(dev, CR_CHAN(insn->chanspec));
+
+ return 1;
+}
+
+/*
+ reads bytes out of eeprom
+*/
+
+static int ni_read_eeprom(struct comedi_device * dev, int addr)
+{
+ int bit;
+ int bitstring;
+
+ bitstring = 0x0300 | ((addr & 0x100) << 3) | (addr & 0xff);
+ ni_writeb(0x04, Serial_Command);
+ for (bit = 0x8000; bit; bit >>= 1) {
+ ni_writeb(0x04 | ((bit & bitstring) ? 0x02 : 0),
+ Serial_Command);
+ ni_writeb(0x05 | ((bit & bitstring) ? 0x02 : 0),
+ Serial_Command);
+ }
+ bitstring = 0;
+ for (bit = 0x80; bit; bit >>= 1) {
+ ni_writeb(0x04, Serial_Command);
+ ni_writeb(0x05, Serial_Command);
+ bitstring |= ((ni_readb(XXX_Status) & PROMOUT) ? bit : 0);
+ }
+ ni_writeb(0x00, Serial_Command);
+
+ return bitstring;
+}
+
+static int ni_m_series_eeprom_insn_read(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = devpriv->eeprom_buffer[CR_CHAN(insn->chanspec)];
+
+ return 1;
+}
+
+static int ni_get_pwm_config(struct comedi_device * dev, unsigned int * data)
+{
+ data[1] = devpriv->pwm_up_count * devpriv->clock_ns;
+ data[2] = devpriv->pwm_down_count * devpriv->clock_ns;
+ return 3;
+}
+
+static int ni_m_series_pwm_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned up_count, down_count;
+ switch (data[0]) {
+ case INSN_CONFIG_PWM_OUTPUT:
+ switch (data[1]) {
+ case TRIG_ROUND_NEAREST:
+ up_count =
+ (data[2] +
+ devpriv->clock_ns / 2) / devpriv->clock_ns;
+ break;
+ case TRIG_ROUND_DOWN:
+ up_count = data[2] / devpriv->clock_ns;
+ break;
+ case TRIG_ROUND_UP:
+ up_count =
+ (data[2] + devpriv->clock_ns -
+ 1) / devpriv->clock_ns;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ switch (data[3]) {
+ case TRIG_ROUND_NEAREST:
+ down_count =
+ (data[4] +
+ devpriv->clock_ns / 2) / devpriv->clock_ns;
+ break;
+ case TRIG_ROUND_DOWN:
+ down_count = data[4] / devpriv->clock_ns;
+ break;
+ case TRIG_ROUND_UP:
+ down_count =
+ (data[4] + devpriv->clock_ns -
+ 1) / devpriv->clock_ns;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ if (up_count * devpriv->clock_ns != data[2] ||
+ down_count * devpriv->clock_ns != data[4]) {
+ data[2] = up_count * devpriv->clock_ns;
+ data[4] = down_count * devpriv->clock_ns;
+ return -EAGAIN;
+ }
+ ni_writel(MSeries_Cal_PWM_High_Time_Bits(up_count) |
+ MSeries_Cal_PWM_Low_Time_Bits(down_count),
+ M_Offset_Cal_PWM);
+ devpriv->pwm_up_count = up_count;
+ devpriv->pwm_down_count = down_count;
+ return 5;
+ break;
+ case INSN_CONFIG_GET_PWM_OUTPUT:
+ return ni_get_pwm_config(dev, data);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return 0;
+}
+
+static int ni_6143_pwm_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned up_count, down_count;
+ switch (data[0]) {
+ case INSN_CONFIG_PWM_OUTPUT:
+ switch (data[1]) {
+ case TRIG_ROUND_NEAREST:
+ up_count =
+ (data[2] +
+ devpriv->clock_ns / 2) / devpriv->clock_ns;
+ break;
+ case TRIG_ROUND_DOWN:
+ up_count = data[2] / devpriv->clock_ns;
+ break;
+ case TRIG_ROUND_UP:
+ up_count =
+ (data[2] + devpriv->clock_ns -
+ 1) / devpriv->clock_ns;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ switch (data[3]) {
+ case TRIG_ROUND_NEAREST:
+ down_count =
+ (data[4] +
+ devpriv->clock_ns / 2) / devpriv->clock_ns;
+ break;
+ case TRIG_ROUND_DOWN:
+ down_count = data[4] / devpriv->clock_ns;
+ break;
+ case TRIG_ROUND_UP:
+ down_count =
+ (data[4] + devpriv->clock_ns -
+ 1) / devpriv->clock_ns;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ if (up_count * devpriv->clock_ns != data[2] ||
+ down_count * devpriv->clock_ns != data[4]) {
+ data[2] = up_count * devpriv->clock_ns;
+ data[4] = down_count * devpriv->clock_ns;
+ return -EAGAIN;
+ }
+ ni_writel(up_count, Calibration_HighTime_6143);
+ devpriv->pwm_up_count = up_count;
+ ni_writel(down_count, Calibration_LowTime_6143);
+ devpriv->pwm_down_count = down_count;
+ return 5;
+ break;
+ case INSN_CONFIG_GET_PWM_OUTPUT:
+ return ni_get_pwm_config(dev, data);
+ default:
+ return -EINVAL;
+ break;
+ }
+ return 0;
+}
+
+static void ni_write_caldac(struct comedi_device * dev, int addr, int val);
+/*
+ calibration subdevice
+*/
+static int ni_calib_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ ni_write_caldac(dev, CR_CHAN(insn->chanspec), data[0]);
+
+ return 1;
+}
+
+static int ni_calib_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ data[0] = devpriv->caldacs[CR_CHAN(insn->chanspec)];
+
+ 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);
+static int pack_ad8804(int addr, int val, int *bitstring);
+static int pack_ad8842(int addr, int val, int *bitstring);
+
+struct caldac_struct {
+ int n_chans;
+ int n_bits;
+ int (*packbits) (int, int, int *);
+};
+
+static struct caldac_struct caldacs[] = {
+ [mb88341] = {12, 8, pack_mb88341},
+ [dac8800] = {8, 8, pack_dac8800},
+ [dac8043] = {1, 12, pack_dac8043},
+ [ad8522] = {2, 12, pack_ad8522},
+ [ad8804] = {12, 8, pack_ad8804},
+ [ad8842] = {8, 8, pack_ad8842},
+ [ad8804_debug] = {16, 8, pack_ad8804},
+};
+
+static void caldac_setup(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int i, j;
+ int n_dacs;
+ int n_chans = 0;
+ int n_bits;
+ int diffbits = 0;
+ int type;
+ int chan;
+
+ type = boardtype.caldac[0];
+ if (type == caldac_none)
+ return;
+ n_bits = caldacs[type].n_bits;
+ for (i = 0; i < 3; i++) {
+ type = boardtype.caldac[i];
+ if (type == caldac_none)
+ break;
+ if (caldacs[type].n_bits != n_bits)
+ diffbits = 1;
+ n_chans += caldacs[type].n_chans;
+ }
+ n_dacs = i;
+ s->n_chan = n_chans;
+
+ if (diffbits) {
+ unsigned int *maxdata_list;
+
+ if (n_chans > MAX_N_CALDACS) {
+ printk("BUG! MAX_N_CALDACS too small\n");
+ }
+ s->maxdata_list = maxdata_list = devpriv->caldac_maxdata_list;
+ chan = 0;
+ for (i = 0; i < n_dacs; i++) {
+ type = boardtype.caldac[i];
+ for (j = 0; j < caldacs[type].n_chans; j++) {
+ maxdata_list[chan] =
+ (1 << caldacs[type].n_bits) - 1;
+ chan++;
+ }
+ }
+
+ for (chan = 0; chan < s->n_chan; chan++)
+ ni_write_caldac(dev, i, s->maxdata_list[i] / 2);
+ } else {
+ type = boardtype.caldac[0];
+ s->maxdata = (1 << caldacs[type].n_bits) - 1;
+
+ for (chan = 0; chan < s->n_chan; chan++)
+ ni_write_caldac(dev, i, s->maxdata / 2);
+ }
+}
+
+static void ni_write_caldac(struct comedi_device * dev, int addr, int val)
+{
+ unsigned int loadbit = 0, bits = 0, bit, bitstring = 0;
+ int i;
+ int type;
+
+ //printk("ni_write_caldac: chan=%d val=%d\n",addr,val);
+ if (devpriv->caldacs[addr] == val)
+ return;
+ devpriv->caldacs[addr] = val;
+
+ for (i = 0; i < 3; i++) {
+ type = boardtype.caldac[i];
+ if (type == caldac_none)
+ break;
+ if (addr < caldacs[type].n_chans) {
+ bits = caldacs[type].packbits(addr, val, &bitstring);
+ loadbit = SerDacLd(i);
+ //printk("caldac: using i=%d addr=%d %x\n",i,addr,bitstring);
+ break;
+ }
+ addr -= caldacs[type].n_chans;
+ }
+
+ for (bit = 1 << (bits - 1); bit; bit >>= 1) {
+ ni_writeb(((bit & bitstring) ? 0x02 : 0), Serial_Command);
+ comedi_udelay(1);
+ ni_writeb(1 | ((bit & bitstring) ? 0x02 : 0), Serial_Command);
+ comedi_udelay(1);
+ }
+ ni_writeb(loadbit, Serial_Command);
+ comedi_udelay(1);
+ 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;
+}
+
+static int pack_ad8804(int addr, int val, int *bitstring)
+{
+ *bitstring = ((addr & 0xf) << 8) | (val & 0xff);
+ return 12;
+}
+
+static int pack_ad8842(int addr, int val, int *bitstring)
+{
+ *bitstring = ((addr + 1) << 8) | (val & 0xff);
+ return 12;
+}
+
+#if 0
+/*
+ * Read the GPCTs current value.
+ */
+static int GPCT_G_Watch(struct comedi_device * dev, int chan)
+{
+ unsigned int hi1, hi2, lo;
+
+ devpriv->gpct_command[chan] &= ~G_Save_Trace;
+ devpriv->stc_writew(dev, devpriv->gpct_command[chan],
+ G_Command_Register(chan));
+
+ devpriv->gpct_command[chan] |= G_Save_Trace;
+ devpriv->stc_writew(dev, devpriv->gpct_command[chan],
+ G_Command_Register(chan));
+
+ /* This procedure is used because the two registers cannot
+ * be read atomically. */
+ do {
+ hi1 = devpriv->stc_readw(dev, G_Save_Register_High(chan));
+ lo = devpriv->stc_readw(dev, G_Save_Register_Low(chan));
+ hi2 = devpriv->stc_readw(dev, G_Save_Register_High(chan));
+ } while (hi1 != hi2);
+
+ return (hi1 << 16) | lo;
+}
+
+static void GPCT_Reset(struct comedi_device * dev, int chan)
+{
+ int temp_ack_reg = 0;
+
+ //printk("GPCT_Reset...");
+ devpriv->gpct_cur_operation[chan] = GPCT_RESET;
+
+ switch (chan) {
+ case 0:
+ devpriv->stc_writew(dev, G0_Reset, Joint_Reset_Register);
+ ni_set_bits(dev, Interrupt_A_Enable_Register,
+ G0_TC_Interrupt_Enable, 0);
+ ni_set_bits(dev, Interrupt_A_Enable_Register,
+ G0_Gate_Interrupt_Enable, 0);
+ temp_ack_reg |= G0_Gate_Error_Confirm;
+ temp_ack_reg |= G0_TC_Error_Confirm;
+ temp_ack_reg |= G0_TC_Interrupt_Ack;
+ temp_ack_reg |= G0_Gate_Interrupt_Ack;
+ devpriv->stc_writew(dev, temp_ack_reg,
+ Interrupt_A_Ack_Register);
+
+ //problem...this interferes with the other ctr...
+ devpriv->an_trig_etc_reg |= GPFO_0_Output_Enable;
+ devpriv->stc_writew(dev, devpriv->an_trig_etc_reg,
+ Analog_Trigger_Etc_Register);
+ break;
+ case 1:
+ devpriv->stc_writew(dev, G1_Reset, Joint_Reset_Register);
+ ni_set_bits(dev, Interrupt_B_Enable_Register,
+ G1_TC_Interrupt_Enable, 0);
+ ni_set_bits(dev, Interrupt_B_Enable_Register,
+ G0_Gate_Interrupt_Enable, 0);
+ temp_ack_reg |= G1_Gate_Error_Confirm;
+ temp_ack_reg |= G1_TC_Error_Confirm;
+ temp_ack_reg |= G1_TC_Interrupt_Ack;
+ temp_ack_reg |= G1_Gate_Interrupt_Ack;
+ devpriv->stc_writew(dev, temp_ack_reg,
+ Interrupt_B_Ack_Register);
+
+ devpriv->an_trig_etc_reg |= GPFO_1_Output_Enable;
+ devpriv->stc_writew(dev, devpriv->an_trig_etc_reg,
+ Analog_Trigger_Etc_Register);
+ break;
+ };
+
+ devpriv->gpct_mode[chan] = 0;
+ devpriv->gpct_input_select[chan] = 0;
+ devpriv->gpct_command[chan] = 0;
+
+ devpriv->gpct_command[chan] |= G_Synchronized_Gate;
+
+ devpriv->stc_writew(dev, devpriv->gpct_mode[chan],
+ G_Mode_Register(chan));
+ devpriv->stc_writew(dev, devpriv->gpct_input_select[chan],
+ G_Input_Select_Register(chan));
+ devpriv->stc_writew(dev, 0, G_Autoincrement_Register(chan));
+
+ //printk("exit GPCT_Reset\n");
+}
+
+#endif
+
+static int ni_gpct_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ struct ni_gpct *counter = s->private;
+ return ni_tio_insn_config(counter, insn, data);
+}
+
+static int ni_gpct_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ struct ni_gpct *counter = s->private;
+ return ni_tio_rinsn(counter, insn, data);
+}
+
+static int ni_gpct_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ struct ni_gpct *counter = s->private;
+ return ni_tio_winsn(counter, insn, data);
+}
+
+static int ni_gpct_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int retval;
+#ifdef PCIDMA
+ struct ni_gpct *counter = s->private;
+// const struct comedi_cmd *cmd = &s->async->cmd;
+
+ retval = ni_request_gpct_mite_channel(dev, counter->counter_index,
+ COMEDI_INPUT);
+ if (retval) {
+ comedi_error(dev,
+ "no dma channel available for use by counter");
+ return retval;
+ }
+ ni_tio_acknowledge_and_confirm(counter, NULL, NULL, NULL, NULL);
+ ni_e_series_enable_second_irq(dev, counter->counter_index, 1);
+ retval = ni_tio_cmd(counter, s->async);
+#else
+ retval = -ENOTSUPP;
+#endif
+ return retval;
+}
+
+static int ni_gpct_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+#ifdef PCIDMA
+ struct ni_gpct *counter = s->private;
+
+ return ni_tio_cmdtest(counter, cmd);
+#else
+ return -ENOTSUPP;
+#endif
+}
+
+static int ni_gpct_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+#ifdef PCIDMA
+ struct ni_gpct *counter = s->private;
+ int retval;
+
+ retval = ni_tio_cancel(counter);
+ ni_e_series_enable_second_irq(dev, counter->counter_index, 0);
+ ni_release_gpct_mite_channel(dev, counter->counter_index);
+ return retval;
+#else
+ return 0;
+#endif
+}
+
+/*
+ *
+ * Programmable Function Inputs
+ *
+ */
+
+static int ni_m_series_set_pfi_routing(struct comedi_device * dev, unsigned chan,
+ unsigned source)
+{
+ unsigned pfi_reg_index;
+ unsigned array_offset;
+ if ((source & 0x1f) != source)
+ return -EINVAL;
+ pfi_reg_index = 1 + chan / 3;
+ array_offset = pfi_reg_index - 1;
+ devpriv->pfi_output_select_reg[array_offset] &=
+ ~MSeries_PFI_Output_Select_Mask(chan);
+ devpriv->pfi_output_select_reg[array_offset] |=
+ MSeries_PFI_Output_Select_Bits(chan, source);
+ ni_writew(devpriv->pfi_output_select_reg[array_offset],
+ M_Offset_PFI_Output_Select(pfi_reg_index));
+ return 2;
+}
+
+static int ni_old_set_pfi_routing(struct comedi_device * dev, unsigned chan,
+ unsigned source)
+{
+ // pre-m-series boards have fixed signals on pfi pins
+ if (source != ni_old_get_pfi_routing(dev, chan))
+ return -EINVAL;
+ return 2;
+}
+
+static int ni_set_pfi_routing(struct comedi_device * dev, unsigned chan,
+ unsigned source)
+{
+ if (boardtype.reg_type & ni_reg_m_series_mask)
+ return ni_m_series_set_pfi_routing(dev, chan, source);
+ else
+ return ni_old_set_pfi_routing(dev, chan, source);
+}
+
+static unsigned ni_m_series_get_pfi_routing(struct comedi_device * dev, unsigned chan)
+{
+ const unsigned array_offset = chan / 3;
+ return MSeries_PFI_Output_Select_Source(chan,
+ devpriv->pfi_output_select_reg[array_offset]);
+}
+
+static unsigned ni_old_get_pfi_routing(struct comedi_device * dev, unsigned chan)
+{
+ // pre-m-series boards have fixed signals on pfi pins
+ switch (chan) {
+ case 0:
+ return NI_PFI_OUTPUT_AI_START1;
+ break;
+ case 1:
+ return NI_PFI_OUTPUT_AI_START2;
+ break;
+ case 2:
+ return NI_PFI_OUTPUT_AI_CONVERT;
+ break;
+ case 3:
+ return NI_PFI_OUTPUT_G_SRC1;
+ break;
+ case 4:
+ return NI_PFI_OUTPUT_G_GATE1;
+ break;
+ case 5:
+ return NI_PFI_OUTPUT_AO_UPDATE_N;
+ break;
+ case 6:
+ return NI_PFI_OUTPUT_AO_START1;
+ break;
+ case 7:
+ return NI_PFI_OUTPUT_AI_START_PULSE;
+ break;
+ case 8:
+ return NI_PFI_OUTPUT_G_SRC0;
+ break;
+ case 9:
+ return NI_PFI_OUTPUT_G_GATE0;
+ break;
+ default:
+ rt_printk("%s: bug, unhandled case in switch.\n", __FUNCTION__);
+ break;
+ }
+ return 0;
+}
+
+static unsigned ni_get_pfi_routing(struct comedi_device * dev, unsigned chan)
+{
+ if (boardtype.reg_type & ni_reg_m_series_mask)
+ return ni_m_series_get_pfi_routing(dev, chan);
+ else
+ return ni_old_get_pfi_routing(dev, chan);
+}
+
+static int ni_config_filter(struct comedi_device * dev, unsigned pfi_channel,
+ enum ni_pfi_filter_select filter)
+{
+ unsigned bits;
+ if ((boardtype.reg_type & ni_reg_m_series_mask) == 0) {
+ return -ENOTSUPP;
+ }
+ bits = ni_readl(M_Offset_PFI_Filter);
+ bits &= ~MSeries_PFI_Filter_Select_Mask(pfi_channel);
+ bits |= MSeries_PFI_Filter_Select_Bits(pfi_channel, filter);
+ ni_writel(bits, M_Offset_PFI_Filter);
+ return 0;
+}
+
+static int ni_pfi_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if ((boardtype.reg_type & ni_reg_m_series_mask) == 0) {
+ return -ENOTSUPP;
+ }
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ ni_writew(s->state, M_Offset_PFI_DO);
+ }
+ data[1] = ni_readw(M_Offset_PFI_DI);
+ return 2;
+}
+
+static int ni_pfi_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int chan;
+
+ if (insn->n < 1)
+ return -EINVAL;
+
+ chan = CR_CHAN(insn->chanspec);
+
+ switch (data[0]) {
+ case COMEDI_OUTPUT:
+ ni_set_bits(dev, IO_Bidirection_Pin_Register, 1 << chan, 1);
+ break;
+ case COMEDI_INPUT:
+ ni_set_bits(dev, IO_Bidirection_Pin_Register, 1 << chan, 0);
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (devpriv->
+ io_bidirection_pin_reg & (1 << chan)) ? COMEDI_OUTPUT :
+ COMEDI_INPUT;
+ return 0;
+ break;
+ case INSN_CONFIG_SET_ROUTING:
+ return ni_set_pfi_routing(dev, chan, data[1]);
+ break;
+ case INSN_CONFIG_GET_ROUTING:
+ data[1] = ni_get_pfi_routing(dev, chan);
+ break;
+ case INSN_CONFIG_FILTER:
+ return ni_config_filter(dev, chan, data[1]);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ *
+ * NI RTSI Bus Functions
+ *
+ */
+static void ni_rtsi_init(struct comedi_device * dev)
+{
+ // Initialises the RTSI bus signal switch to a default state
+
+ // Set clock mode to internal
+ devpriv->clock_and_fout2 = MSeries_RTSI_10MHz_Bit;
+ if (ni_set_master_clock(dev, NI_MIO_INTERNAL_CLOCK, 0) < 0) {
+ rt_printk("ni_set_master_clock failed, bug?");
+ }
+ // default internal lines routing to RTSI bus lines
+ devpriv->rtsi_trig_a_output_reg =
+ RTSI_Trig_Output_Bits(0,
+ NI_RTSI_OUTPUT_ADR_START1) | RTSI_Trig_Output_Bits(1,
+ NI_RTSI_OUTPUT_ADR_START2) | RTSI_Trig_Output_Bits(2,
+ NI_RTSI_OUTPUT_SCLKG) | RTSI_Trig_Output_Bits(3,
+ NI_RTSI_OUTPUT_DACUPDN);
+ devpriv->stc_writew(dev, devpriv->rtsi_trig_a_output_reg,
+ RTSI_Trig_A_Output_Register);
+ devpriv->rtsi_trig_b_output_reg =
+ RTSI_Trig_Output_Bits(4,
+ NI_RTSI_OUTPUT_DA_START1) | RTSI_Trig_Output_Bits(5,
+ NI_RTSI_OUTPUT_G_SRC0) | RTSI_Trig_Output_Bits(6,
+ NI_RTSI_OUTPUT_G_GATE0);
+ if (boardtype.reg_type & ni_reg_m_series_mask)
+ devpriv->rtsi_trig_b_output_reg |=
+ RTSI_Trig_Output_Bits(7, NI_RTSI_OUTPUT_RTSI_OSC);
+ devpriv->stc_writew(dev, devpriv->rtsi_trig_b_output_reg,
+ RTSI_Trig_B_Output_Register);
+
+ // Sets the source and direction of the 4 on board lines
+// devpriv->stc_writew(dev, 0x0000, RTSI_Board_Register);
+}
+
+static int ni_rtsi_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ data[1] = 0;
+
+ return 2;
+}
+
+/* Find best multiplier/divider to try and get the PLL running at 80 MHz
+ * given an arbitrary frequency input clock */
+static int ni_mseries_get_pll_parameters(unsigned reference_period_ns,
+ unsigned *freq_divider, unsigned *freq_multiplier,
+ unsigned *actual_period_ns)
+{
+ unsigned div;
+ unsigned best_div = 1;
+ static const unsigned max_div = 0x10;
+ unsigned mult;
+ unsigned best_mult = 1;
+ static const unsigned max_mult = 0x100;
+ static const unsigned pico_per_nano = 1000;
+
+ const unsigned reference_picosec = reference_period_ns * pico_per_nano;
+ /* m-series wants the phased-locked loop to output 80MHz, which is divided by 4 to
+ * 20 MHz for most timing clocks */
+ static const unsigned target_picosec = 12500;
+ static const unsigned fudge_factor_80_to_20Mhz = 4;
+ int best_period_picosec = 0;
+ for (div = 1; div <= max_div; ++div) {
+ for (mult = 1; mult <= max_mult; ++mult) {
+ unsigned new_period_ps =
+ (reference_picosec * div) / mult;
+ if (abs(new_period_ps - target_picosec) <
+ abs(best_period_picosec - target_picosec)) {
+ best_period_picosec = new_period_ps;
+ best_div = div;
+ best_mult = mult;
+ }
+ }
+ }
+ if (best_period_picosec == 0) {
+ rt_printk("%s: bug, failed to find pll parameters\n",
+ __FUNCTION__);
+ return -EIO;
+ }
+ *freq_divider = best_div;
+ *freq_multiplier = best_mult;
+ *actual_period_ns =
+ (best_period_picosec * fudge_factor_80_to_20Mhz +
+ (pico_per_nano / 2)) / pico_per_nano;
+ return 0;
+}
+
+static inline unsigned num_configurable_rtsi_channels(struct comedi_device * dev)
+{
+ if (boardtype.reg_type & ni_reg_m_series_mask)
+ return 8;
+ else
+ return 7;
+}
+
+static int ni_mseries_set_pll_master_clock(struct comedi_device * dev, unsigned source,
+ unsigned period_ns)
+{
+ static const unsigned min_period_ns = 50;
+ static const unsigned max_period_ns = 1000;
+ static const unsigned timeout = 1000;
+ unsigned pll_control_bits;
+ unsigned freq_divider;
+ unsigned freq_multiplier;
+ unsigned i;
+ int retval;
+ if (source == NI_MIO_PLL_PXI10_CLOCK)
+ period_ns = 100;
+ // these limits are somewhat arbitrary, but NI advertises 1 to 20MHz range so we'll use that
+ if (period_ns < min_period_ns || period_ns > max_period_ns) {
+ rt_printk
+ ("%s: you must specify an input clock frequency between %i and %i nanosec "
+ "for the phased-lock loop.\n", __FUNCTION__,
+ min_period_ns, max_period_ns);
+ return -EINVAL;
+ }
+ devpriv->rtsi_trig_direction_reg &= ~Use_RTSI_Clock_Bit;
+ devpriv->stc_writew(dev, devpriv->rtsi_trig_direction_reg,
+ RTSI_Trig_Direction_Register);
+ pll_control_bits =
+ MSeries_PLL_Enable_Bit | MSeries_PLL_VCO_Mode_75_150MHz_Bits;
+ devpriv->clock_and_fout2 |=
+ MSeries_Timebase1_Select_Bit | MSeries_Timebase3_Select_Bit;
+ devpriv->clock_and_fout2 &= ~MSeries_PLL_In_Source_Select_Mask;
+ switch (source) {
+ case NI_MIO_PLL_PXI_STAR_TRIGGER_CLOCK:
+ devpriv->clock_and_fout2 |=
+ MSeries_PLL_In_Source_Select_Star_Trigger_Bits;
+ retval = ni_mseries_get_pll_parameters(period_ns, &freq_divider,
+ &freq_multiplier, &devpriv->clock_ns);
+ if (retval < 0)
+ return retval;
+ break;
+ case NI_MIO_PLL_PXI10_CLOCK:
+ /* pxi clock is 10MHz */
+ devpriv->clock_and_fout2 |=
+ MSeries_PLL_In_Source_Select_PXI_Clock10;
+ retval = ni_mseries_get_pll_parameters(period_ns, &freq_divider,
+ &freq_multiplier, &devpriv->clock_ns);
+ if (retval < 0)
+ return retval;
+ break;
+ default:
+ {
+ unsigned rtsi_channel;
+ static const unsigned max_rtsi_channel = 7;
+ for (rtsi_channel = 0; rtsi_channel <= max_rtsi_channel;
+ ++rtsi_channel) {
+ if (source ==
+ NI_MIO_PLL_RTSI_CLOCK(rtsi_channel)) {
+ devpriv->clock_and_fout2 |=
+ MSeries_PLL_In_Source_Select_RTSI_Bits
+ (rtsi_channel);
+ break;
+ }
+ }
+ if (rtsi_channel > max_rtsi_channel)
+ return -EINVAL;
+ retval = ni_mseries_get_pll_parameters(period_ns,
+ &freq_divider, &freq_multiplier,
+ &devpriv->clock_ns);
+ if (retval < 0)
+ return retval;
+ }
+ break;
+ }
+ ni_writew(devpriv->clock_and_fout2, M_Offset_Clock_and_Fout2);
+ pll_control_bits |=
+ MSeries_PLL_Divisor_Bits(freq_divider) |
+ MSeries_PLL_Multiplier_Bits(freq_multiplier);
+// rt_printk("using divider=%i, multiplier=%i for PLL. pll_control_bits = 0x%x\n", freq_divider, freq_multiplier, pll_control_bits);
+// rt_printk("clock_ns=%d\n", devpriv->clock_ns);
+ ni_writew(pll_control_bits, M_Offset_PLL_Control);
+ devpriv->clock_source = source;
+ /* it seems to typically take a few hundred microseconds for PLL to lock */
+ for (i = 0; i < timeout; ++i) {
+ if (ni_readw(M_Offset_PLL_Status) & MSeries_PLL_Locked_Bit) {
+ break;
+ }
+ udelay(1);
+ }
+ if (i == timeout) {
+ rt_printk
+ ("%s: timed out waiting for PLL to lock to reference clock source %i with period %i ns.\n",
+ __FUNCTION__, source, period_ns);
+ return -ETIMEDOUT;
+ }
+ return 3;
+}
+
+static int ni_set_master_clock(struct comedi_device * dev, unsigned source,
+ unsigned period_ns)
+{
+ if (source == NI_MIO_INTERNAL_CLOCK) {
+ devpriv->rtsi_trig_direction_reg &= ~Use_RTSI_Clock_Bit;
+ devpriv->stc_writew(dev, devpriv->rtsi_trig_direction_reg,
+ RTSI_Trig_Direction_Register);
+ devpriv->clock_ns = TIMEBASE_1_NS;
+ if (boardtype.reg_type & ni_reg_m_series_mask) {
+ devpriv->clock_and_fout2 &=
+ ~(MSeries_Timebase1_Select_Bit |
+ MSeries_Timebase3_Select_Bit);
+ ni_writew(devpriv->clock_and_fout2,
+ M_Offset_Clock_and_Fout2);
+ ni_writew(0, M_Offset_PLL_Control);
+ }
+ devpriv->clock_source = source;
+ } else {
+ if (boardtype.reg_type & ni_reg_m_series_mask) {
+ return ni_mseries_set_pll_master_clock(dev, source,
+ period_ns);
+ } else {
+ if (source == NI_MIO_RTSI_CLOCK) {
+ devpriv->rtsi_trig_direction_reg |=
+ Use_RTSI_Clock_Bit;
+ devpriv->stc_writew(dev,
+ devpriv->rtsi_trig_direction_reg,
+ RTSI_Trig_Direction_Register);
+ if (period_ns == 0) {
+ rt_printk
+ ("%s: we don't handle an unspecified clock period correctly yet, returning error.\n",
+ __FUNCTION__);
+ return -EINVAL;
+ } else {
+ devpriv->clock_ns = period_ns;
+ }
+ devpriv->clock_source = source;
+ } else
+ return -EINVAL;
+ }
+ }
+ return 3;
+}
+
+static int ni_valid_rtsi_output_source(struct comedi_device * dev, unsigned chan,
+ unsigned source)
+{
+ if (chan >= num_configurable_rtsi_channels(dev)) {
+ if (chan == old_RTSI_clock_channel) {
+ if (source == NI_RTSI_OUTPUT_RTSI_OSC)
+ return 1;
+ else {
+ rt_printk
+ ("%s: invalid source for channel=%i, channel %i is always the RTSI clock for pre-m-series boards.\n",
+ __FUNCTION__, chan,
+ old_RTSI_clock_channel);
+ return 0;
+ }
+ }
+ return 0;
+ }
+ switch (source) {
+ case NI_RTSI_OUTPUT_ADR_START1:
+ case NI_RTSI_OUTPUT_ADR_START2:
+ case NI_RTSI_OUTPUT_SCLKG:
+ case NI_RTSI_OUTPUT_DACUPDN:
+ case NI_RTSI_OUTPUT_DA_START1:
+ case NI_RTSI_OUTPUT_G_SRC0:
+ case NI_RTSI_OUTPUT_G_GATE0:
+ case NI_RTSI_OUTPUT_RGOUT0:
+ case NI_RTSI_OUTPUT_RTSI_BRD_0:
+ return 1;
+ break;
+ case NI_RTSI_OUTPUT_RTSI_OSC:
+ if (boardtype.reg_type & ni_reg_m_series_mask)
+ return 1;
+ else
+ return 0;
+ break;
+ default:
+ return 0;
+ break;
+ }
+}
+
+static int ni_set_rtsi_routing(struct comedi_device * dev, unsigned chan,
+ unsigned source)
+{
+ if (ni_valid_rtsi_output_source(dev, chan, source) == 0)
+ return -EINVAL;
+ if (chan < 4) {
+ devpriv->rtsi_trig_a_output_reg &= ~RTSI_Trig_Output_Mask(chan);
+ devpriv->rtsi_trig_a_output_reg |=
+ RTSI_Trig_Output_Bits(chan, source);
+ devpriv->stc_writew(dev, devpriv->rtsi_trig_a_output_reg,
+ RTSI_Trig_A_Output_Register);
+ } else if (chan < 8) {
+ devpriv->rtsi_trig_b_output_reg &= ~RTSI_Trig_Output_Mask(chan);
+ devpriv->rtsi_trig_b_output_reg |=
+ RTSI_Trig_Output_Bits(chan, source);
+ devpriv->stc_writew(dev, devpriv->rtsi_trig_b_output_reg,
+ RTSI_Trig_B_Output_Register);
+ }
+ return 2;
+}
+
+static unsigned ni_get_rtsi_routing(struct comedi_device * dev, unsigned chan)
+{
+ if (chan < 4) {
+ return RTSI_Trig_Output_Source(chan,
+ devpriv->rtsi_trig_a_output_reg);
+ } else if (chan < num_configurable_rtsi_channels(dev)) {
+ return RTSI_Trig_Output_Source(chan,
+ devpriv->rtsi_trig_b_output_reg);
+ } else {
+ if (chan == old_RTSI_clock_channel)
+ return NI_RTSI_OUTPUT_RTSI_OSC;
+ rt_printk("%s: bug! should never get here?\n", __FUNCTION__);
+ return 0;
+ }
+}
+
+static int ni_rtsi_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ if (chan < num_configurable_rtsi_channels(dev)) {
+ devpriv->rtsi_trig_direction_reg |=
+ RTSI_Output_Bit(chan,
+ (boardtype.reg_type & ni_reg_m_series_mask) !=
+ 0);
+ } else if (chan == old_RTSI_clock_channel) {
+ devpriv->rtsi_trig_direction_reg |=
+ Drive_RTSI_Clock_Bit;
+ }
+ devpriv->stc_writew(dev, devpriv->rtsi_trig_direction_reg,
+ RTSI_Trig_Direction_Register);
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ if (chan < num_configurable_rtsi_channels(dev)) {
+ devpriv->rtsi_trig_direction_reg &=
+ ~RTSI_Output_Bit(chan,
+ (boardtype.reg_type & ni_reg_m_series_mask) !=
+ 0);
+ } else if (chan == old_RTSI_clock_channel) {
+ devpriv->rtsi_trig_direction_reg &=
+ ~Drive_RTSI_Clock_Bit;
+ }
+ devpriv->stc_writew(dev, devpriv->rtsi_trig_direction_reg,
+ RTSI_Trig_Direction_Register);
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ if (chan < num_configurable_rtsi_channels(dev)) {
+ data[1] =
+ (devpriv->
+ rtsi_trig_direction_reg & RTSI_Output_Bit(chan,
+ (boardtype.
+ reg_type & ni_reg_m_series_mask)
+ !=
+ 0)) ? INSN_CONFIG_DIO_OUTPUT :
+ INSN_CONFIG_DIO_INPUT;
+ } else if (chan == old_RTSI_clock_channel) {
+ data[1] =
+ (devpriv->
+ rtsi_trig_direction_reg & Drive_RTSI_Clock_Bit)
+ ? INSN_CONFIG_DIO_OUTPUT :
+ INSN_CONFIG_DIO_INPUT;
+ }
+ return 2;
+ break;
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ return ni_set_master_clock(dev, data[1], data[2]);
+ break;
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ data[1] = devpriv->clock_source;
+ data[2] = devpriv->clock_ns;
+ return 3;
+ break;
+ case INSN_CONFIG_SET_ROUTING:
+ return ni_set_rtsi_routing(dev, chan, data[1]);
+ break;
+ case INSN_CONFIG_GET_ROUTING:
+ data[1] = ni_get_rtsi_routing(dev, chan);
+ return 2;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return 1;
+}
+
+static int cs5529_wait_for_idle(struct comedi_device * dev)
+{
+ unsigned short status;
+ const int timeout = HZ;
+ int i;
+
+ for (i = 0; i < timeout; i++) {
+ status = ni_ao_win_inw(dev, CAL_ADC_Status_67xx);
+ if ((status & CSS_ADC_BUSY) == 0) {
+ break;
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (schedule_timeout(1)) {
+ return -EIO;
+ }
+ }
+//printk("looped %i times waiting for idle\n", i);
+ if (i == timeout) {
+ rt_printk("%s: %s: timeout\n", __FILE__, __FUNCTION__);
+ return -ETIME;
+ }
+ return 0;
+}
+
+static void cs5529_command(struct comedi_device * dev, unsigned short value)
+{
+ static const int timeout = 100;
+ int i;
+
+ ni_ao_win_outw(dev, value, CAL_ADC_Command_67xx);
+ /* give time for command to start being serially clocked into cs5529.
+ * this insures that the CSS_ADC_BUSY bit will get properly
+ * set before we exit this function.
+ */
+ for (i = 0; i < timeout; i++) {
+ if ((ni_ao_win_inw(dev, CAL_ADC_Status_67xx) & CSS_ADC_BUSY))
+ break;
+ comedi_udelay(1);
+ }
+//printk("looped %i times writing command to cs5529\n", i);
+ if (i == timeout) {
+ comedi_error(dev, "possible problem - never saw adc go busy?");
+ }
+}
+
+/* write to cs5529 register */
+static void cs5529_config_write(struct comedi_device * dev, unsigned int value,
+ unsigned int reg_select_bits)
+{
+ ni_ao_win_outw(dev, ((value >> 16) & 0xff),
+ CAL_ADC_Config_Data_High_Word_67xx);
+ ni_ao_win_outw(dev, (value & 0xffff),
+ CAL_ADC_Config_Data_Low_Word_67xx);
+ reg_select_bits &= CSCMD_REGISTER_SELECT_MASK;
+ cs5529_command(dev, CSCMD_COMMAND | reg_select_bits);
+ if (cs5529_wait_for_idle(dev))
+ comedi_error(dev, "time or signal in cs5529_config_write()");
+}
+
+#ifdef NI_CS5529_DEBUG
+/* read from cs5529 register */
+static unsigned int cs5529_config_read(struct comedi_device * dev,
+ unsigned int reg_select_bits)
+{
+ unsigned int value;
+
+ reg_select_bits &= CSCMD_REGISTER_SELECT_MASK;
+ cs5529_command(dev, CSCMD_COMMAND | CSCMD_READ | reg_select_bits);
+ if (cs5529_wait_for_idle(dev))
+ comedi_error(dev, "timeout or signal in cs5529_config_read()");
+ value = (ni_ao_win_inw(dev,
+ CAL_ADC_Config_Data_High_Word_67xx) << 16) & 0xff0000;
+ value |= ni_ao_win_inw(dev, CAL_ADC_Config_Data_Low_Word_67xx) & 0xffff;
+ return value;
+}
+#endif
+
+static int cs5529_do_conversion(struct comedi_device * dev, unsigned short *data)
+{
+ int retval;
+ unsigned short status;
+
+ cs5529_command(dev, CSCMD_COMMAND | CSCMD_SINGLE_CONVERSION);
+ retval = cs5529_wait_for_idle(dev);
+ if (retval) {
+ comedi_error(dev,
+ "timeout or signal in cs5529_do_conversion()");
+ return -ETIME;
+ }
+ status = ni_ao_win_inw(dev, CAL_ADC_Status_67xx);
+ if (status & CSS_OSC_DETECT) {
+ rt_printk
+ ("ni_mio_common: cs5529 conversion error, status CSS_OSC_DETECT\n");
+ return -EIO;
+ }
+ if (status & CSS_OVERRANGE) {
+ rt_printk
+ ("ni_mio_common: cs5529 conversion error, overrange (ignoring)\n");
+ }
+ if (data) {
+ *data = ni_ao_win_inw(dev, CAL_ADC_Data_67xx);
+ /* cs5529 returns 16 bit signed data in bipolar mode */
+ *data ^= (1 << 15);
+ }
+ return 0;
+}
+
+static int cs5529_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, retval;
+ unsigned short sample;
+ unsigned int channel_select;
+ const unsigned int INTERNAL_REF = 0x1000;
+
+ /* Set calibration adc source. Docs lie, reference select bits 8 to 11
+ * do nothing. bit 12 seems to chooses internal reference voltage, bit
+ * 13 causes the adc input to go overrange (maybe reads external reference?) */
+ if (insn->chanspec & CR_ALT_SOURCE)
+ channel_select = INTERNAL_REF;
+ else
+ channel_select = CR_CHAN(insn->chanspec);
+ ni_ao_win_outw(dev, channel_select, AO_Calibration_Channel_Select_67xx);
+
+ for (n = 0; n < insn->n; n++) {
+ retval = cs5529_do_conversion(dev, &sample);
+ if (retval < 0)
+ return retval;
+ data[n] = sample;
+ }
+ return insn->n;
+}
+
+static int init_cs5529(struct comedi_device * dev)
+{
+ unsigned int config_bits =
+ CSCFG_PORT_MODE | CSCFG_WORD_RATE_2180_CYCLES;
+
+#if 1
+ /* do self-calibration */
+ cs5529_config_write(dev, config_bits | CSCFG_SELF_CAL_OFFSET_GAIN,
+ CSCMD_CONFIG_REGISTER);
+ /* need to force a conversion for calibration to run */
+ cs5529_do_conversion(dev, NULL);
+#else
+ /* force gain calibration to 1 */
+ cs5529_config_write(dev, 0x400000, CSCMD_GAIN_REGISTER);
+ cs5529_config_write(dev, config_bits | CSCFG_SELF_CAL_OFFSET,
+ CSCMD_CONFIG_REGISTER);
+ if (cs5529_wait_for_idle(dev))
+ comedi_error(dev, "timeout or signal in init_cs5529()\n");
+#endif
+#ifdef NI_CS5529_DEBUG
+ rt_printk("config: 0x%x\n", cs5529_config_read(dev,
+ CSCMD_CONFIG_REGISTER));
+ rt_printk("gain: 0x%x\n", cs5529_config_read(dev,
+ CSCMD_GAIN_REGISTER));
+ rt_printk("offset: 0x%x\n", cs5529_config_read(dev,
+ CSCMD_OFFSET_REGISTER));
+#endif
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/ni_mio_cs.c b/drivers/staging/comedi/drivers/ni_mio_cs.c
new file mode 100644
index 000000000000..d6357c25aca5
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_mio_cs.c
@@ -0,0 +1,551 @@
+/*
+ comedi/drivers/ni_mio_cs.c
+ Hardware driver for NI PCMCIA MIO E series cards
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: ni_mio_cs
+Description: National Instruments DAQCard E series
+Author: ds
+Status: works
+Devices: [National Instruments] DAQCard-AI-16XE-50 (ni_mio_cs),
+ DAQCard-AI-16E-4, DAQCard-6062E, DAQCard-6024E, DAQCard-6036E
+Updated: Thu Oct 23 19:43:17 CDT 2003
+
+See the notes in the ni_atmio.o driver.
+*/
+/*
+ The real guts of the driver is in ni_mio_common.c, which is
+ included by all the E series drivers.
+
+ References for specifications:
+
+ 341080a.pdf DAQCard E Series Register Level Programmer Manual
+
+*/
+
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+
+#include "ni_stc.h"
+#include "8255.h"
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+#undef DEBUG
+
+#define ATMIO 1
+#undef PCIMIO
+
+/*
+ * AT specific setup
+ */
+
+#define NI_SIZE 0x20
+
+#define MAX_N_CALDACS 32
+
+static const ni_board ni_boards[] = {
+ {device_id:0x010d,
+ name: "DAQCard-ai-16xe-50",
+ n_adchan:16,
+ adbits: 16,
+ ai_fifo_depth:1024,
+ alwaysdither:0,
+ gainlkup:ai_gain_8,
+ ai_speed:5000,
+ n_aochan:0,
+ aobits: 0,
+ ao_fifo_depth:0,
+ ao_unipolar:0,
+ num_p0_dio_channels:8,
+ has_8255:0,
+ caldac: {dac8800, dac8043},
+ },
+ {device_id:0x010c,
+ name: "DAQCard-ai-16e-4",
+ n_adchan:16,
+ adbits: 12,
+ ai_fifo_depth:1024,
+ alwaysdither:0,
+ gainlkup:ai_gain_16,
+ ai_speed:4000,
+ n_aochan:0,
+ aobits: 0,
+ ao_fifo_depth:0,
+ ao_unipolar:0,
+ num_p0_dio_channels:8,
+ has_8255:0,
+ caldac: {mb88341}, /* verified */
+ },
+ {device_id:0x02c4,
+ name: "DAQCard-6062E",
+ n_adchan:16,
+ adbits: 12,
+ ai_fifo_depth:8192,
+ alwaysdither:0,
+ gainlkup:ai_gain_16,
+ ai_speed:2000,
+ n_aochan:2,
+ aobits: 12,
+ ao_fifo_depth:2048,
+ ao_range_table:&range_bipolar10,
+ ao_unipolar:0,
+ ao_speed:1176,
+ num_p0_dio_channels:8,
+ has_8255:0,
+ caldac: {ad8804_debug}, /* verified */
+ },
+ {device_id:0x075e,
+ name: "DAQCard-6024E", /* specs incorrect! */
+ n_adchan:16,
+ adbits: 12,
+ ai_fifo_depth:1024,
+ alwaysdither:0,
+ gainlkup:ai_gain_16,
+ ai_speed:5000,
+ n_aochan:2,
+ aobits: 12,
+ ao_fifo_depth:0,
+ ao_range_table:&range_bipolar10,
+ ao_unipolar:0,
+ ao_speed:1000000,
+ num_p0_dio_channels:8,
+ has_8255:0,
+ caldac: {ad8804_debug},
+ },
+ {device_id:0x0245,
+ name: "DAQCard-6036E", /* specs incorrect! */
+ n_adchan:16,
+ adbits: 16,
+ ai_fifo_depth:1024,
+ alwaysdither:1,
+ gainlkup:ai_gain_4,
+ ai_speed:5000,
+ n_aochan:2,
+ aobits: 16,
+ ao_fifo_depth:0,
+ ao_range_table:&range_bipolar10,
+ ao_unipolar:0,
+ ao_speed:1000000,
+ num_p0_dio_channels:8,
+ has_8255:0,
+ caldac: {ad8804_debug},
+ },
+#if 0
+ {device_id:0x0000, /* unknown */
+ name: "DAQCard-6715",
+ n_adchan:0,
+ n_aochan:8,
+ aobits: 12,
+ ao_671x: 8192,
+ num_p0_dio_channels:8,
+ caldac: {mb88341, mb88341},
+ },
+#endif
+ /* N.B. Update ni_mio_cs_ids[] when entries added above. */
+};
+
+#define interrupt_pin(a) 0
+
+#define IRQ_POLARITY 1
+
+#define NI_E_IRQ_FLAGS IRQF_SHARED
+
+struct ni_private {
+
+ struct pcmcia_device *link;
+
+ NI_PRIVATE_COMMON};
+
+#define devpriv ((struct ni_private *)dev->private)
+
+/* How we access registers */
+
+#define ni_writel(a,b) (outl((a),(b)+dev->iobase))
+#define ni_readl(a) (inl((a)+dev->iobase))
+#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))
+
+/* How we access windowed registers */
+
+/* We automatically take advantage of STC registers that can be
+ * read/written directly in the I/O space of the board. The
+ * DAQCard devices map the low 8 STC registers to iobase+addr*2. */
+
+static void mio_cs_win_out(struct comedi_device * dev, uint16_t data, int addr)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->window_lock, flags);
+ if (addr < 8) {
+ ni_writew(data, addr * 2);
+ } else {
+ ni_writew(addr, Window_Address);
+ ni_writew(data, Window_Data);
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->window_lock, flags);
+}
+
+static uint16_t mio_cs_win_in(struct comedi_device * dev, int addr)
+{
+ unsigned long flags;
+ uint16_t ret;
+
+ comedi_spin_lock_irqsave(&devpriv->window_lock, flags);
+ if (addr < 8) {
+ ret = ni_readw(addr * 2);
+ } else {
+ ni_writew(addr, Window_Address);
+ ret = ni_readw(Window_Data);
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->window_lock, flags);
+
+ return ret;
+}
+
+static int mio_cs_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int mio_cs_detach(struct comedi_device * dev);
+static struct comedi_driver driver_ni_mio_cs = {
+ driver_name:"ni_mio_cs",
+ module:THIS_MODULE,
+ attach:mio_cs_attach,
+ detach:mio_cs_detach,
+};
+
+#include "ni_mio_common.c"
+
+static int ni_getboardtype(struct comedi_device * dev, struct pcmcia_device *link);
+
+/* clean up allocated resources */
+/* called when driver is removed */
+static int mio_cs_detach(struct comedi_device * dev)
+{
+ mio_common_detach(dev);
+
+ /* PCMCIA layer frees the IO region */
+
+ if (dev->irq) {
+ comedi_free_irq(dev->irq, dev);
+ }
+
+ return 0;
+}
+
+static void mio_cs_config(struct pcmcia_device *link);
+static void cs_release(struct pcmcia_device *link);
+static void cs_detach(struct pcmcia_device *);
+
+static struct pcmcia_device *cur_dev = NULL;
+static const dev_info_t dev_info = "ni_mio_cs";
+static dev_node_t dev_node = {
+ "ni_mio_cs",
+ COMEDI_MAJOR, 0,
+ NULL
+};
+static int cs_attach(struct pcmcia_device *link)
+{
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ link->io.NumPorts1 = 16;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ cur_dev = link;
+
+ mio_cs_config(link);
+
+ return 0;
+}
+
+static void cs_release(struct pcmcia_device *link)
+{
+ pcmcia_disable_device(link);
+}
+
+static void cs_detach(struct pcmcia_device *link)
+{
+ DPRINTK("cs_detach(link=%p)\n", link);
+
+ if (link->dev_node) {
+ cs_release(link);
+ }
+}
+
+static int mio_cs_suspend(struct pcmcia_device *link)
+{
+ DPRINTK("pm suspend\n");
+
+ return 0;
+}
+
+static int mio_cs_resume(struct pcmcia_device *link)
+{
+ DPRINTK("pm resume\n");
+ return 0;
+}
+
+static void mio_cs_config(struct pcmcia_device *link)
+{
+ tuple_t tuple;
+ u_short buf[128];
+ cisparse_t parse;
+ int manfid = 0, prodid = 0;
+ int ret;
+
+ DPRINTK("mio_cs_config(link=%p)\n", link);
+
+ tuple.TupleData = (cisdata_t *) buf;
+ tuple.TupleOffset = 0;
+ tuple.TupleDataMax = 255;
+ tuple.Attributes = 0;
+
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ ret = pcmcia_get_first_tuple(link, &tuple);
+ ret = pcmcia_get_tuple_data(link, &tuple);
+ ret = pcmcia_parse_tuple(&tuple, &parse);
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+
+#if 0
+ tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
+ tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK;
+ info->multi(first_tuple(link, &tuple, &parse) == 0);
+#endif
+
+ tuple.DesiredTuple = CISTPL_MANFID;
+ tuple.Attributes = TUPLE_RETURN_COMMON;
+ if ((pcmcia_get_first_tuple(link, &tuple) == 0) &&
+ (pcmcia_get_tuple_data(link, &tuple) == 0)) {
+ manfid = le16_to_cpu(buf[0]);
+ prodid = le16_to_cpu(buf[1]);
+ }
+ //printk("manfid = 0x%04x, 0x%04x\n",manfid,prodid);
+
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ tuple.Attributes = 0;
+ ret = pcmcia_get_first_tuple(link, &tuple);
+ ret = pcmcia_get_tuple_data(link, &tuple);
+ ret = pcmcia_parse_tuple(&tuple, &parse);
+
+#if 0
+ printk(" index: 0x%x\n", parse.cftable_entry.index);
+ printk(" flags: 0x%x\n", parse.cftable_entry.flags);
+ printk(" io flags: 0x%x\n", parse.cftable_entry.io.flags);
+ printk(" io nwin: 0x%x\n", parse.cftable_entry.io.nwin);
+ printk(" io base: 0x%x\n", parse.cftable_entry.io.win[0].base);
+ printk(" io len: 0x%x\n", parse.cftable_entry.io.win[0].len);
+ printk(" irq1: 0x%x\n", parse.cftable_entry.irq.IRQInfo1);
+ printk(" irq2: 0x%x\n", parse.cftable_entry.irq.IRQInfo2);
+ printk(" mem flags: 0x%x\n", parse.cftable_entry.mem.flags);
+ printk(" mem nwin: 0x%x\n", parse.cftable_entry.mem.nwin);
+ printk(" subtuples: 0x%x\n", parse.cftable_entry.subtuples);
+#endif
+
+#if 0
+ link->io.NumPorts1 = 0x20;
+ link->io.IOAddrLines = 5;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+#endif
+ link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
+ link->io.IOAddrLines =
+ parse.cftable_entry.io.flags & CISTPL_IO_LINES_MASK;
+ link->io.NumPorts2 = 0;
+
+ {
+ int base;
+ for (base = 0x000; base < 0x400; base += 0x20) {
+ link->io.BasePort1 = base;
+ ret = pcmcia_request_io(link, &link->io);
+ //printk("RequestIO 0x%02x\n",ret);
+ if (!ret)
+ break;
+ }
+ }
+
+ link->irq.IRQInfo1 = parse.cftable_entry.irq.IRQInfo1;
+ link->irq.IRQInfo2 = parse.cftable_entry.irq.IRQInfo2;
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret) {
+ printk("pcmcia_request_irq() returned error: %i\n", ret);
+ }
+ //printk("RequestIRQ 0x%02x\n",ret);
+
+ link->conf.ConfigIndex = 1;
+
+ ret = pcmcia_request_configuration(link, &link->conf);
+ //printk("RequestConfiguration %d\n",ret);
+
+ link->dev_node = &dev_node;
+}
+
+static int mio_cs_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct pcmcia_device *link;
+ unsigned int irq;
+ int ret;
+
+ DPRINTK("mio_cs_attach(dev=%p,it=%p)\n", dev, it);
+
+ link = cur_dev; /* XXX hack */
+ if (!link)
+ return -EIO;
+
+ dev->driver = &driver_ni_mio_cs;
+ dev->iobase = link->io.BasePort1;
+
+ irq = link->irq.AssignedIRQ;
+
+ printk("comedi%d: %s: DAQCard: io 0x%04lx, irq %u, ",
+ dev->minor, dev->driver->driver_name, dev->iobase, irq);
+
+#if 0
+ {
+ int i;
+
+ printk(" board fingerprint:");
+ for (i = 0; i < 32; i += 2) {
+ printk(" %04x %02x", inw(dev->iobase + i),
+ inb(dev->iobase + i + 1));
+ }
+ printk("\n");
+ printk(" board fingerprint (windowed):");
+ for (i = 0; i < 10; i++) {
+ printk(" 0x%04x", win_in(i));
+ }
+ printk("\n");
+ }
+#endif
+
+ dev->board_ptr = ni_boards + ni_getboardtype(dev, link);
+
+ printk(" %s", boardtype.name);
+ dev->board_name = boardtype.name;
+
+ if ((ret = comedi_request_irq(irq, ni_E_interrupt, NI_E_IRQ_FLAGS,
+ "ni_mio_cs", dev)) < 0) {
+ printk(" irq not available\n");
+ return -EINVAL;
+ }
+ dev->irq = irq;
+
+ /* allocate private area */
+ if ((ret = ni_alloc_private(dev)) < 0)
+ return ret;
+ devpriv->stc_writew = &mio_cs_win_out;
+ devpriv->stc_readw = &mio_cs_win_in;
+ devpriv->stc_writel = &win_out2;
+ devpriv->stc_readl = &win_in2;
+
+ if ((ret = ni_E_init(dev, it)) < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static int get_prodid(struct comedi_device * dev, struct pcmcia_device *link)
+{
+ tuple_t tuple;
+ u_short buf[128];
+ int prodid = 0;
+
+ tuple.TupleData = (cisdata_t *) buf;
+ tuple.TupleOffset = 0;
+ tuple.TupleDataMax = 255;
+ tuple.DesiredTuple = CISTPL_MANFID;
+ tuple.Attributes = TUPLE_RETURN_COMMON;
+ if ((pcmcia_get_first_tuple(link, &tuple) == 0) &&
+ (pcmcia_get_tuple_data(link, &tuple) == 0)) {
+ prodid = le16_to_cpu(buf[1]);
+ }
+
+ return prodid;
+}
+
+static int ni_getboardtype(struct comedi_device * dev, struct pcmcia_device *link)
+{
+ int id;
+ int i;
+
+ id = get_prodid(dev, link);
+
+ for (i = 0; i < n_ni_boards; i++) {
+ if (ni_boards[i].device_id == id) {
+ return i;
+ }
+ }
+
+ printk("unknown board 0x%04x -- pretend it is a ", id);
+
+ return 0;
+}
+
+#ifdef MODULE
+
+MODULE_LICENSE("GPL");
+
+static struct pcmcia_device_id ni_mio_cs_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x010d), /* DAQCard-ai-16xe-50 */
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x010c), /* DAQCard-ai-16e-4 */
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x02c4), /* DAQCard-6062E */
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x075e), /* DAQCard-6024E */
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0245), /* DAQCard-6036E */
+ PCMCIA_DEVICE_NULL
+};
+
+MODULE_DEVICE_TABLE(pcmcia, ni_mio_cs_ids);
+
+struct pcmcia_driver ni_mio_cs_driver = {
+ .probe = &cs_attach,
+ .remove = &cs_detach,
+ .suspend = &mio_cs_suspend,
+ .resume = &mio_cs_resume,
+ .id_table = ni_mio_cs_ids,
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = dev_info,
+ },
+};
+
+int init_module(void)
+{
+ pcmcia_register_driver(&ni_mio_cs_driver);
+ comedi_driver_register(&driver_ni_mio_cs);
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ pcmcia_unregister_driver(&ni_mio_cs_driver);
+#if 0
+ while (cur_dev != NULL)
+ cs_detach(cur_dev->handle);
+#endif
+ comedi_driver_unregister(&driver_ni_mio_cs);
+}
+#endif
diff --git a/drivers/staging/comedi/drivers/ni_pcidio.c b/drivers/staging/comedi/drivers/ni_pcidio.c
new file mode 100644
index 000000000000..87def2cc9a29
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_pcidio.c
@@ -0,0 +1,1302 @@
+/*
+ comedi/drivers/ni_pcidio.c
+ driver for National Instruments PCI-DIO-96/PCI-6508
+ National Instruments PCI-DIO-32HS
+ National Instruments PCI-6503
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999,2002 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: ni_pcidio
+Description: National Instruments PCI-DIO32HS, PCI-DIO96, PCI-6533, PCI-6503
+Author: ds
+Status: works
+Devices: [National Instruments] PCI-DIO-32HS (ni_pcidio), PXI-6533,
+ PCI-DIO-96, PCI-DIO-96B, PXI-6508, PCI-6503, PCI-6503B, PCI-6503X,
+ PXI-6503, PCI-6533, PCI-6534
+Updated: Sun, 21 Apr 2002 21:03:38 -0700
+
+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
+is 0=A0, 1=A1, 2=A2, ... 8=B0, 16=C0, 24=D0. The driver only
+supports simple digital I/O; no handshaking is supported.
+
+DMA mostly works for the PCI-DIO32HS, but only in timed input mode.
+
+This driver could be easily modified to support AT-MIO32HS and
+AT-MIO96.
+
+The PCI-6534 requires a firmware upload after power-up to work, the
+firmware data and instructions for loading it with comedi_config
+it are contained in the
+comedi_nonfree_firmware tarball available from http://www.comedi.org
+*/
+
+/*
+ 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.
+
+ Manuals (available from ftp://ftp.natinst.com/support/manuals)
+
+ 320938c.pdf PCI-DIO-96/PXI-6508/PCI-6503 User Manual
+ 321464b.pdf AT/PCI-DIO-32HS User Manual
+ 341329A.pdf PCI-6533 Register-Level Programmer Manual
+ 341330A.pdf DAQ-DIO Technical Reference Manual
+
+ */
+
+#define USE_DMA
+//#define DEBUG 1
+//#define DEBUG_FLAGS
+
+#include "../comedidev.h"
+
+#include "mite.h"
+#include "8255.h"
+
+#undef DPRINTK
+#ifdef DEBUG
+#define DPRINTK(format, args...) printk(format, ## args)
+#else
+#define DPRINTK(format, args...)
+#endif
+
+#define PCI_VENDOR_ID_NATINST 0x1093
+
+#define PCI_DIO_SIZE 4096
+#define PCI_MITE_SIZE 4096
+
+/* defines for the PCI-DIO-96 */
+
+#define NIDIO_8255_BASE(x) ((x)*4)
+#define NIDIO_A 0
+#define NIDIO_B 4
+#define NIDIO_C 8
+#define NIDIO_D 12
+
+/* defines for the PCI-DIO-32HS */
+
+#define Window_Address 4 /* W */
+#define Interrupt_And_Window_Status 4 /* R */
+#define IntStatus1 (1<<0)
+#define IntStatus2 (1<<1)
+#define WindowAddressStatus_mask 0x7c
+
+#define Master_DMA_And_Interrupt_Control 5 /* W */
+#define InterruptLine(x) ((x)&3)
+#define OpenInt (1<<2)
+#define Group_Status 5 /* R */
+#define DataLeft (1<<0)
+#define Req (1<<2)
+#define StopTrig (1<<3)
+
+#define Group_1_Flags 6 /* R */
+#define Group_2_Flags 7 /* R */
+#define TransferReady (1<<0)
+#define CountExpired (1<<1)
+#define Waited (1<<5)
+#define PrimaryTC (1<<6)
+#define SecondaryTC (1<<7)
+ //#define SerialRose
+ //#define ReqRose
+ //#define Paused
+
+#define Group_1_First_Clear 6 /* W */
+#define Group_2_First_Clear 7 /* W */
+#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_1_FIFO 8 /* W */
+#define Group_2_FIFO 12 /* W */
+
+#define Transfer_Count 20
+#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 Master_Clock_Routing 45
+#define RTSIClocking(x) (((x)&3)<<4)
+
+#define Group_1_Second_Clear 46 /* W */
+#define Group_2_Second_Clear 47 /* W */
+#define ClearExpired (1<<0)
+
+#define Port_Pattern(x) (48+(x))
+
+#define Data_Path 64
+#define FIFOEnableA (1<<0)
+#define FIFOEnableB (1<<1)
+#define FIFOEnableC (1<<2)
+#define FIFOEnableD (1<<3)
+#define Funneling(x) (((x)&3)<<4)
+#define GroupDirection (1<<7)
+
+#define Protocol_Register_1 65
+#define OpMode Protocol_Register_1
+#define RunMode(x) ((x)&7)
+#define Numbered (1<<3)
+
+#define Protocol_Register_2 66
+#define ClockReg Protocol_Register_2
+#define ClockLine(x) (((x)&3)<<5)
+#define InvertStopTrig (1<<7)
+#define DataLatching(x) (((x)&3)<<5)
+
+#define Protocol_Register_3 67
+#define Sequence Protocol_Register_3
+
+#define Protocol_Register_14 68 /* 16 bit */
+#define ClockSpeed Protocol_Register_14
+
+#define Protocol_Register_4 70
+#define ReqReg Protocol_Register_4
+#define ReqConditioning(x) (((x)&7)<<3)
+
+#define Protocol_Register_5 71
+#define BlockMode Protocol_Register_5
+
+#define FIFO_Control 72
+#define ReadyLevel(x) ((x)&7)
+
+#define Protocol_Register_6 73
+#define LinePolarities Protocol_Register_6
+#define InvertAck (1<<0)
+#define InvertReq (1<<1)
+#define InvertClock (1<<2)
+#define InvertSerial (1<<3)
+#define OpenAck (1<<4)
+#define OpenClock (1<<5)
+
+#define Protocol_Register_7 74
+#define AckSer Protocol_Register_7
+#define AckLine(x) (((x)&3)<<2)
+#define ExchangePins (1<<7)
+
+#define Interrupt_Control 75
+ /* bits same as flags */
+
+#define DMA_Line_Control_Group1 76
+#define DMA_Line_Control_Group2 108
+// channel zero is none
+static inline unsigned primary_DMAChannel_bits(unsigned channel)
+{
+ return channel & 0x3;
+}
+static inline unsigned secondary_DMAChannel_bits(unsigned channel)
+{
+ return (channel << 2) & 0xc;
+}
+
+#define Transfer_Size_Control 77
+#define TransferWidth(x) ((x)&3)
+#define TransferLength(x) (((x)&3)<<3)
+#define RequireRLevel (1<<5)
+
+#define Protocol_Register_15 79
+#define DAQOptions Protocol_Register_15
+#define StartSource(x) ((x)&0x3)
+#define InvertStart (1<<2)
+#define StopSource(x) (((x)&0x3)<<3)
+#define ReqStart (1<<6)
+#define PreStart (1<<7)
+
+#define Pattern_Detection 81
+#define DetectionMethod (1<<0)
+#define InvertMatch (1<<1)
+#define IE_Pattern_Detection (1<<2)
+
+#define Protocol_Register_9 82
+#define ReqDelay Protocol_Register_9
+
+#define Protocol_Register_10 83
+#define ReqNotDelay Protocol_Register_10
+
+#define Protocol_Register_11 84
+#define AckDelay Protocol_Register_11
+
+#define Protocol_Register_12 85
+#define AckNotDelay Protocol_Register_12
+
+#define Protocol_Register_13 86
+#define Data1Delay Protocol_Register_13
+
+#define Protocol_Register_8 88 /* 32 bit */
+#define StartDelay Protocol_Register_8
+
+enum pci_6534_firmware_registers { /* 16 bit */
+ Firmware_Control_Register = 0x100,
+ Firmware_Status_Register = 0x104,
+ Firmware_Data_Register = 0x108,
+ Firmware_Mask_Register = 0x10c,
+ Firmware_Debug_Register = 0x110,
+};
+/* main fpga registers (32 bit)*/
+enum pci_6534_fpga_registers {
+ FPGA_Control1_Register = 0x200,
+ FPGA_Control2_Register = 0x204,
+ FPGA_Irq_Mask_Register = 0x208,
+ FPGA_Status_Register = 0x20c,
+ FPGA_Signature_Register = 0x210,
+ FPGA_SCALS_Counter_Register = 0x280, /*write-clear */
+ FPGA_SCAMS_Counter_Register = 0x284, /*write-clear */
+ FPGA_SCBLS_Counter_Register = 0x288, /*write-clear */
+ FPGA_SCBMS_Counter_Register = 0x28c, /*write-clear */
+ FPGA_Temp_Control_Register = 0x2a0,
+ FPGA_DAR_Register = 0x2a8,
+ FPGA_ELC_Read_Register = 0x2b8,
+ FPGA_ELC_Write_Register = 0x2bc,
+};
+enum FPGA_Control_Bits {
+ FPGA_Enable_Bit = 0x8000,
+};
+
+#define TIMER_BASE 50 /* nanoseconds */
+
+#ifdef USE_DMA
+#define IntEn (CountExpired|Waited|PrimaryTC|SecondaryTC)
+#else
+#define IntEn (TransferReady|CountExpired|Waited|PrimaryTC|SecondaryTC)
+#endif
+
+static int nidio_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int nidio_detach(struct comedi_device * dev);
+static int ni_pcidio_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+
+static struct comedi_driver driver_pcidio = {
+ driver_name:"ni_pcidio",
+ module:THIS_MODULE,
+ attach:nidio_attach,
+ detach:nidio_detach,
+};
+
+struct nidio_board {
+
+ int dev_id;
+ const char *name;
+ int n_8255;
+ unsigned int is_diodaq:1;
+ unsigned int uses_firmware:1;
+};
+
+static const struct nidio_board nidio_boards[] = {
+ {
+ dev_id: 0x1150,
+ name: "pci-dio-32hs",
+ n_8255: 0,
+ is_diodaq:1,
+ },
+ {
+ dev_id: 0x1320,
+ name: "pxi-6533",
+ n_8255: 0,
+ is_diodaq:1,
+ },
+ {
+ dev_id: 0x12b0,
+ name: "pci-6534",
+ n_8255: 0,
+ is_diodaq:1,
+ uses_firmware:1,
+ },
+ {
+ dev_id: 0x0160,
+ name: "pci-dio-96",
+ n_8255: 4,
+ is_diodaq:0,
+ },
+ {
+ dev_id: 0x1630,
+ name: "pci-dio-96b",
+ n_8255: 4,
+ is_diodaq:0,
+ },
+ {
+ dev_id: 0x13c0,
+ name: "pxi-6508",
+ n_8255: 4,
+ is_diodaq:0,
+ },
+ {
+ dev_id: 0x0400,
+ name: "pci-6503",
+ n_8255: 1,
+ is_diodaq:0,
+ },
+ {
+ dev_id: 0x1250,
+ name: "pci-6503b",
+ n_8255: 1,
+ is_diodaq:0,
+ },
+ {
+ dev_id: 0x17d0,
+ name: "pci-6503x",
+ n_8255: 1,
+ is_diodaq:0,
+ },
+ {
+ dev_id: 0x1800,
+ name: "pxi-6503",
+ n_8255: 1,
+ is_diodaq:0,
+ },
+};
+
+#define n_nidio_boards (sizeof(nidio_boards)/sizeof(nidio_boards[0]))
+#define this_board ((const struct nidio_board *)dev->board_ptr)
+
+static DEFINE_PCI_DEVICE_TABLE(ni_pcidio_pci_table) = {
+ {PCI_VENDOR_ID_NATINST, 0x1150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x12b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x0160, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1630, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x13c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x0400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1250, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x17d0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, ni_pcidio_pci_table);
+
+struct nidio96_private {
+ struct mite_struct *mite;
+ int boardtype;
+ int dio;
+ unsigned short OpModeBits;
+ struct mite_channel *di_mite_chan;
+ struct mite_dma_descriptor_ring *di_mite_ring;
+ spinlock_t mite_channel_lock;
+};
+#define devpriv ((struct nidio96_private *)dev->private)
+
+static int ni_pcidio_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int ni_pcidio_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int ni_pcidio_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trignum);
+static int nidio_find_device(struct comedi_device * dev, int bus, int slot);
+static int ni_pcidio_ns_to_timer(int *nanosec, int round_mode);
+static int setup_mite_dma(struct comedi_device * dev, struct comedi_subdevice * s);
+
+#ifdef DEBUG_FLAGS
+static void ni_pcidio_print_flags(unsigned int flags);
+static void ni_pcidio_print_status(unsigned int status);
+#else
+#define ni_pcidio_print_flags(x)
+#define ni_pcidio_print_status(x)
+#endif
+
+static int ni_pcidio_request_di_mite_channel(struct comedi_device * dev)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ BUG_ON(devpriv->di_mite_chan);
+ devpriv->di_mite_chan =
+ mite_request_channel_in_range(devpriv->mite,
+ devpriv->di_mite_ring, 1, 2);
+ if (devpriv->di_mite_chan == NULL) {
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock,
+ flags);
+ comedi_error(dev, "failed to reserve mite dma channel.");
+ return -EBUSY;
+ }
+ writeb(primary_DMAChannel_bits(devpriv->di_mite_chan->channel) |
+ secondary_DMAChannel_bits(devpriv->di_mite_chan->channel),
+ devpriv->mite->daq_io_addr + DMA_Line_Control_Group1);
+ mmiowb();
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ return 0;
+}
+
+static void ni_pcidio_release_di_mite_channel(struct comedi_device * dev)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->di_mite_chan) {
+ mite_dma_disarm(devpriv->di_mite_chan);
+ mite_dma_reset(devpriv->di_mite_chan);
+ mite_release_channel(devpriv->di_mite_chan);
+ devpriv->di_mite_chan = NULL;
+ writeb(primary_DMAChannel_bits(0) |
+ secondary_DMAChannel_bits(0),
+ devpriv->mite->daq_io_addr + DMA_Line_Control_Group1);
+ mmiowb();
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+}
+
+static int nidio96_8255_cb(int dir, int port, int data, unsigned long iobase)
+{
+ if (dir) {
+ writeb(data, (void *)(iobase + port));
+ return 0;
+ } else {
+ return readb((void *)(iobase + port));
+ }
+}
+
+void ni_pcidio_event(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ if (s->async->
+ events & (COMEDI_CB_EOA | COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW))
+ {
+ ni_pcidio_cancel(dev, s);
+ }
+ comedi_event(dev, s);
+}
+
+static irqreturn_t nidio_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices;
+ struct comedi_async *async = s->async;
+ struct mite_struct *mite = devpriv->mite;
+
+ //int i, j;
+ long int AuxData = 0;
+ short data1 = 0;
+ short data2 = 0;
+ int flags;
+ int status;
+ int work = 0;
+ unsigned int m_status = 0;
+ unsigned long irq_flags;
+
+ //interrupcions parasites
+ if (dev->attached == 0) {
+ // assume it's from another card
+ return IRQ_NONE;
+ }
+
+ status = readb(devpriv->mite->daq_io_addr +
+ Interrupt_And_Window_Status);
+ flags = readb(devpriv->mite->daq_io_addr + Group_1_Flags);
+
+ DPRINTK("ni_pcidio_interrupt: status=0x%02x,flags=0x%02x\n",
+ status, flags);
+ ni_pcidio_print_flags(flags);
+ ni_pcidio_print_status(status);
+
+ //printk("buf[0]=%08x\n",*(unsigned int *)async->prealloc_buf);
+ //printk("buf[4096]=%08x\n",*(unsigned int *)(async->prealloc_buf+4096));
+
+ comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, irq_flags);
+ if (devpriv->di_mite_chan)
+ m_status = mite_get_status(devpriv->di_mite_chan);
+#ifdef MITE_DEBUG
+ mite_print_chsr(m_status);
+#endif
+ //printk("mite_bytes_transferred: %d\n",mite_bytes_transferred(mite,DI_DMA_CHAN));
+ //mite_dump_regs(mite);
+ if (m_status & CHSR_INT) {
+ if (m_status & CHSR_LINKC) {
+ writel(CHOR_CLRLC,
+ mite->mite_io_addr +
+ MITE_CHOR(devpriv->di_mite_chan->channel));
+ mite_sync_input_dma(devpriv->di_mite_chan, s->async);
+ /* XXX need to byteswap */
+ }
+ if (m_status & ~(CHSR_INT | CHSR_LINKC | CHSR_DONE | CHSR_DRDY |
+ CHSR_DRQ1 | CHSR_MRDY)) {
+ DPRINTK("unknown mite interrupt, disabling IRQ\n");
+ async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ disable_irq(dev->irq);
+ }
+ }
+ comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, irq_flags);
+
+ while (status & DataLeft) {
+ work++;
+ if (work > 20) {
+ DPRINTK("too much work in interrupt\n");
+ writeb(0x00,
+ devpriv->mite->daq_io_addr +
+ Master_DMA_And_Interrupt_Control);
+ break;
+ }
+
+ flags &= IntEn;
+
+ if (flags & TransferReady) {
+ //DPRINTK("TransferReady\n");
+ while (flags & TransferReady) {
+ work++;
+ if (work > 100) {
+ DPRINTK("too much work in interrupt\n");
+ writeb(0x00,
+ devpriv->mite->daq_io_addr +
+ Master_DMA_And_Interrupt_Control);
+ goto out;
+ }
+ AuxData =
+ readl(devpriv->mite->daq_io_addr +
+ Group_1_FIFO);
+ data1 = AuxData & 0xffff;
+ data2 = (AuxData & 0xffff0000) >> 16;
+ comedi_buf_put(async, data1);
+ comedi_buf_put(async, data2);
+ //DPRINTK("read:%d, %d\n",data1,data2);
+ flags = readb(devpriv->mite->daq_io_addr +
+ Group_1_Flags);
+ }
+ //DPRINTK("buf_int_count: %d\n",async->buf_int_count);
+ //DPRINTK("1) IntEn=%d,flags=%d,status=%d\n",IntEn,flags,status);
+ //ni_pcidio_print_flags(flags);
+ //ni_pcidio_print_status(status);
+ async->events |= COMEDI_CB_BLOCK;
+ }
+
+ if (flags & CountExpired) {
+ DPRINTK("CountExpired\n");
+ writeb(ClearExpired,
+ devpriv->mite->daq_io_addr +
+ Group_1_Second_Clear);
+ async->events |= COMEDI_CB_EOA;
+
+ writeb(0x00, devpriv->mite->daq_io_addr + OpMode);
+ break;
+ } else if (flags & Waited) {
+ DPRINTK("Waited\n");
+ writeb(ClearWaited,
+ devpriv->mite->daq_io_addr +
+ Group_1_First_Clear);
+ async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ break;
+ } else if (flags & PrimaryTC) {
+ DPRINTK("PrimaryTC\n");
+ writeb(ClearPrimaryTC,
+ devpriv->mite->daq_io_addr +
+ Group_1_First_Clear);
+ async->events |= COMEDI_CB_EOA;
+ } else if (flags & SecondaryTC) {
+ DPRINTK("SecondaryTC\n");
+ writeb(ClearSecondaryTC,
+ devpriv->mite->daq_io_addr +
+ Group_1_First_Clear);
+ async->events |= COMEDI_CB_EOA;
+ }
+#if 0
+ else {
+ printk("ni_pcidio: unknown interrupt\n");
+ async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
+ writeb(0x00,
+ devpriv->mite->daq_io_addr +
+ Master_DMA_And_Interrupt_Control);
+ }
+#endif
+ flags = readb(devpriv->mite->daq_io_addr + Group_1_Flags);
+ status = readb(devpriv->mite->daq_io_addr +
+ Interrupt_And_Window_Status);
+ //DPRINTK("loop end: IntEn=0x%02x,flags=0x%02x,status=0x%02x\n",
+ // IntEn,flags,status);
+ //ni_pcidio_print_flags(flags);
+ //ni_pcidio_print_status(status);
+ }
+
+ out:
+ ni_pcidio_event(dev, s);
+#if 0
+ if (!tag) {
+ writeb(0x03,
+ devpriv->mite->daq_io_addr +
+ Master_DMA_And_Interrupt_Control);
+ }
+#endif
+ return IRQ_HANDLED;
+}
+
+#ifdef DEBUG_FLAGS
+static const char *const flags_strings[] = {
+ "TransferReady", "CountExpired", "2", "3",
+ "4", "Waited", "PrimaryTC", "SecondaryTC",
+};
+static void ni_pcidio_print_flags(unsigned int flags)
+{
+ int i;
+
+ printk("group_1_flags:");
+ for (i = 7; i >= 0; i--) {
+ if (flags & (1 << i)) {
+ printk(" %s", flags_strings[i]);
+ }
+ }
+ printk("\n");
+}
+static char *status_strings[] = {
+ "DataLeft1", "Reserved1", "Req1", "StopTrig1",
+ "DataLeft2", "Reserved2", "Req2", "StopTrig2",
+};
+static void ni_pcidio_print_status(unsigned int flags)
+{
+ int i;
+
+ printk("group_status:");
+ for (i = 7; i >= 0; i--) {
+ if (flags & (1 << i)) {
+ printk(" %s", status_strings[i]);
+ }
+ }
+ printk("\n");
+}
+#endif
+
+#ifdef unused
+static void debug_int(struct comedi_device * dev)
+{
+ int a, b;
+ static int n_int = 0;
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ a = readb(devpriv->mite->daq_io_addr + Group_Status);
+ b = readb(devpriv->mite->daq_io_addr + Group_1_Flags);
+
+ if (n_int < 10) {
+ DPRINTK("status 0x%02x flags 0x%02x time %06d\n", a, b,
+ (int)tv.tv_usec);
+ }
+
+ while (b & 1) {
+ writew(0xff, devpriv->mite->daq_io_addr + Group_1_FIFO);
+ b = readb(devpriv->mite->daq_io_addr + Group_1_Flags);
+ }
+
+ b = readb(devpriv->mite->daq_io_addr + Group_1_Flags);
+
+ if (n_int < 10) {
+ DPRINTK("new status 0x%02x\n", b);
+ n_int++;
+ }
+}
+#endif
+
+static int ni_pcidio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 1)
+ return -EINVAL;
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= 1 << CR_CHAN(insn->chanspec);
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~(1 << CR_CHAN(insn->chanspec));
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (s->io_bits & (1 << CR_CHAN(insn->
+ chanspec))) ? COMEDI_OUTPUT :
+ COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(s->io_bits, devpriv->mite->daq_io_addr + Port_Pin_Directions(0));
+
+ return 1;
+}
+
+static int ni_pcidio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ writel(s->state, devpriv->mite->daq_io_addr + Port_IO(0));
+ }
+ data[1] = readl(devpriv->mite->daq_io_addr + Port_IO(0));
+
+ return 2;
+}
+
+static int ni_pcidio_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW | TRIG_INT;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ /* note that mutual compatiblity is not an issue here */
+ if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_INT)
+ err++;
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_EXT)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ /* same for both TRIG_INT and TRIG_NOW */
+ cmd->start_arg = 0;
+ err++;
+ }
+#define MAX_SPEED (TIMER_BASE) /* in nanoseconds */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->scan_begin_arg < MAX_SPEED) {
+ cmd->scan_begin_arg = MAX_SPEED;
+ err++;
+ }
+ /* no minumum speed */
+ } else {
+ /* TRIG_EXT */
+ /* should be level/edge, hi/lo specification here */
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ }
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ /* no limit */
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ ni_pcidio_ns_to_timer(&cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int ni_pcidio_ns_to_timer(int *nanosec, int round_mode)
+{
+ int divider, base;
+
+ base = TIMER_BASE;
+
+ switch (round_mode) {
+ case TRIG_ROUND_NEAREST:
+ default:
+ divider = (*nanosec + base / 2) / base;
+ break;
+ case TRIG_ROUND_DOWN:
+ divider = (*nanosec) / base;
+ break;
+ case TRIG_ROUND_UP:
+ divider = (*nanosec + base - 1) / base;
+ break;
+ }
+
+ *nanosec = base * divider;
+ return divider;
+}
+
+static int ni_pcidio_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ /* XXX configure ports for input */
+ writel(0x0000, devpriv->mite->daq_io_addr + Port_Pin_Directions(0));
+
+ if (1) {
+ /* enable fifos A B C D */
+ writeb(0x0f, devpriv->mite->daq_io_addr + Data_Path);
+
+ /* set transfer width a 32 bits */
+ writeb(TransferWidth(0) | TransferLength(0),
+ devpriv->mite->daq_io_addr + Transfer_Size_Control);
+ } else {
+ writeb(0x03, devpriv->mite->daq_io_addr + Data_Path);
+ writeb(TransferWidth(3) | TransferLength(0),
+ devpriv->mite->daq_io_addr + Transfer_Size_Control);
+ }
+
+ /* protocol configuration */
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* page 4-5, "input with internal REQs" */
+ writeb(0, devpriv->mite->daq_io_addr + OpMode);
+ writeb(0x00, devpriv->mite->daq_io_addr + ClockReg);
+ writeb(1, devpriv->mite->daq_io_addr + Sequence);
+ writeb(0x04, devpriv->mite->daq_io_addr + ReqReg);
+ writeb(4, devpriv->mite->daq_io_addr + BlockMode);
+ writeb(3, devpriv->mite->daq_io_addr + LinePolarities);
+ writeb(0xc0, devpriv->mite->daq_io_addr + AckSer);
+ writel(ni_pcidio_ns_to_timer(&cmd->scan_begin_arg,
+ TRIG_ROUND_NEAREST),
+ devpriv->mite->daq_io_addr + StartDelay);
+ writeb(1, devpriv->mite->daq_io_addr + ReqDelay);
+ writeb(1, devpriv->mite->daq_io_addr + ReqNotDelay);
+ writeb(1, devpriv->mite->daq_io_addr + AckDelay);
+ writeb(0x0b, devpriv->mite->daq_io_addr + AckNotDelay);
+ writeb(0x01, devpriv->mite->daq_io_addr + Data1Delay);
+ /* manual, page 4-5: ClockSpeed comment is incorrectly listed
+ * on DAQOptions */
+ writew(0, devpriv->mite->daq_io_addr + ClockSpeed);
+ writeb(0, devpriv->mite->daq_io_addr + DAQOptions);
+ } else {
+ /* TRIG_EXT */
+ /* page 4-5, "input with external REQs" */
+ writeb(0, devpriv->mite->daq_io_addr + OpMode);
+ writeb(0x00, devpriv->mite->daq_io_addr + ClockReg);
+ writeb(0, devpriv->mite->daq_io_addr + Sequence);
+ writeb(0x00, devpriv->mite->daq_io_addr + ReqReg);
+ writeb(4, devpriv->mite->daq_io_addr + BlockMode);
+ writeb(0, devpriv->mite->daq_io_addr + LinePolarities);
+ writeb(0x00, devpriv->mite->daq_io_addr + AckSer);
+ writel(1, devpriv->mite->daq_io_addr + StartDelay);
+ writeb(1, devpriv->mite->daq_io_addr + ReqDelay);
+ writeb(1, devpriv->mite->daq_io_addr + ReqNotDelay);
+ writeb(1, devpriv->mite->daq_io_addr + AckDelay);
+ writeb(0x0C, devpriv->mite->daq_io_addr + AckNotDelay);
+ writeb(0x10, devpriv->mite->daq_io_addr + Data1Delay);
+ writew(0, devpriv->mite->daq_io_addr + ClockSpeed);
+ writeb(0x60, devpriv->mite->daq_io_addr + DAQOptions);
+ }
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ writel(cmd->stop_arg,
+ devpriv->mite->daq_io_addr + Transfer_Count);
+ } else {
+ /* XXX */
+ }
+
+#ifdef USE_DMA
+ writeb(ClearPrimaryTC | ClearSecondaryTC,
+ devpriv->mite->daq_io_addr + Group_1_First_Clear);
+
+ {
+ int retval = setup_mite_dma(dev, s);
+ if (retval)
+ return retval;
+ }
+#else
+ writeb(0x00, devpriv->mite->daq_io_addr + DMA_Line_Control_Group1);
+#endif
+ writeb(0x00, devpriv->mite->daq_io_addr + DMA_Line_Control_Group2);
+
+ /* clear and enable interrupts */
+ writeb(0xff, devpriv->mite->daq_io_addr + Group_1_First_Clear);
+ //writeb(ClearExpired,devpriv->mite->daq_io_addr+Group_1_Second_Clear);
+
+ writeb(IntEn, devpriv->mite->daq_io_addr + Interrupt_Control);
+ writeb(0x03,
+ devpriv->mite->daq_io_addr + Master_DMA_And_Interrupt_Control);
+
+ if (cmd->stop_src == TRIG_NONE) {
+ devpriv->OpModeBits = DataLatching(0) | RunMode(7);
+ } else { // TRIG_TIMER
+ devpriv->OpModeBits = Numbered | RunMode(7);
+ }
+ if (cmd->start_src == TRIG_NOW) {
+ /* start */
+ writeb(devpriv->OpModeBits,
+ devpriv->mite->daq_io_addr + OpMode);
+ s->async->inttrig = NULL;
+ } else {
+ /* TRIG_INT */
+ s->async->inttrig = ni_pcidio_inttrig;
+ }
+
+ DPRINTK("ni_pcidio: command started\n");
+ return 0;
+}
+
+static int setup_mite_dma(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int retval;
+
+ retval = ni_pcidio_request_di_mite_channel(dev);
+ if (retval)
+ return retval;
+
+ devpriv->di_mite_chan->dir = COMEDI_INPUT;
+
+ mite_prep_dma(devpriv->di_mite_chan, 32, 32);
+
+ mite_dma_arm(devpriv->di_mite_chan);
+ return 0;
+}
+
+static int ni_pcidio_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trignum)
+{
+ if (trignum != 0)
+ return -EINVAL;
+
+ writeb(devpriv->OpModeBits, devpriv->mite->daq_io_addr + OpMode);
+ s->async->inttrig = NULL;
+
+ return 1;
+}
+
+static int ni_pcidio_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ writeb(0x00,
+ devpriv->mite->daq_io_addr + Master_DMA_And_Interrupt_Control);
+ ni_pcidio_release_di_mite_channel(dev);
+
+ return 0;
+}
+
+static int ni_pcidio_change(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned long new_size)
+{
+ int ret;
+
+ ret = mite_buf_change(devpriv->di_mite_ring, s->async);
+ if (ret < 0)
+ return ret;
+
+ memset(s->async->prealloc_buf, 0xaa, s->async->prealloc_bufsz);
+
+ return 0;
+}
+
+static int pci_6534_load_fpga(struct comedi_device * dev, int fpga_index, u8 * data,
+ int data_len)
+{
+ static const int timeout = 1000;
+ int i, j;
+ writew(0x80 | fpga_index,
+ devpriv->mite->daq_io_addr + Firmware_Control_Register);
+ writew(0xc0 | fpga_index,
+ devpriv->mite->daq_io_addr + Firmware_Control_Register);
+ for (i = 0;
+ (readw(devpriv->mite->daq_io_addr +
+ Firmware_Status_Register) & 0x2) == 0
+ && i < timeout; ++i) {
+ udelay(1);
+ }
+ if (i == timeout) {
+ printk("ni_pcidio: failed to load fpga %i, waiting for status 0x2\n", fpga_index);
+ return -EIO;
+ }
+ writew(0x80 | fpga_index,
+ devpriv->mite->daq_io_addr + Firmware_Control_Register);
+ for (i = 0;
+ readw(devpriv->mite->daq_io_addr + Firmware_Status_Register) !=
+ 0x3 && i < timeout; ++i) {
+ udelay(1);
+ }
+ if (i == timeout) {
+ printk("ni_pcidio: failed to load fpga %i, waiting for status 0x3\n", fpga_index);
+ return -EIO;
+ }
+ for (j = 0; j + 1 < data_len;) {
+ unsigned int value = data[j++];
+ value |= data[j++] << 8;
+ writew(value,
+ devpriv->mite->daq_io_addr + Firmware_Data_Register);
+ for (i = 0;
+ (readw(devpriv->mite->daq_io_addr +
+ Firmware_Status_Register) & 0x2) == 0
+ && i < timeout; ++i) {
+ udelay(1);
+ }
+ if (i == timeout) {
+ printk("ni_pcidio: failed to load word into fpga %i\n",
+ fpga_index);
+ return -EIO;
+ }
+ if (need_resched())
+ schedule();
+ }
+ writew(0x0, devpriv->mite->daq_io_addr + Firmware_Control_Register);
+ return 0;
+}
+
+static int pci_6534_reset_fpga(struct comedi_device * dev, int fpga_index)
+{
+ return pci_6534_load_fpga(dev, fpga_index, NULL, 0);
+}
+
+static int pci_6534_reset_fpgas(struct comedi_device * dev)
+{
+ int ret;
+ int i;
+ writew(0x0, devpriv->mite->daq_io_addr + Firmware_Control_Register);
+ for (i = 0; i < 3; ++i) {
+ ret = pci_6534_reset_fpga(dev, i);
+ if (ret < 0)
+ break;
+ }
+ writew(0x0, devpriv->mite->daq_io_addr + Firmware_Mask_Register);
+ return ret;
+}
+
+static void pci_6534_init_main_fpga(struct comedi_device * dev)
+{
+ writel(0, devpriv->mite->daq_io_addr + FPGA_Control1_Register);
+ writel(0, devpriv->mite->daq_io_addr + FPGA_Control2_Register);
+ writel(0, devpriv->mite->daq_io_addr + FPGA_SCALS_Counter_Register);
+ writel(0, devpriv->mite->daq_io_addr + FPGA_SCAMS_Counter_Register);
+ writel(0, devpriv->mite->daq_io_addr + FPGA_SCBLS_Counter_Register);
+ writel(0, devpriv->mite->daq_io_addr + FPGA_SCBMS_Counter_Register);
+}
+
+static int pci_6534_upload_firmware(struct comedi_device * dev, int options[])
+{
+ int ret;
+ void *main_fpga_data, *scarab_a_data, *scarab_b_data;
+ int main_fpga_data_len, scarab_a_data_len, scarab_b_data_len;
+
+ if (options[COMEDI_DEVCONF_AUX_DATA_LENGTH] == 0)
+ return 0;
+ ret = pci_6534_reset_fpgas(dev);
+ if (ret < 0)
+ return ret;
+ main_fpga_data = comedi_aux_data(options, 0);
+ main_fpga_data_len = options[COMEDI_DEVCONF_AUX_DATA0_LENGTH];
+ ret = pci_6534_load_fpga(dev, 2, main_fpga_data, main_fpga_data_len);
+ if (ret < 0)
+ return ret;
+ pci_6534_init_main_fpga(dev);
+ scarab_a_data = comedi_aux_data(options, 1);
+ scarab_a_data_len = options[COMEDI_DEVCONF_AUX_DATA1_LENGTH];
+ ret = pci_6534_load_fpga(dev, 0, scarab_a_data, scarab_a_data_len);
+ if (ret < 0)
+ return ret;
+ scarab_b_data = comedi_aux_data(options, 2);
+ scarab_b_data_len = options[COMEDI_DEVCONF_AUX_DATA2_LENGTH];
+ ret = pci_6534_load_fpga(dev, 1, scarab_b_data, scarab_b_data_len);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int nidio_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int i;
+ int ret;
+ int n_subdevices;
+ unsigned int irq;
+
+ printk("comedi%d: nidio:", dev->minor);
+
+ if ((ret = alloc_private(dev, sizeof(struct nidio96_private))) < 0)
+ return ret;
+ spin_lock_init(&devpriv->mite_channel_lock);
+
+ ret = nidio_find_device(dev, it->options[0], it->options[1]);
+ if (ret < 0)
+ return ret;
+
+ ret = mite_setup(devpriv->mite);
+ if (ret < 0) {
+ printk("error setting up mite\n");
+ return ret;
+ }
+ comedi_set_hw_dev(dev, &devpriv->mite->pcidev->dev);
+ devpriv->di_mite_ring = mite_alloc_ring(devpriv->mite);
+ if (devpriv->di_mite_ring == NULL)
+ return -ENOMEM;
+
+ dev->board_name = this_board->name;
+ irq = mite_irq(devpriv->mite);
+ printk(" %s", dev->board_name);
+ if (this_board->uses_firmware) {
+ ret = pci_6534_upload_firmware(dev, it->options);
+ if (ret < 0)
+ return ret;
+ }
+ if (!this_board->is_diodaq) {
+ n_subdevices = this_board->n_8255;
+ } else {
+ n_subdevices = 1;
+ }
+ if ((ret = alloc_subdevices(dev, n_subdevices)) < 0)
+ return ret;
+
+ if (!this_board->is_diodaq) {
+ for (i = 0; i < this_board->n_8255; i++) {
+ subdev_8255_init(dev, dev->subdevices + i,
+ nidio96_8255_cb,
+ (unsigned long)(devpriv->mite->daq_io_addr +
+ NIDIO_8255_BASE(i)));
+ }
+ } else {
+
+ printk(" rev=%d",
+ readb(devpriv->mite->daq_io_addr + Chip_Version));
+
+ s = dev->subdevices + 0;
+
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags =
+ SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL | SDF_PACKED |
+ SDF_CMD_READ;
+ s->n_chan = 32;
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+ s->insn_config = &ni_pcidio_insn_config;
+ s->insn_bits = &ni_pcidio_insn_bits;
+ s->do_cmd = &ni_pcidio_cmd;
+ s->do_cmdtest = &ni_pcidio_cmdtest;
+ s->cancel = &ni_pcidio_cancel;
+ s->len_chanlist = 32; /* XXX */
+ s->buf_change = &ni_pcidio_change;
+ s->async_dma_dir = DMA_BIDIRECTIONAL;
+
+ writel(0, devpriv->mite->daq_io_addr + Port_IO(0));
+ writel(0, devpriv->mite->daq_io_addr + Port_Pin_Directions(0));
+ writel(0, devpriv->mite->daq_io_addr + Port_Pin_Mask(0));
+
+ /* disable interrupts on board */
+ writeb(0x00,
+ devpriv->mite->daq_io_addr +
+ Master_DMA_And_Interrupt_Control);
+
+ ret = comedi_request_irq(irq, nidio_interrupt, IRQF_SHARED,
+ "ni_pcidio", dev);
+ if (ret < 0) {
+ printk(" irq not available");
+ }
+ dev->irq = irq;
+ }
+
+ printk("\n");
+
+ return 0;
+}
+
+static int nidio_detach(struct comedi_device * dev)
+{
+ int i;
+
+ if (this_board && !this_board->is_diodaq) {
+ for (i = 0; i < this_board->n_8255; i++) {
+ subdev_8255_cleanup(dev, dev->subdevices + i);
+ }
+ }
+
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+
+ if (devpriv) {
+ if (devpriv->di_mite_ring) {
+ mite_free_ring(devpriv->di_mite_ring);
+ devpriv->di_mite_ring = NULL;
+ }
+ if (devpriv->mite)
+ mite_unsetup(devpriv->mite);
+ }
+ return 0;
+}
+
+static int nidio_find_device(struct comedi_device * dev, int bus, int slot)
+{
+ struct mite_struct *mite;
+ int i;
+
+ for (mite = mite_devices; mite; mite = mite->next) {
+ if (mite->used)
+ continue;
+ if (bus || slot) {
+ if (bus != mite->pcidev->bus->number ||
+ slot != PCI_SLOT(mite->pcidev->devfn))
+ continue;
+ }
+ for (i = 0; i < n_nidio_boards; i++) {
+ if (mite_device_id(mite) == nidio_boards[i].dev_id) {
+ dev->board_ptr = nidio_boards + i;
+ devpriv->mite = mite;
+
+ return 0;
+ }
+ }
+ }
+ printk("no device found\n");
+ mite_list_devices();
+ return -EIO;
+}
+
+COMEDI_PCI_INITCLEANUP(driver_pcidio, ni_pcidio_pci_table);
diff --git a/drivers/staging/comedi/drivers/ni_pcimio.c b/drivers/staging/comedi/drivers/ni_pcimio.c
new file mode 100644
index 000000000000..3a2aba7c8dd3
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_pcimio.c
@@ -0,0 +1,1788 @@
+/*
+ comedi/drivers/ni_pcimio.c
+ Hardware driver for NI PCI-MIO E series cards
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+Driver: ni_pcimio
+Description: National Instruments PCI-MIO-E series and M series (all boards)
+Author: ds, John Hallen, Frank Mori Hess, Rolf Mueller, Herbert Peremans,
+ Herman Bruyninckx, Terry Barnaby
+Status: works
+Devices: [National Instruments] PCI-MIO-16XE-50 (ni_pcimio),
+ PCI-MIO-16XE-10, PXI-6030E, PCI-MIO-16E-1, PCI-MIO-16E-4, PCI-6014, PCI-6040E,
+ PXI-6040E, PCI-6030E, PCI-6031E, PCI-6032E, PCI-6033E, PCI-6071E, PCI-6023E,
+ PCI-6024E, PCI-6025E, PXI-6025E, PCI-6034E, PCI-6035E, PCI-6052E,
+ PCI-6110, PCI-6111, PCI-6220, PCI-6221, PCI-6224, PXI-6224, PCI-6225,
+ PCI-6229, PCI-6250, PCI-6251, PCIe-6251, PCI-6254, PCI-6259, PCIe-6259,
+ PCI-6280, PCI-6281, PXI-6281, PCI-6284, PCI-6289,
+ PCI-6711, PXI-6711, PCI-6713, PXI-6713,
+ PXI-6071E, PCI-6070E, PXI-6070E,
+ PXI-6052E, PCI-6036E, PCI-6731, PCI-6733, PXI-6733,
+ PCI-6143, PXI-6143
+Updated: Wed, 03 Dec 2008 10:51:47 +0000
+
+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 for
+the ni_atmio.o driver for additional information about these boards.
+
+Autocalibration is supported on many of the devices, using the
+comedi_calibrate (or comedi_soft_calibrate for m-series) utility.
+M-Series boards do analog input and analog output calibration entirely
+in software. The software calibration corrects
+the analog input for offset, gain and
+nonlinearity. The analog outputs are corrected for offset and gain.
+See the comedilib documentation on comedi_get_softcal_converter() for
+more information.
+
+By default, the driver uses DMA to transfer analog input data to
+memory. When DMA is enabled, not all triggering features are
+supported.
+
+Digital I/O may not work on 673x.
+
+Note that the PCI-6143 is a simultaineous sampling device with 8 convertors.
+With this board all of the convertors perform one simultaineous sample during
+a scan interval. The period for a scan is used for the convert time in a
+Comedi cmd. The convert trigger source is normally set to TRIG_NOW by default.
+
+The RTSI trigger bus is supported on these cards on
+subdevice 10. See the comedilib documentation for details.
+
+Information (number of channels, bits, etc.) for some devices may be
+incorrect. Please check this and submit a bug if there are problems
+for your device.
+
+SCXI is probably broken for m-series boards.
+
+Bugs:
+ - When DMA is enabled, COMEDI_EV_CONVERT does
+ not work correctly.
+
+*/
+/*
+ The PCI-MIO E series driver was originally written by
+ Tomasz Motylewski <...>, and ported to comedi by ds.
+
+ References:
+
+ 341079b.pdf PCI E Series Register-Level Programmer Manual
+ 340934b.pdf DAQ-STC reference manual
+
+ 322080b.pdf 6711/6713/6715 User Manual
+
+ 320945c.pdf PCI E Series User Manual
+ 322138a.pdf PCI-6052E and DAQPad-6052E User Manual
+
+ 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 comedi_udelay(). Timing specs: (clock)
+ AD8522 30ns
+ DAC8043 120ns
+ DAC8800 60ns
+ MB88341 ?
+
+*/
+
+#include "../comedidev.h"
+
+#include <asm/byteorder.h>
+#include <linux/delay.h>
+
+#include "ni_stc.h"
+#include "mite.h"
+
+//#define PCI_DEBUG
+
+#define PCIDMA
+
+#define PCIMIO 1
+#undef ATMIO
+
+#define MAX_N_CALDACS (16+16+2)
+
+#define DRV_NAME "ni_pcimio"
+
+/* The following two tables must be in the same order */
+static DEFINE_PCI_DEVICE_TABLE(ni_pci_table) = {
+ {PCI_VENDOR_ID_NATINST, 0x0162, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1170, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1180, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1190, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x11b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x11c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x11d0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1270, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1330, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1350, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x14e0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x14f0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1580, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x15b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1880, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x1870, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x18b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x18c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x2410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x2420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x2430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x2890, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x28c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x2a60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x2a70, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x2a80, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x2ab0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x2b80, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x2b90, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x2c80, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x2ca0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70aa, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70ab, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70ac, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70af, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70b4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70b6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70b7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70b8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70bc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70bd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70bf, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x70f2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x710d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x716c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x717f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x71bc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_NATINST, 0x717d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, ni_pci_table);
+
+/* These are not all the possible ao ranges for 628x boards.
+ They can do OFFSET +- REFERENCE where OFFSET can be
+ 0V, 5V, APFI<0,1>, or AO<0...3> and RANGE can
+ be 10V, 5V, 2V, 1V, APFI<0,1>, AO<0...3>. That's
+ 63 different possibilities. An AO channel
+ can not act as it's own OFFSET or REFERENCE.
+*/
+static const struct comedi_lrange range_ni_M_628x_ao = { 8, {
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ RANGE(-2, 2),
+ RANGE(-1, 1),
+ RANGE(-5, 15),
+ RANGE(0, 10),
+ RANGE(3, 7),
+ RANGE(4, 6),
+ RANGE_ext(-1, 1)
+ }
+};
+static const struct comedi_lrange range_ni_M_625x_ao = { 3, {
+ RANGE(-10, 10),
+ RANGE(-5, 5),
+ RANGE_ext(-1, 1)
+ }
+};
+static const struct comedi_lrange range_ni_M_622x_ao = { 1, {
+ RANGE(-10, 10),
+ }
+};
+
+static const ni_board ni_boards[] = {
+ {
+ .device_id = 0x0162, // NI also says 0x1620. typo?
+ .name = "pci-mio-16xe-50",
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 2048,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_8,
+ .ai_speed = 50000,
+ .n_aochan = 2,
+ .aobits = 12,
+ .ao_fifo_depth = 0,
+ .ao_range_table = &range_bipolar10,
+ .ao_unipolar = 0,
+ .ao_speed = 50000,
+ .num_p0_dio_channels = 8,
+ .caldac = {dac8800, dac8043},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x1170,
+ .name = "pci-mio-16xe-10", // aka pci-6030E
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_14,
+ .ai_speed = 10000,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_unipolar = 1,
+ .ao_speed = 10000,
+ .num_p0_dio_channels = 8,
+ .caldac = {dac8800, dac8043, ad8522},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x28c0,
+ .name = "pci-6014",
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_fifo_depth = 0,
+ .ao_range_table = &range_bipolar10,
+ .ao_unipolar = 0,
+ .ao_speed = 100000,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804_debug},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x11d0,
+ .name = "pxi-6030e",
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_14,
+ .ai_speed = 10000,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_unipolar = 1,
+ .ao_speed = 10000,
+ .num_p0_dio_channels = 8,
+ .caldac = {dac8800, dac8043, ad8522},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x1180,
+ .name = "pci-mio-16e-1", /* aka pci-6070e */
+ .n_adchan = 16,
+ .adbits = 12,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 0,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 800,
+ .n_aochan = 2,
+ .aobits = 12,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_unipolar = 1,
+ .ao_speed = 1000,
+ .num_p0_dio_channels = 8,
+ .caldac = {mb88341},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x1190,
+ .name = "pci-mio-16e-4", /* aka pci-6040e */
+ .n_adchan = 16,
+ .adbits = 12,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 0,
+ .gainlkup = ai_gain_16,
+ /* Note: there have been reported problems with full speed
+ * on this board */
+ .ai_speed = 2000,
+ .n_aochan = 2,
+ .aobits = 12,
+ .ao_fifo_depth = 512,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_unipolar = 1,
+ .ao_speed = 1000,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804_debug}, // doc says mb88341
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x11c0,
+ .name = "pxi-6040e",
+ .n_adchan = 16,
+ .adbits = 12,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 0,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 2000,
+ .n_aochan = 2,
+ .aobits = 12,
+ .ao_fifo_depth = 512,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_unipolar = 1,
+ .ao_speed = 1000,
+ .num_p0_dio_channels = 8,
+ .caldac = {mb88341},
+ .has_8255 = 0,
+ },
+
+ {
+ .device_id = 0x1330,
+ .name = "pci-6031e",
+ .n_adchan = 64,
+ .adbits = 16,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_14,
+ .ai_speed = 10000,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_unipolar = 1,
+ .ao_speed = 10000,
+ .num_p0_dio_channels = 8,
+ .caldac = {dac8800, dac8043, ad8522},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x1270,
+ .name = "pci-6032e",
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_14,
+ .ai_speed = 10000,
+ .n_aochan = 0,
+ .aobits = 0,
+ .ao_fifo_depth = 0,
+ .ao_unipolar = 0,
+ .num_p0_dio_channels = 8,
+ .caldac = {dac8800, dac8043, ad8522},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x1340,
+ .name = "pci-6033e",
+ .n_adchan = 64,
+ .adbits = 16,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_14,
+ .ai_speed = 10000,
+ .n_aochan = 0,
+ .aobits = 0,
+ .ao_fifo_depth = 0,
+ .ao_unipolar = 0,
+ .num_p0_dio_channels = 8,
+ .caldac = {dac8800, dac8043, ad8522},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x1350,
+ .name = "pci-6071e",
+ .n_adchan = 64,
+ .adbits = 12,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 800,
+ .n_aochan = 2,
+ .aobits = 12,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_unipolar = 1,
+ .ao_speed = 1000,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804_debug},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x2a60,
+ .name = "pci-6023e",
+ .n_adchan = 16,
+ .adbits = 12,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 0,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 0,
+ .aobits = 0,
+ .ao_unipolar = 0,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804_debug}, /* manual is wrong */
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x2a70,
+ .name = "pci-6024e",
+ .n_adchan = 16,
+ .adbits = 12,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 0,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 2,
+ .aobits = 12,
+ .ao_fifo_depth = 0,
+ .ao_range_table = &range_bipolar10,
+ .ao_unipolar = 0,
+ .ao_speed = 100000,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804_debug}, /* manual is wrong */
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x2a80,
+ .name = "pci-6025e",
+ .n_adchan = 16,
+ .adbits = 12,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 0,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 2,
+ .aobits = 12,
+ .ao_fifo_depth = 0,
+ .ao_range_table = &range_bipolar10,
+ .ao_unipolar = 0,
+ .ao_speed = 100000,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804_debug}, /* manual is wrong */
+ .has_8255 = 1,
+ },
+ {
+ .device_id = 0x2ab0,
+ .name = "pxi-6025e",
+ .n_adchan = 16,
+ .adbits = 12,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 0,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 2,
+ .aobits = 12,
+ .ao_fifo_depth = 0,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_unipolar = 1,
+ .ao_speed = 100000,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804_debug}, /* manual is wrong */
+ .has_8255 = 1,
+ },
+
+ {
+ .device_id = 0x2ca0,
+ .name = "pci-6034e",
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 0,
+ .aobits = 0,
+ .ao_fifo_depth = 0,
+ .ao_unipolar = 0,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804_debug},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x2c80,
+ .name = "pci-6035e",
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 2,
+ .aobits = 12,
+ .ao_fifo_depth = 0,
+ .ao_range_table = &range_bipolar10,
+ .ao_unipolar = 0,
+ .ao_speed = 100000,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804_debug},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x18b0,
+ .name = "pci-6052e",
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 3000,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_unipolar = 1,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 3000,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804_debug, ad8804_debug, ad8522}, /* manual is wrong */
+ },
+ {.device_id = 0x14e0,
+ .name = "pci-6110",
+ .n_adchan = 4,
+ .adbits = 12,
+ .ai_fifo_depth = 8192,
+ .alwaysdither = 0,
+ .gainlkup = ai_gain_611x,
+ .ai_speed = 200,
+ .n_aochan = 2,
+ .aobits = 16,
+ .reg_type = ni_reg_611x,
+ .ao_range_table = &range_bipolar10,
+ .ao_unipolar = 0,
+ .ao_fifo_depth = 2048,
+ .ao_speed = 250,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804, ad8804},
+ },
+ {
+ .device_id = 0x14f0,
+ .name = "pci-6111",
+ .n_adchan = 2,
+ .adbits = 12,
+ .ai_fifo_depth = 8192,
+ .alwaysdither = 0,
+ .gainlkup = ai_gain_611x,
+ .ai_speed = 200,
+ .n_aochan = 2,
+ .aobits = 16,
+ .reg_type = ni_reg_611x,
+ .ao_range_table = &range_bipolar10,
+ .ao_unipolar = 0,
+ .ao_fifo_depth = 2048,
+ .ao_speed = 250,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804, ad8804},
+ },
+#if 0
+ /* The 6115 boards probably need their own driver */
+ {
+ .device_id = 0x2ed0,
+ .name = "pci-6115",
+ .n_adchan = 4,
+ .adbits = 12,
+ .ai_fifo_depth = 8192,
+ .alwaysdither = 0,
+ .gainlkup = ai_gain_611x,
+ .ai_speed = 100,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_671x = 1,
+ .ao_unipolar = 0,
+ .ao_fifo_depth = 2048,
+ .ao_speed = 250,
+ .num_p0_dio_channels = 8,
+ .reg_611x = 1,
+ .caldac = {ad8804_debug, ad8804_debug, ad8804_debug}, /* XXX */
+ },
+#endif
+#if 0
+ {
+ .device_id = 0x0000,
+ .name = "pxi-6115",
+ .n_adchan = 4,
+ .adbits = 12,
+ .ai_fifo_depth = 8192,
+ .alwaysdither = 0,
+ .gainlkup = ai_gain_611x,
+ .ai_speed = 100,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_671x = 1,
+ .ao_unipolar = 0,
+ .ao_fifo_depth = 2048,
+ .ao_speed = 250,
+ .reg_611x = 1,
+ .num_p0_dio_channels = 8,
+ caldac = {ad8804_debug, ad8804_debug, ad8804_debug}, /* XXX */
+ },
+#endif
+ {
+ .device_id = 0x1880,
+ .name = "pci-6711",
+ .n_adchan = 0, /* no analog input */
+ .n_aochan = 4,
+ .aobits = 12,
+ .ao_unipolar = 0,
+ .ao_fifo_depth = 16384,
+ /* data sheet says 8192, but fifo really holds 16384 samples */
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000,
+ .num_p0_dio_channels = 8,
+ .reg_type = ni_reg_6711,
+ .caldac = {ad8804_debug},
+ },
+ {
+ .device_id = 0x2b90,
+ .name = "pxi-6711",
+ .n_adchan = 0, /* no analog input */
+ .n_aochan = 4,
+ .aobits = 12,
+ .ao_unipolar = 0,
+ .ao_fifo_depth = 16384,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000,
+ .num_p0_dio_channels = 8,
+ .reg_type = ni_reg_6711,
+ .caldac = {ad8804_debug},
+ },
+ {
+ .device_id = 0x1870,
+ .name = "pci-6713",
+ .n_adchan = 0, /* no analog input */
+ .n_aochan = 8,
+ .aobits = 12,
+ .ao_unipolar = 0,
+ .ao_fifo_depth = 16384,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000,
+ .num_p0_dio_channels = 8,
+ .reg_type = ni_reg_6713,
+ .caldac = {ad8804_debug, ad8804_debug},
+ },
+ {
+ .device_id = 0x2b80,
+ .name = "pxi-6713",
+ .n_adchan = 0, /* no analog input */
+ .n_aochan = 8,
+ .aobits = 12,
+ .ao_unipolar = 0,
+ .ao_fifo_depth = 16384,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000,
+ .num_p0_dio_channels = 8,
+ .reg_type = ni_reg_6713,
+ .caldac = {ad8804_debug, ad8804_debug},
+ },
+ {
+ .device_id = 0x2430,
+ .name = "pci-6731",
+ .n_adchan = 0, /* no analog input */
+ .n_aochan = 4,
+ .aobits = 16,
+ .ao_unipolar = 0,
+ .ao_fifo_depth = 8192,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000,
+ .num_p0_dio_channels = 8,
+ .reg_type = ni_reg_6711,
+ .caldac = {ad8804_debug},
+ },
+#if 0 /* need device ids */
+ {
+ .device_id = 0x0,
+ .name = "pxi-6731",
+ .n_adchan = 0, /* no analog input */
+ .n_aochan = 4,
+ .aobits = 16,
+ .ao_unipolar = 0,
+ .ao_fifo_depth = 8192,
+ .ao_range_table = &range_bipolar10,
+ .num_p0_dio_channels = 8,
+ .reg_type = ni_reg_6711,
+ .caldac = {ad8804_debug},
+ },
+#endif
+ {
+ .device_id = 0x2410,
+ .name = "pci-6733",
+ .n_adchan = 0, /* no analog input */
+ .n_aochan = 8,
+ .aobits = 16,
+ .ao_unipolar = 0,
+ .ao_fifo_depth = 16384,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000,
+ .num_p0_dio_channels = 8,
+ .reg_type = ni_reg_6713,
+ .caldac = {ad8804_debug, ad8804_debug},
+ },
+ {
+ .device_id = 0x2420,
+ .name = "pxi-6733",
+ .n_adchan = 0, /* no analog input */
+ .n_aochan = 8,
+ .aobits = 16,
+ .ao_unipolar = 0,
+ .ao_fifo_depth = 16384,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000,
+ .num_p0_dio_channels = 8,
+ .reg_type = ni_reg_6713,
+ .caldac = {ad8804_debug, ad8804_debug},
+ },
+ {
+ .device_id = 0x15b0,
+ .name = "pxi-6071e",
+ .n_adchan = 64,
+ .adbits = 12,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 800,
+ .n_aochan = 2,
+ .aobits = 12,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_unipolar = 1,
+ .ao_speed = 1000,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804_debug},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x11b0,
+ .name = "pxi-6070e",
+ .n_adchan = 16,
+ .adbits = 12,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 800,
+ .n_aochan = 2,
+ .aobits = 12,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_unipolar = 1,
+ .ao_speed = 1000,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804_debug},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x18c0,
+ .name = "pxi-6052e",
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 3000,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_unipolar = 1,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 3000,
+ .num_p0_dio_channels = 8,
+ .caldac = {mb88341, mb88341, ad8522},
+ },
+ {
+ .device_id = 0x1580,
+ .name = "pxi-6031e",
+ .n_adchan = 64,
+ .adbits = 16,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_14,
+ .ai_speed = 10000,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_unipolar = 1,
+ .ao_speed = 10000,
+ .num_p0_dio_channels = 8,
+ .caldac = {dac8800, dac8043, ad8522},
+ },
+ {
+ .device_id = 0x2890,
+ .name = "pci-6036e",
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_fifo_depth = 0,
+ .ao_range_table = &range_bipolar10,
+ .ao_unipolar = 0,
+ .ao_speed = 100000,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804_debug},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x70b0,
+ .name = "pci-6220",
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 512,
+ //FIXME: guess
+ .gainlkup = ai_gain_622x,
+ .ai_speed = 4000,
+ .n_aochan = 0,
+ .aobits = 0,
+ .ao_fifo_depth = 0,
+ .num_p0_dio_channels = 8,
+ .reg_type = ni_reg_622x,
+ .ao_unipolar = 0,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x70af,
+ .name = "pci-6221",
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_622x,
+ .ai_speed = 4000,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_622x_ao,
+ .reg_type = ni_reg_622x,
+ .ao_unipolar = 0,
+ .ao_speed = 1200,
+ .num_p0_dio_channels = 8,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x71bc,
+ .name = "pci-6221_37pin",
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_622x,
+ .ai_speed = 4000,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_622x_ao,
+ .reg_type = ni_reg_622x,
+ .ao_unipolar = 0,
+ .ao_speed = 1200,
+ .num_p0_dio_channels = 8,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x70f2,
+ .name = "pci-6224",
+ .n_adchan = 32,
+ .adbits = 16,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_622x,
+ .ai_speed = 4000,
+ .n_aochan = 0,
+ .aobits = 0,
+ .ao_fifo_depth = 0,
+ .reg_type = ni_reg_622x,
+ .ao_unipolar = 0,
+ .num_p0_dio_channels = 32,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x70f3,
+ .name = "pxi-6224",
+ .n_adchan = 32,
+ .adbits = 16,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_622x,
+ .ai_speed = 4000,
+ .n_aochan = 0,
+ .aobits = 0,
+ .ao_fifo_depth = 0,
+ .reg_type = ni_reg_622x,
+ .ao_unipolar = 0,
+ .num_p0_dio_channels = 32,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x716c,
+ .name = "pci-6225",
+ .n_adchan = 80,
+ .adbits = 16,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_622x,
+ .ai_speed = 4000,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_622x_ao,
+ .reg_type = ni_reg_622x,
+ .ao_unipolar = 0,
+ .ao_speed = 1200,
+ .num_p0_dio_channels = 32,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x70aa,
+ .name = "pci-6229",
+ .n_adchan = 32,
+ .adbits = 16,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_622x,
+ .ai_speed = 4000,
+ .n_aochan = 4,
+ .aobits = 16,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_622x_ao,
+ .reg_type = ni_reg_622x,
+ .ao_unipolar = 0,
+ .ao_speed = 1200,
+ .num_p0_dio_channels = 32,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x70b4,
+ .name = "pci-6250",
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 800,
+ .n_aochan = 0,
+ .aobits = 0,
+ .ao_fifo_depth = 0,
+ .reg_type = ni_reg_625x,
+ .ao_unipolar = 0,
+ .num_p0_dio_channels = 8,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x70b8,
+ .name = "pci-6251",
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 800,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_625x_ao,
+ .reg_type = ni_reg_625x,
+ .ao_unipolar = 0,
+ .ao_speed = 357,
+ .num_p0_dio_channels = 8,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x717d,
+ .name = "pcie-6251",
+ .n_adchan = 16,
+ .adbits = 16,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 800,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_625x_ao,
+ .reg_type = ni_reg_625x,
+ .ao_unipolar = 0,
+ .ao_speed = 357,
+ .num_p0_dio_channels = 8,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x70b7,
+ .name = "pci-6254",
+ .n_adchan = 32,
+ .adbits = 16,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 800,
+ .n_aochan = 0,
+ .aobits = 0,
+ .ao_fifo_depth = 0,
+ .reg_type = ni_reg_625x,
+ .ao_unipolar = 0,
+ .num_p0_dio_channels = 32,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x70ab,
+ .name = "pci-6259",
+ .n_adchan = 32,
+ .adbits = 16,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 800,
+ .n_aochan = 4,
+ .aobits = 16,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_625x_ao,
+ .reg_type = ni_reg_625x,
+ .ao_unipolar = 0,
+ .ao_speed = 357,
+ .num_p0_dio_channels = 32,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x717f,
+ .name = "pcie-6259",
+ .n_adchan = 32,
+ .adbits = 16,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 800,
+ .n_aochan = 4,
+ .aobits = 16,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_625x_ao,
+ .reg_type = ni_reg_625x,
+ .ao_unipolar = 0,
+ .ao_speed = 357,
+ .num_p0_dio_channels = 32,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x70b6,
+ .name = "pci-6280",
+ .n_adchan = 16,
+ .adbits = 18,
+ .ai_fifo_depth = 2047,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 1600,
+ .n_aochan = 0,
+ .aobits = 0,
+ .ao_fifo_depth = 8191,
+ .reg_type = ni_reg_628x,
+ .ao_unipolar = 0,
+ .num_p0_dio_channels = 8,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x70bd,
+ .name = "pci-6281",
+ .n_adchan = 16,
+ .adbits = 18,
+ .ai_fifo_depth = 2047,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 1600,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_628x_ao,
+ .reg_type = ni_reg_628x,
+ .ao_unipolar = 1,
+ .ao_speed = 357,
+ .num_p0_dio_channels = 8,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x70bf,
+ .name = "pxi-6281",
+ .n_adchan = 16,
+ .adbits = 18,
+ .ai_fifo_depth = 2047,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 1600,
+ .n_aochan = 2,
+ .aobits = 16,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_628x_ao,
+ .reg_type = ni_reg_628x,
+ .ao_unipolar = 1,
+ .ao_speed = 357,
+ .num_p0_dio_channels = 8,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x70bc,
+ .name = "pci-6284",
+ .n_adchan = 32,
+ .adbits = 18,
+ .ai_fifo_depth = 2047,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 1600,
+ .n_aochan = 0,
+ .aobits = 0,
+ .ao_fifo_depth = 0,
+ .reg_type = ni_reg_628x,
+ .ao_unipolar = 0,
+ .num_p0_dio_channels = 32,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x70ac,
+ .name = "pci-6289",
+ .n_adchan = 32,
+ .adbits = 18,
+ .ai_fifo_depth = 2047,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 1600,
+ .n_aochan = 4,
+ .aobits = 16,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_628x_ao,
+ .reg_type = ni_reg_628x,
+ .ao_unipolar = 1,
+ .ao_speed = 357,
+ .num_p0_dio_channels = 32,
+ .caldac = {caldac_none},
+ .has_8255 = 0,
+ },
+ {
+ .device_id = 0x70C0,
+ .name = "pci-6143",
+ .n_adchan = 8,
+ .adbits = 16,
+ .ai_fifo_depth = 1024,
+ .alwaysdither = 0,
+ .gainlkup = ai_gain_6143,
+ .ai_speed = 4000,
+ .n_aochan = 0,
+ .aobits = 0,
+ .reg_type = ni_reg_6143,
+ .ao_unipolar = 0,
+ .ao_fifo_depth = 0,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804_debug, ad8804_debug},
+ },
+ {
+ .device_id = 0x710D,
+ .name = "pxi-6143",
+ .n_adchan = 8,
+ .adbits = 16,
+ .ai_fifo_depth = 1024,
+ .alwaysdither = 0,
+ .gainlkup = ai_gain_6143,
+ .ai_speed = 4000,
+ .n_aochan = 0,
+ .aobits = 0,
+ .reg_type = ni_reg_6143,
+ .ao_unipolar = 0,
+ .ao_fifo_depth = 0,
+ .num_p0_dio_channels = 8,
+ .caldac = {ad8804_debug, ad8804_debug},
+ },
+};
+
+#define n_pcimio_boards ((sizeof(ni_boards)/sizeof(ni_boards[0])))
+
+static int pcimio_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcimio_detach(struct comedi_device * dev);
+static struct comedi_driver driver_pcimio = {
+ driver_name: DRV_NAME,
+ module:THIS_MODULE,
+ attach:pcimio_attach,
+ detach:pcimio_detach,
+};
+
+COMEDI_PCI_INITCLEANUP(driver_pcimio, ni_pci_table)
+
+typedef struct {
+NI_PRIVATE_COMMON} ni_private;
+#define devpriv ((ni_private *)dev->private)
+
+/* How we access registers */
+
+#define ni_writel(a,b) (writel((a), devpriv->mite->daq_io_addr + (b)))
+#define ni_readl(a) (readl(devpriv->mite->daq_io_addr + (a)))
+#define ni_writew(a,b) (writew((a), devpriv->mite->daq_io_addr + (b)))
+#define ni_readw(a) (readw(devpriv->mite->daq_io_addr + (a)))
+#define ni_writeb(a,b) (writeb((a), devpriv->mite->daq_io_addr + (b)))
+#define ni_readb(a) (readb(devpriv->mite->daq_io_addr + (a)))
+
+/* How we access STC registers */
+
+/* We automatically take advantage of STC registers that can be
+ * read/written directly in the I/O space of the board. Most
+ * PCIMIO devices map the low 8 STC registers to iobase+addr*2.
+ * The 611x devices map the write registers to iobase+addr*2, and
+ * the read registers to iobase+(addr-1)*2. */
+/* However, the 611x boards still aren't working, so I'm disabling
+ * non-windowed STC access temporarily */
+
+static void e_series_win_out(struct comedi_device * dev, uint16_t data, int reg)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&devpriv->window_lock, flags);
+ ni_writew(reg, Window_Address);
+ ni_writew(data, Window_Data);
+ comedi_spin_unlock_irqrestore(&devpriv->window_lock, flags);
+}
+
+static uint16_t e_series_win_in(struct comedi_device * dev, int reg)
+{
+ unsigned long flags;
+ uint16_t ret;
+
+ comedi_spin_lock_irqsave(&devpriv->window_lock, flags);
+ ni_writew(reg, Window_Address);
+ ret = ni_readw(Window_Data);
+ comedi_spin_unlock_irqrestore(&devpriv->window_lock, flags);
+
+ return ret;
+}
+
+static void m_series_stc_writew(struct comedi_device * dev, uint16_t data, int reg)
+{
+ unsigned offset;
+ switch (reg) {
+ case ADC_FIFO_Clear:
+ offset = M_Offset_AI_FIFO_Clear;
+ break;
+ case AI_Command_1_Register:
+ offset = M_Offset_AI_Command_1;
+ break;
+ case AI_Command_2_Register:
+ offset = M_Offset_AI_Command_2;
+ break;
+ case AI_Mode_1_Register:
+ offset = M_Offset_AI_Mode_1;
+ break;
+ case AI_Mode_2_Register:
+ offset = M_Offset_AI_Mode_2;
+ break;
+ case AI_Mode_3_Register:
+ offset = M_Offset_AI_Mode_3;
+ break;
+ case AI_Output_Control_Register:
+ offset = M_Offset_AI_Output_Control;
+ break;
+ case AI_Personal_Register:
+ offset = M_Offset_AI_Personal;
+ break;
+ case AI_SI2_Load_A_Register:
+ // this is actually a 32 bit register on m series boards
+ ni_writel(data, M_Offset_AI_SI2_Load_A);
+ return;
+ break;
+ case AI_SI2_Load_B_Register:
+ // this is actually a 32 bit register on m series boards
+ ni_writel(data, M_Offset_AI_SI2_Load_B);
+ return;
+ break;
+ case AI_START_STOP_Select_Register:
+ offset = M_Offset_AI_START_STOP_Select;
+ break;
+ case AI_Trigger_Select_Register:
+ offset = M_Offset_AI_Trigger_Select;
+ break;
+ case Analog_Trigger_Etc_Register:
+ offset = M_Offset_Analog_Trigger_Etc;
+ break;
+ case AO_Command_1_Register:
+ offset = M_Offset_AO_Command_1;
+ break;
+ case AO_Command_2_Register:
+ offset = M_Offset_AO_Command_2;
+ break;
+ case AO_Mode_1_Register:
+ offset = M_Offset_AO_Mode_1;
+ break;
+ case AO_Mode_2_Register:
+ offset = M_Offset_AO_Mode_2;
+ break;
+ case AO_Mode_3_Register:
+ offset = M_Offset_AO_Mode_3;
+ break;
+ case AO_Output_Control_Register:
+ offset = M_Offset_AO_Output_Control;
+ break;
+ case AO_Personal_Register:
+ offset = M_Offset_AO_Personal;
+ break;
+ case AO_Start_Select_Register:
+ offset = M_Offset_AO_Start_Select;
+ break;
+ case AO_Trigger_Select_Register:
+ offset = M_Offset_AO_Trigger_Select;
+ break;
+ case Clock_and_FOUT_Register:
+ offset = M_Offset_Clock_and_FOUT;
+ break;
+ case Configuration_Memory_Clear:
+ offset = M_Offset_Configuration_Memory_Clear;
+ break;
+ case DAC_FIFO_Clear:
+ offset = M_Offset_AO_FIFO_Clear;
+ break;
+ case DIO_Control_Register:
+ rt_printk
+ ("%s: FIXME: register 0x%x does not map cleanly on to m-series boards.\n",
+ __FUNCTION__, reg);
+ return;
+ break;
+ case G_Autoincrement_Register(0):
+ offset = M_Offset_G0_Autoincrement;
+ break;
+ case G_Autoincrement_Register(1):
+ offset = M_Offset_G1_Autoincrement;
+ break;
+ case G_Command_Register(0):
+ offset = M_Offset_G0_Command;
+ break;
+ case G_Command_Register(1):
+ offset = M_Offset_G1_Command;
+ break;
+ case G_Input_Select_Register(0):
+ offset = M_Offset_G0_Input_Select;
+ break;
+ case G_Input_Select_Register(1):
+ offset = M_Offset_G1_Input_Select;
+ break;
+ case G_Mode_Register(0):
+ offset = M_Offset_G0_Mode;
+ break;
+ case G_Mode_Register(1):
+ offset = M_Offset_G1_Mode;
+ break;
+ case Interrupt_A_Ack_Register:
+ offset = M_Offset_Interrupt_A_Ack;
+ break;
+ case Interrupt_A_Enable_Register:
+ offset = M_Offset_Interrupt_A_Enable;
+ break;
+ case Interrupt_B_Ack_Register:
+ offset = M_Offset_Interrupt_B_Ack;
+ break;
+ case Interrupt_B_Enable_Register:
+ offset = M_Offset_Interrupt_B_Enable;
+ break;
+ case Interrupt_Control_Register:
+ offset = M_Offset_Interrupt_Control;
+ break;
+ case IO_Bidirection_Pin_Register:
+ offset = M_Offset_IO_Bidirection_Pin;
+ break;
+ case Joint_Reset_Register:
+ offset = M_Offset_Joint_Reset;
+ break;
+ case RTSI_Trig_A_Output_Register:
+ offset = M_Offset_RTSI_Trig_A_Output;
+ break;
+ case RTSI_Trig_B_Output_Register:
+ offset = M_Offset_RTSI_Trig_B_Output;
+ break;
+ case RTSI_Trig_Direction_Register:
+ offset = M_Offset_RTSI_Trig_Direction;
+ break;
+ /* FIXME: DIO_Output_Register (16 bit reg) is replaced by M_Offset_Static_Digital_Output (32 bit)
+ and M_Offset_SCXI_Serial_Data_Out (8 bit) */
+ default:
+ rt_printk("%s: bug! unhandled register=0x%x in switch.\n",
+ __FUNCTION__, reg);
+ BUG();
+ return;
+ break;
+ }
+ ni_writew(data, offset);
+}
+
+static uint16_t m_series_stc_readw(struct comedi_device * dev, int reg)
+{
+ unsigned offset;
+ switch (reg) {
+ case AI_Status_1_Register:
+ offset = M_Offset_AI_Status_1;
+ break;
+ case AO_Status_1_Register:
+ offset = M_Offset_AO_Status_1;
+ break;
+ case AO_Status_2_Register:
+ offset = M_Offset_AO_Status_2;
+ break;
+ case DIO_Serial_Input_Register:
+ return ni_readb(M_Offset_SCXI_Serial_Data_In);
+ break;
+ case Joint_Status_1_Register:
+ offset = M_Offset_Joint_Status_1;
+ break;
+ case Joint_Status_2_Register:
+ offset = M_Offset_Joint_Status_2;
+ break;
+ case G_Status_Register:
+ offset = M_Offset_G01_Status;
+ break;
+ default:
+ rt_printk("%s: bug! unhandled register=0x%x in switch.\n",
+ __FUNCTION__, reg);
+ BUG();
+ return 0;
+ break;
+ }
+ return ni_readw(offset);
+}
+
+static void m_series_stc_writel(struct comedi_device * dev, uint32_t data, int reg)
+{
+ unsigned offset;
+ switch (reg) {
+ case AI_SC_Load_A_Registers:
+ offset = M_Offset_AI_SC_Load_A;
+ break;
+ case AI_SI_Load_A_Registers:
+ offset = M_Offset_AI_SI_Load_A;
+ break;
+ case AO_BC_Load_A_Register:
+ offset = M_Offset_AO_BC_Load_A;
+ break;
+ case AO_UC_Load_A_Register:
+ offset = M_Offset_AO_UC_Load_A;
+ break;
+ case AO_UI_Load_A_Register:
+ offset = M_Offset_AO_UI_Load_A;
+ break;
+ case G_Load_A_Register(0):
+ offset = M_Offset_G0_Load_A;
+ break;
+ case G_Load_A_Register(1):
+ offset = M_Offset_G1_Load_A;
+ break;
+ case G_Load_B_Register(0):
+ offset = M_Offset_G0_Load_B;
+ break;
+ case G_Load_B_Register(1):
+ offset = M_Offset_G1_Load_B;
+ break;
+ default:
+ rt_printk("%s: bug! unhandled register=0x%x in switch.\n",
+ __FUNCTION__, reg);
+ BUG();
+ return;
+ break;
+ }
+ ni_writel(data, offset);
+}
+
+static uint32_t m_series_stc_readl(struct comedi_device * dev, int reg)
+{
+ unsigned offset;
+ switch (reg) {
+ case G_HW_Save_Register(0):
+ offset = M_Offset_G0_HW_Save;
+ break;
+ case G_HW_Save_Register(1):
+ offset = M_Offset_G1_HW_Save;
+ break;
+ case G_Save_Register(0):
+ offset = M_Offset_G0_Save;
+ break;
+ case G_Save_Register(1):
+ offset = M_Offset_G1_Save;
+ break;
+ default:
+ rt_printk("%s: bug! unhandled register=0x%x in switch.\n",
+ __FUNCTION__, reg);
+ BUG();
+ return 0;
+ break;
+ }
+ return ni_readl(offset);
+}
+
+#define interrupt_pin(a) 0
+#define IRQ_POLARITY 1
+
+#define NI_E_IRQ_FLAGS IRQF_SHARED
+
+#include "ni_mio_common.c"
+
+static int pcimio_find_device(struct comedi_device * dev, int bus, int slot);
+static int pcimio_ai_change(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned long new_size);
+static int pcimio_ao_change(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned long new_size);
+static int pcimio_gpct0_change(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned long new_size);
+static int pcimio_gpct1_change(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned long new_size);
+static int pcimio_dio_change(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned long new_size);
+
+static void m_series_init_eeprom_buffer(struct comedi_device * dev)
+{
+ static const int Start_Cal_EEPROM = 0x400;
+ static const unsigned window_size = 10;
+ static const int serial_number_eeprom_offset = 0x4;
+ static const int serial_number_eeprom_length = 0x4;
+ unsigned old_iodwbsr_bits;
+ unsigned old_iodwbsr1_bits;
+ unsigned old_iodwcr1_bits;
+ int i;
+
+ old_iodwbsr_bits = readl(devpriv->mite->mite_io_addr + MITE_IODWBSR);
+ old_iodwbsr1_bits = readl(devpriv->mite->mite_io_addr + MITE_IODWBSR_1);
+ old_iodwcr1_bits = readl(devpriv->mite->mite_io_addr + MITE_IODWCR_1);
+ writel(0x0, devpriv->mite->mite_io_addr + MITE_IODWBSR);
+ writel(((0x80 | window_size) | devpriv->mite->daq_phys_addr),
+ devpriv->mite->mite_io_addr + MITE_IODWBSR_1);
+ writel(0x1 | old_iodwcr1_bits, devpriv->mite->mite_io_addr + MITE_IODWCR_1);
+ writel(0xf, devpriv->mite->mite_io_addr + 0x30);
+
+ BUG_ON(serial_number_eeprom_length > sizeof(devpriv->serial_number));
+ for (i = 0; i < serial_number_eeprom_length; ++i) {
+ char *byte_ptr = (char*)&devpriv->serial_number + i;
+ *byte_ptr = ni_readb(serial_number_eeprom_offset + i);
+ }
+ devpriv->serial_number = be32_to_cpu(devpriv->serial_number);
+
+ for (i = 0; i < M_SERIES_EEPROM_SIZE; ++i) {
+ devpriv->eeprom_buffer[i] = ni_readb(Start_Cal_EEPROM + i);
+ }
+
+ writel(old_iodwbsr1_bits, devpriv->mite->mite_io_addr + MITE_IODWBSR_1);
+ writel(old_iodwbsr_bits, devpriv->mite->mite_io_addr + MITE_IODWBSR);
+ writel(old_iodwcr1_bits, devpriv->mite->mite_io_addr + MITE_IODWCR_1);
+ writel(0x0, devpriv->mite->mite_io_addr + 0x30);
+}
+
+static void init_6143(struct comedi_device * dev)
+{
+ // Disable interrupts
+ devpriv->stc_writew(dev, 0, Interrupt_Control_Register);
+
+ // Initialise 6143 AI specific bits
+ ni_writeb(0x00, Magic_6143); // Set G0,G1 DMA mode to E series version
+ ni_writeb(0x80, PipelineDelay_6143); // Set EOCMode, ADCMode and pipelinedelay
+ ni_writeb(0x00, EOC_Set_6143); // Set EOC Delay
+
+ ni_writel(boardtype.ai_fifo_depth / 2, AIFIFO_Flag_6143); // Set the FIFO half full level
+
+ // Strobe Relay disable bit
+ devpriv->ai_calib_source_enabled = 0;
+ ni_writew(devpriv->ai_calib_source | Calibration_Channel_6143_RelayOff,
+ Calibration_Channel_6143);
+ ni_writew(devpriv->ai_calib_source, Calibration_Channel_6143);
+}
+
+/* cleans up allocated resources */
+static int pcimio_detach(struct comedi_device * dev)
+{
+ mio_common_detach(dev);
+ if (dev->irq) {
+ comedi_free_irq(dev->irq, dev);
+ }
+ if (dev->private) {
+ mite_free_ring(devpriv->ai_mite_ring);
+ mite_free_ring(devpriv->ao_mite_ring);
+ mite_free_ring(devpriv->cdo_mite_ring);
+ mite_free_ring(devpriv->gpct_mite_ring[0]);
+ mite_free_ring(devpriv->gpct_mite_ring[1]);
+ if (devpriv->mite)
+ mite_unsetup(devpriv->mite);
+ }
+
+ return 0;
+}
+
+static int pcimio_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int ret;
+
+ printk("comedi%d: ni_pcimio:", dev->minor);
+
+ ret = ni_alloc_private(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = pcimio_find_device(dev, it->options[0], it->options[1]);
+ if (ret < 0)
+ return ret;
+
+ printk(" %s", boardtype.name);
+ dev->board_name = boardtype.name;
+
+ if (boardtype.reg_type & ni_reg_m_series_mask) {
+ devpriv->stc_writew = &m_series_stc_writew;
+ devpriv->stc_readw = &m_series_stc_readw;
+ devpriv->stc_writel = &m_series_stc_writel;
+ devpriv->stc_readl = &m_series_stc_readl;
+ } else {
+ devpriv->stc_writew = &e_series_win_out;
+ devpriv->stc_readw = &e_series_win_in;
+ devpriv->stc_writel = &win_out2;
+ devpriv->stc_readl = &win_in2;
+ }
+
+ ret = mite_setup(devpriv->mite);
+ if (ret < 0) {
+ printk(" error setting up mite\n");
+ return ret;
+ }
+ comedi_set_hw_dev(dev, &devpriv->mite->pcidev->dev);
+ devpriv->ai_mite_ring = mite_alloc_ring(devpriv->mite);
+ if (devpriv->ai_mite_ring == NULL)
+ return -ENOMEM;
+ devpriv->ao_mite_ring = mite_alloc_ring(devpriv->mite);
+ if (devpriv->ao_mite_ring == NULL)
+ return -ENOMEM;
+ devpriv->cdo_mite_ring = mite_alloc_ring(devpriv->mite);
+ if (devpriv->cdo_mite_ring == NULL)
+ return -ENOMEM;
+ devpriv->gpct_mite_ring[0] = mite_alloc_ring(devpriv->mite);
+ if (devpriv->gpct_mite_ring[0] == NULL)
+ return -ENOMEM;
+ devpriv->gpct_mite_ring[1] = mite_alloc_ring(devpriv->mite);
+ if (devpriv->gpct_mite_ring[1] == NULL)
+ return -ENOMEM;
+
+ if (boardtype.reg_type & ni_reg_m_series_mask)
+ m_series_init_eeprom_buffer(dev);
+ if (boardtype.reg_type == ni_reg_6143)
+ init_6143(dev);
+
+ dev->irq = mite_irq(devpriv->mite);
+
+ if (dev->irq == 0) {
+ printk(" unknown irq (bad)\n");
+ } else {
+ printk(" ( irq = %u )", dev->irq);
+ if ((ret = comedi_request_irq(dev->irq, ni_E_interrupt,
+ NI_E_IRQ_FLAGS, DRV_NAME,
+ dev)) < 0) {
+ printk(" irq not available\n");
+ dev->irq = 0;
+ }
+ }
+
+ ret = ni_E_init(dev, it);
+ if (ret < 0)
+ return ret;
+
+ dev->subdevices[NI_AI_SUBDEV].buf_change = &pcimio_ai_change;
+ dev->subdevices[NI_AO_SUBDEV].buf_change = &pcimio_ao_change;
+ dev->subdevices[NI_GPCT_SUBDEV(0)].buf_change = &pcimio_gpct0_change;
+ dev->subdevices[NI_GPCT_SUBDEV(1)].buf_change = &pcimio_gpct1_change;
+ dev->subdevices[NI_DIO_SUBDEV].buf_change = &pcimio_dio_change;
+
+ return ret;
+}
+
+static int pcimio_find_device(struct comedi_device * dev, int bus, int slot)
+{
+ struct mite_struct *mite;
+ int i;
+
+ for (mite = mite_devices; mite; mite = mite->next) {
+ if (mite->used)
+ continue;
+ if (bus || slot) {
+ if (bus != mite->pcidev->bus->number ||
+ slot != PCI_SLOT(mite->pcidev->devfn))
+ continue;
+ }
+
+ for (i = 0; i < n_pcimio_boards; i++) {
+ if (mite_device_id(mite) == ni_boards[i].device_id) {
+ dev->board_ptr = ni_boards + i;
+ devpriv->mite = mite;
+
+ return 0;
+ }
+ }
+ }
+ printk("no device found\n");
+ mite_list_devices();
+ return -EIO;
+}
+
+static int pcimio_ai_change(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned long new_size)
+{
+ int ret;
+
+ ret = mite_buf_change(devpriv->ai_mite_ring, s->async);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int pcimio_ao_change(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned long new_size)
+{
+ int ret;
+
+ ret = mite_buf_change(devpriv->ao_mite_ring, s->async);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int pcimio_gpct0_change(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned long new_size)
+{
+ int ret;
+
+ ret = mite_buf_change(devpriv->gpct_mite_ring[0], s->async);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int pcimio_gpct1_change(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned long new_size)
+{
+ int ret;
+
+ ret = mite_buf_change(devpriv->gpct_mite_ring[1], s->async);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int pcimio_dio_change(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned long new_size)
+{
+ int ret;
+
+ ret = mite_buf_change(devpriv->cdo_mite_ring, s->async);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/ni_stc.h b/drivers/staging/comedi/drivers/ni_stc.h
new file mode 100644
index 000000000000..1ebf521ab773
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_stc.h
@@ -0,0 +1,1498 @@
+/*
+ module/ni_stc.h
+ Register descriptions for NI DAQ-STC chip
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998-9 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/*
+ References:
+ DAQ-STC Technical Reference Manual
+*/
+
+#ifndef _COMEDI_NI_STC_H
+#define _COMEDI_NI_STC_H
+
+#include "ni_tio.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
+
+#define NUM_PFI_OUTPUT_SELECT_REGS 6
+
+/* 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 AI_Status_2_Register 5
+
+#define Interrupt_B_Ack_Register 3
+enum Interrupt_B_Ack_Bits {
+ G1_Gate_Error_Confirm = _bit1,
+ G1_TC_Error_Confirm = _bit2,
+ AO_BC_TC_Trigger_Error_Confirm = _bit3,
+ AO_BC_TC_Error_Confirm = _bit4,
+ AO_UI2_TC_Error_Confrim = _bit5,
+ AO_UI2_TC_Interrupt_Ack = _bit6,
+ AO_UC_TC_Interrupt_Ack = _bit7,
+ AO_BC_TC_Interrupt_Ack = _bit8,
+ AO_START1_Interrupt_Ack = _bit9,
+ AO_UPDATE_Interrupt_Ack = _bit10,
+ AO_START_Interrupt_Ack = _bit11,
+ AO_STOP_Interrupt_Ack = _bit12,
+ AO_Error_Interrupt_Ack = _bit13,
+ G1_TC_Interrupt_Ack = _bit14,
+ G1_Gate_Interrupt_Ack = _bit15
+};
+
+#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) & 0x3) << 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 AO_Status_2_Register 6
+
+#define DIO_Parallel_Input_Register 7
+
+#define AI_Command_1_Register 8
+#define AI_Analog_Trigger_Reset _bit14
+#define AI_Disarm _bit13
+#define AI_SI2_Arm _bit12
+#define AI_SI2_Load _bit11
+#define AI_SI_Arm _bit10
+#define AI_SI_Load _bit9
+#define AI_DIV_Arm _bit8
+#define AI_DIV_Load _bit7
+#define AI_SC_Arm _bit6
+#define AI_SC_Load _bit5
+#define AI_SCAN_IN_PROG_Pulse _bit4
+#define AI_EXTMUX_CLK_Pulse _bit3
+#define AI_LOCALMUX_CLK_Pulse _bit2
+#define AI_SC_TC_Pulse _bit1
+#define AI_CONVERT_Pulse _bit0
+
+#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_Parallel_Data_Out(a) ((a)&0xff)
+#define DIO_Parallel_Data_Mask 0xff
+#define DIO_SDOUT _bit0
+#define DIO_SDIN _bit4
+#define DIO_Serial_Data_Out(a) (((a)&0xff)<<8)
+#define DIO_Serial_Data_Mask 0xff00
+
+#define DIO_Control_Register 11
+#define DIO_Software_Serial_Control _bit11
+#define DIO_HW_Serial_Timebase _bit10
+#define DIO_HW_Serial_Enable _bit9
+#define DIO_HW_Serial_Start _bit8
+#define DIO_Pins_Dir(a) ((a)&0xff)
+#define DIO_Pins_Dir_Mask 0xff
+
+#define AI_Mode_1_Register 12
+#define AI_CONVERT_Source_Select(a) (((a) & 0x1f) << 11)
+#define AI_SI_Source_select(a) (((a) & 0x1f) << 6)
+#define AI_CONVERT_Source_Polarity _bit5
+#define AI_SI_Source_Polarity _bit4
+#define AI_Start_Stop _bit3
+#define AI_Mode_1_Reserved _bit2
+#define AI_Continuous _bit1
+#define AI_Trigger_Once _bit0
+
+#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_Initial_Load_Source _bit9
+#define AI_SI2_Reload_Mode _bit8
+#define AI_SI_Initial_Load_Source _bit7
+#define AI_SI_Reload_Mode(a) (((a) & 0x7)<<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_SI_Save_Registers 64
+#define AI_SC_Save_Registers 66
+
+#define AI_SI2_Load_A_Register 23
+#define AI_SI2_Load_B_Register 25
+
+#define Joint_Status_1_Register 27
+#define DIO_Serial_IO_In_Progress_St _bit12
+
+#define DIO_Serial_Input_Register 28
+#define Joint_Status_2_Register 29
+enum Joint_Status_2_Bits {
+ AO_TMRDACWRs_In_Progress_St = 0x20,
+};
+
+#define AO_Mode_1_Register 38
+#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_Mask ( 0x3 << 14 )
+enum AO_FIFO_Mode_Bits {
+ AO_FIFO_Mode_HF_to_F = (3 << 14),
+ AO_FIFO_Mode_F = (2 << 14),
+ AO_FIFO_Mode_HF = (1 << 14),
+ AO_FIFO_Mode_E = (0 << 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) & 0x7) << 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 40
+#define AO_UI_Load_A_Register_High 40
+#define AO_UI_Load_A_Register_Low 41
+#define AO_UI_Load_B_Register 42
+#define AO_UI_Save_Registers 16
+#define AO_BC_Load_A_Register 44
+#define AO_BC_Load_A_Register_High 44
+#define AO_BC_Load_A_Register_Low 45
+#define AO_BC_Load_B_Register 46
+#define AO_BC_Load_B_Register_High 46
+#define AO_BC_Load_B_Register_Low 47
+#define AO_BC_Save_Registers 18
+#define AO_UC_Load_A_Register 48
+#define AO_UC_Load_A_Register_High 48
+#define AO_UC_Load_A_Register_Low 49
+#define AO_UC_Load_B_Register 50
+#define AO_UC_Save_Registers 20
+
+#define Clock_and_FOUT_Register 56
+enum Clock_and_FOUT_bits {
+ FOUT_Enable = _bit15,
+ FOUT_Timebase_Select = _bit14,
+ DIO_Serial_Out_Divide_By_2 = _bit13,
+ Slow_Internal_Time_Divide_By_2 = _bit12,
+ Slow_Internal_Timebase = _bit11,
+ G_Source_Divide_By_2 = _bit10,
+ Clock_To_Board_Divide_By_2 = _bit9,
+ Clock_To_Board = _bit8,
+ AI_Output_Divide_By_2 = _bit7,
+ AI_Source_Divide_By_2 = _bit6,
+ AO_Output_Divide_By_2 = _bit5,
+ AO_Source_Divide_By_2 = _bit4,
+ FOUT_Divider_mask = 0xf
+};
+static inline unsigned FOUT_Divider(unsigned divider)
+{
+ return (divider & FOUT_Divider_mask);
+}
+
+#define IO_Bidirection_Pin_Register 57
+#define RTSI_Trig_Direction_Register 58
+enum RTSI_Trig_Direction_Bits {
+ Drive_RTSI_Clock_Bit = 0x1,
+ Use_RTSI_Clock_Bit = 0x2,
+};
+static inline unsigned RTSI_Output_Bit(unsigned channel, int is_mseries)
+{
+ unsigned max_channel;
+ unsigned base_bit_shift;
+ if (is_mseries) {
+ base_bit_shift = 8;
+ max_channel = 7;
+ } else {
+ base_bit_shift = 9;
+ max_channel = 6;
+ }
+ if (channel > max_channel) {
+ rt_printk("%s: bug, invalid RTSI_channel=%i\n", __FUNCTION__,
+ channel);
+ return 0;
+ }
+ return 1 << (base_bit_shift + channel);
+}
+
+#define Interrupt_Control_Register 59
+#define Interrupt_B_Enable _bit15
+#define Interrupt_B_Output_Select(x) ((x)<<12)
+#define Interrupt_A_Enable _bit11
+#define Interrupt_A_Output_Select(x) ((x)<<8)
+#define Pass_Thru_0_Interrupt_Polarity _bit3
+#define Pass_Thru_1_Interrupt_Polarity _bit2
+#define Interrupt_Output_On_3_Pins _bit1
+#define Interrupt_Output_Polarity _bit0
+
+#define AI_Output_Control_Register 60
+#define AI_START_Output_Select _bit10
+#define AI_SCAN_IN_PROG_Output_Select(x) (((x) & 0x3) << 8)
+#define AI_EXTMUX_CLK_Output_Select(x) (((x) & 0x3) << 6)
+#define AI_LOCALMUX_CLK_Output_Select(x) ((x)<<4)
+#define AI_SC_TC_Output_Select(x) ((x)<<2)
+enum ai_convert_output_selection {
+ AI_CONVERT_Output_High_Z = 0,
+ AI_CONVERT_Output_Ground = 1,
+ AI_CONVERT_Output_Enable_Low = 2,
+ AI_CONVERT_Output_Enable_High = 3
+};
+static unsigned AI_CONVERT_Output_Select(enum ai_convert_output_selection
+ selection)
+{
+ return selection & 0x3;
+}
+
+#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) & 0x1f)<<7)
+#define AI_START_Sync _bit6
+#define AI_START_Edge _bit5
+#define AI_START_Select(a) ((a) & 0x1f)
+
+#define AI_Trigger_Select_Register 63
+#define AI_START1_Polarity _bit15
+#define AI_START2_Polarity _bit14
+#define AI_START2_Sync _bit13
+#define AI_START2_Edge _bit12
+#define AI_START2_Select(a) (((a) & 0x1f) << 7)
+#define AI_START1_Sync _bit6
+#define AI_START1_Edge _bit5
+#define AI_START1_Select(a) ((a) & 0x1f)
+
+#define AI_DIV_Load_A_Register 64
+
+#define AO_Start_Select_Register 66
+#define AO_UI2_Software_Gate _bit15
+#define AO_UI2_External_Gate_Polarity _bit14
+#define AO_START_Polarity _bit13
+#define AO_AOFREQ_Enable _bit12
+#define AO_UI2_External_Gate_Select(a) (((a) & 0x1f) << 7)
+#define AO_START_Sync _bit6
+#define AO_START_Edge _bit5
+#define AO_START_Select(a) ((a) & 0x1f)
+
+#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 AO_Last_Gate_Disable _bit0 /* M Series only */
+
+#define Joint_Reset_Register 72
+#define Software_Reset _bit11
+#define AO_Configuration_End _bit9
+#define AI_Configuration_End _bit8
+#define AO_Configuration_Start _bit5
+#define AI_Configuration_Start _bit4
+#define G1_Reset _bit3
+#define G0_Reset _bit2
+#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_A_Enable_Register 74
+enum Second_IRQ_A_Enable_Bits {
+ AI_SC_TC_Second_Irq_Enable = _bit0,
+ AI_START1_Second_Irq_Enable = _bit1,
+ AI_START2_Second_Irq_Enable = _bit2,
+ AI_START_Second_Irq_Enable = _bit3,
+ AI_STOP_Second_Irq_Enable = _bit4,
+ AI_Error_Second_Irq_Enable = _bit5,
+ G0_TC_Second_Irq_Enable = _bit6,
+ AI_FIFO_Second_Irq_Enable = _bit7,
+ G0_Gate_Second_Irq_Enable = _bit8,
+ Pass_Thru_0_Second_Irq_Enable = _bit9
+};
+
+#define Second_IRQ_B_Enable_Register 76
+enum Second_IRQ_B_Enable_Bits {
+ AO_BC_TC_Second_Irq_Enable = _bit0,
+ AO_START1_Second_Irq_Enable = _bit1,
+ AO_UPDATE_Second_Irq_Enable = _bit2,
+ AO_START_Second_Irq_Enable = _bit3,
+ AO_STOP_Second_Irq_Enable = _bit4,
+ AO_Error_Second_Irq_Enable = _bit5,
+ AO_UC_TC_Second_Irq_Enable = _bit6,
+ AO_UI2_TC_Second_Irq_Enable = _bit7,
+ AO_FIFO_Second_Irq_Enable = _bit8,
+ G1_TC_Second_Irq_Enable = _bit9,
+ G1_Gate_Second_Irq_Enable = _bit10,
+ Pass_Thru_1_Second_Irq_Enable = _bit11
+};
+
+#define AI_Personal_Register 77
+#define AI_SHIFTIN_Pulse_Width _bit15
+#define AI_EOC_Polarity _bit14
+#define AI_SOC_Polarity _bit13
+#define AI_SHIFTIN_Polarity _bit12
+#define AI_CONVERT_Pulse_Timebase _bit11
+#define AI_CONVERT_Pulse_Width _bit10
+#define AI_CONVERT_Original_Pulse _bit9
+#define AI_FIFO_Flags_Polarity _bit8
+#define AI_Overrun_Mode _bit7
+#define AI_EXTMUX_CLK_Pulse_Width _bit6
+#define AI_LOCALMUX_CLK_Pulse_Width _bit5
+#define AI_AIFREQ_Polarity _bit4
+
+#define AO_Personal_Register 78
+enum AO_Personal_Bits {
+ AO_Interval_Buffer_Mode = 1 << 3,
+ AO_BC_Source_Select = 1 << 4,
+ AO_UPDATE_Pulse_Width = 1 << 5,
+ AO_UPDATE_Pulse_Timebase = 1 << 6,
+ AO_UPDATE_Original_Pulse = 1 << 7,
+ AO_DMA_PIO_Control = 1 << 8, /* M Series: reserved */
+ AO_AOFREQ_Polarity = 1 << 9, /* M Series: reserved */
+ AO_FIFO_Enable = 1 << 10,
+ AO_FIFO_Flags_Polarity = 1 << 11, /* M Series: reserved */
+ AO_TMRDACWR_Pulse_Width = 1 << 12,
+ AO_Fast_CPU = 1 << 13, /* M Series: reserved */
+ AO_Number_Of_DAC_Packages = 1 << 14, // 1 for "single" mode, 0 for "dual"
+ AO_Multiple_DACS_Per_Package = 1 << 15 // m-series only
+};
+#define RTSI_Trig_A_Output_Register 79
+#define RTSI_Trig_B_Output_Register 80
+enum RTSI_Trig_B_Output_Bits {
+ RTSI_Sub_Selection_1_Bit = 0x8000 // not for m-series
+};
+static inline unsigned RTSI_Trig_Output_Bits(unsigned rtsi_channel,
+ unsigned source)
+{
+ return (source & 0xf) << ((rtsi_channel % 4) * 4);
+};
+static inline unsigned RTSI_Trig_Output_Mask(unsigned rtsi_channel)
+{
+ return 0xf << ((rtsi_channel % 4) * 4);
+};
+
+// inverse to RTSI_Trig_Output_Bits()
+static inline unsigned RTSI_Trig_Output_Source(unsigned rtsi_channel,
+ unsigned bits)
+{
+ return (bits >> ((rtsi_channel % 4) * 4)) & 0xf;
+};
+
+#define RTSI_Board_Register 81
+#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
+enum ao_update_output_selection {
+ AO_Update_Output_High_Z = 0,
+ AO_Update_Output_Ground = 1,
+ AO_Update_Output_Enable_Low = 2,
+ AO_Update_Output_Enable_High = 3
+};
+static unsigned AO_UPDATE_Output_Select(enum ao_update_output_selection
+ selection)
+{
+ return selection & 0x3;
+}
+
+#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) & 0x1f)
+
+#define G_Autoincrement_Register(a) (68+(a))
+#define G_Command_Register(a) (6+(a))
+#define G_HW_Save_Register(a) (8+(a)*2)
+#define G_HW_Save_Register_High(a) (8+(a)*2)
+#define G_HW_Save_Register_Low(a) (9+(a)*2)
+#define G_Input_Select_Register(a) (36+(a))
+#define G_Load_A_Register(a) (28+(a)*4)
+#define G_Load_A_Register_High(a) (28+(a)*4)
+#define G_Load_A_Register_Low(a) (29+(a)*4)
+#define G_Load_B_Register(a) (30+(a)*4)
+#define G_Load_B_Register_High(a) (30+(a)*4)
+#define G_Load_B_Register_Low(a) (31+(a)*4)
+#define G_Mode_Register(a) (26+(a))
+#define G_Save_Register(a) (12+(a)*2)
+#define G_Save_Register_High(a) (12+(a)*2)
+#define G_Save_Register_Low(a) (13+(a)*2)
+#define G_Status_Register 4
+#define Analog_Trigger_Etc_Register 61
+
+/* command register */
+#define G_Disarm_Copy _bit15 /* strobe */
+#define G_Save_Trace_Copy _bit14
+#define G_Arm_Copy _bit13 /* strobe */
+#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_Save_Trace _bit1
+#define G_Arm _bit0 /* strobe */
+
+/*channel agnostic names for the command register #defines */
+#define G_Bank_Switch_Enable _bit12
+#define G_Bank_Switch_Mode _bit11
+#define G_Load _bit2 /* strobe */
+
+/* input select register */
+#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
+
+/* same input select register, but with channel agnostic names */
+#define G_Source_Polarity _bit15
+#define G_Output_Polarity _bit14
+#define G_OR_Gate _bit13
+#define G_Gate_Select_Load_Source _bit12
+
+/* mode register */
+#define G_Loading_On_TC _bit12
+#define G_Output_Mode(a) (((a)&0x03)<<8)
+#define G_Trigger_Mode_For_Edge_Gate(a) (((a)&0x03)<<3)
+#define G_Gating_Mode(a) (((a)&0x03)<<0)
+
+/* same input mode register, but with channel agnostic names */
+#define G_Load_Source_Select _bit7
+#define G_Reload_Source_Switching _bit15
+#define G_Loading_On_Gate _bit14
+#define G_Gate_Polarity _bit13
+
+#define G_Counting_Once(a) (((a)&0x03)<<10)
+#define G_Stop_Mode(a) (((a)&0x03)<<5)
+#define G_Gate_On_Both_Edges _bit2
+
+/* G_Status_Register */
+#define G1_Gate_Error_St _bit15
+#define G0_Gate_Error_St _bit14
+#define G1_TC_Error_St _bit13
+#define G0_TC_Error_St _bit12
+#define G1_No_Load_Between_Gates_St _bit11
+#define G0_No_Load_Between_Gates_St _bit10
+#define G1_Armed_St _bit9
+#define G0_Armed_St _bit8
+#define G1_Stale_Data_St _bit7
+#define G0_Stale_Data_St _bit6
+#define G1_Next_Load_Source_St _bit5
+#define G0_Next_Load_Source_St _bit4
+#define G1_Counting_St _bit3
+#define G0_Counting_St _bit2
+#define G1_Save_St _bit1
+#define G0_Save_St _bit0
+
+/* general purpose counter timer */
+#define G_Autoincrement(a) ((a)<<0)
+
+/*Analog_Trigger_Etc_Register*/
+#define Analog_Trigger_Mode(x) ((x) & 0x7)
+#define Analog_Trigger_Enable _bit3
+#define Analog_Trigger_Drive _bit4
+#define GPFO_1_Output_Select _bit7
+#define GPFO_0_Output_Select(a) ((a)<<11)
+#define GPFO_0_Output_Enable _bit14
+#define GPFO_1_Output_Enable _bit15
+
+/* Additional windowed registers unique to E series */
+
+/* 16 bit registers shadowed from DAQ-STC */
+#define Window_Address 0x00
+#define Window_Data 0x02
+
+#define Configuration_Memory_Clear 82
+#define ADC_FIFO_Clear 83
+#define DAC_FIFO_Clear 84
+
+/* i/o port offsets */
+
+/* 8 bit registers */
+#define XXX_Status 0x01
+enum XXX_Status_Bits {
+ PROMOUT = 0x1,
+ AI_FIFO_LOWER_NOT_EMPTY = 0x8,
+};
+#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
+enum AI_AO_Select_Bits {
+ AI_DMA_Select_Shift = 0,
+ AI_DMA_Select_Mask = 0xf,
+ AO_DMA_Select_Shift = 4,
+ AO_DMA_Select_Mask = 0xf << AO_DMA_Select_Shift
+};
+#define G0_G1_Select 0x0b
+static inline unsigned ni_stc_dma_channel_select_bitfield(unsigned channel)
+{
+ if (channel < 4)
+ return 1 << channel;
+ if (channel == 4)
+ return 0x3;
+ if (channel == 5)
+ return 0x5;
+ BUG();
+ return 0;
+}
+static inline unsigned GPCT_DMA_Select_Bits(unsigned gpct_index,
+ unsigned mite_channel)
+{
+ BUG_ON(gpct_index > 1);
+ return ni_stc_dma_channel_select_bitfield(mite_channel) << (4 *
+ gpct_index);
+}
+static inline unsigned GPCT_DMA_Select_Mask(unsigned gpct_index)
+{
+ BUG_ON(gpct_index > 1);
+ return 0xf << (4 * gpct_index);
+}
+
+/* 16 bit registers */
+
+#define Configuration_Memory_Low 0x10
+enum Configuration_Memory_Low_Bits {
+ AI_DITHER = 0x200,
+ AI_LAST_CHANNEL = 0x8000,
+};
+#define Configuration_Memory_High 0x12
+enum Configuration_Memory_High_Bits {
+ AI_AC_COUPLE = 0x800,
+ AI_DIFFERENTIAL = 0x1000,
+ AI_COMMON = 0x2000,
+ AI_GROUND = 0x3000,
+};
+static inline unsigned int AI_CONFIG_CHANNEL(unsigned int channel)
+{
+ return (channel & 0x3f);
+}
+
+#define ADC_FIFO_Data_Register 0x1c
+
+#define AO_Configuration 0x16
+#define AO_Bipolar _bit0
+#define AO_Deglitch _bit1
+#define AO_Ext_Ref _bit2
+#define AO_Ground_Ref _bit3
+#define AO_Channel(x) ((x) << 8)
+
+#define DAC_FIFO_Data 0x1e
+#define DAC0_Direct_Data 0x18
+#define DAC1_Direct_Data 0x1a
+
+/* 611x registers (these boards differ from the e-series) */
+
+#define Magic_611x 0x19 /* w8 (new) */
+#define Calibration_Channel_Select_611x 0x1a /* w16 (new) */
+#define ADC_FIFO_Data_611x 0x1c /* r32 (incompatible) */
+#define AI_FIFO_Offset_Load_611x 0x05 /* r8 (new) */
+#define DAC_FIFO_Data_611x 0x14 /* w32 (incompatible) */
+#define Cal_Gain_Select_611x 0x05 /* w8 (new) */
+
+#define AO_Window_Address_611x 0x18
+#define AO_Window_Data_611x 0x1e
+
+/* 6143 registers */
+#define Magic_6143 0x19 /* w8 */
+#define G0G1_DMA_Select_6143 0x0B /* w8 */
+#define PipelineDelay_6143 0x1f /* w8 */
+#define EOC_Set_6143 0x1D /* w8 */
+#define AIDMA_Select_6143 0x09 /* w8 */
+#define AIFIFO_Data_6143 0x8C /* w32 */
+#define AIFIFO_Flag_6143 0x84 /* w32 */
+#define AIFIFO_Control_6143 0x88 /* w32 */
+#define AIFIFO_Status_6143 0x88 /* w32 */
+#define AIFIFO_DMAThreshold_6143 0x90 /* w32 */
+#define AIFIFO_Words_Available_6143 0x94 /* w32 */
+
+#define Calibration_Channel_6143 0x42 /* w16 */
+#define Calibration_LowTime_6143 0x20 /* w16 */
+#define Calibration_HighTime_6143 0x22 /* w16 */
+#define Relay_Counter_Load_Val__6143 0x4C /* w32 */
+#define Signature_6143 0x50 /* w32 */
+#define Release_Date_6143 0x54 /* w32 */
+#define Release_Oldest_Date_6143 0x58 /* w32 */
+
+#define Calibration_Channel_6143_RelayOn 0x8000 /* Calibration relay switch On */
+#define Calibration_Channel_6143_RelayOff 0x4000 /* Calibration relay switch Off */
+#define Calibration_Channel_Gnd_Gnd 0x00 /* Offset Calibration */
+#define Calibration_Channel_2v5_Gnd 0x02 /* 2.5V Reference */
+#define Calibration_Channel_Pwm_Gnd 0x05 /* +/- 5V Self Cal */
+#define Calibration_Channel_2v5_Pwm 0x0a /* PWM Calibration */
+#define Calibration_Channel_Pwm_Pwm 0x0d /* CMRR */
+#define Calibration_Channel_Gnd_Pwm 0x0e /* PWM Calibration */
+
+/* 671x, 611x registers */
+
+/* 671xi, 611x windowed ao registers */
+enum windowed_regs_67xx_61xx {
+ AO_Immediate_671x = 0x11, /* W 16 */
+ AO_Timed_611x = 0x10, /* W 16 */
+ AO_FIFO_Offset_Load_611x = 0x13, /* W32 */
+ AO_Later_Single_Point_Updates = 0x14, /* W 16 */
+ AO_Waveform_Generation_611x = 0x15, /* W 16 */
+ AO_Misc_611x = 0x16, /* W 16 */
+ AO_Calibration_Channel_Select_67xx = 0x17, /* W 16 */
+ AO_Configuration_2_67xx = 0x18, /* W 16 */
+ CAL_ADC_Command_67xx = 0x19, /* W 8 */
+ CAL_ADC_Status_67xx = 0x1a, /* R 8 */
+ CAL_ADC_Data_67xx = 0x1b, /* R 16 */
+ CAL_ADC_Config_Data_High_Word_67xx = 0x1c, /* RW 16 */
+ CAL_ADC_Config_Data_Low_Word_67xx = 0x1d, /* RW 16 */
+};
+static inline unsigned int DACx_Direct_Data_671x(int channel)
+{
+ return channel;
+}
+enum AO_Misc_611x_Bits {
+ CLEAR_WG = 1,
+};
+enum cs5529_configuration_bits {
+ CSCFG_CAL_CONTROL_MASK = 0x7,
+ CSCFG_SELF_CAL_OFFSET = 0x1,
+ CSCFG_SELF_CAL_GAIN = 0x2,
+ CSCFG_SELF_CAL_OFFSET_GAIN = 0x3,
+ CSCFG_SYSTEM_CAL_OFFSET = 0x5,
+ CSCFG_SYSTEM_CAL_GAIN = 0x6,
+ CSCFG_DONE = 1 << 3,
+ CSCFG_POWER_SAVE_SELECT = 1 << 4,
+ CSCFG_PORT_MODE = 1 << 5,
+ CSCFG_RESET_VALID = 1 << 6,
+ CSCFG_RESET = 1 << 7,
+ CSCFG_UNIPOLAR = 1 << 12,
+ CSCFG_WORD_RATE_2180_CYCLES = 0x0 << 13,
+ CSCFG_WORD_RATE_1092_CYCLES = 0x1 << 13,
+ CSCFG_WORD_RATE_532_CYCLES = 0x2 << 13,
+ CSCFG_WORD_RATE_388_CYCLES = 0x3 << 13,
+ CSCFG_WORD_RATE_324_CYCLES = 0x4 << 13,
+ CSCFG_WORD_RATE_17444_CYCLES = 0x5 << 13,
+ CSCFG_WORD_RATE_8724_CYCLES = 0x6 << 13,
+ CSCFG_WORD_RATE_4364_CYCLES = 0x7 << 13,
+ CSCFG_WORD_RATE_MASK = 0x7 << 13,
+ CSCFG_LOW_POWER = 1 << 16,
+};
+static inline unsigned int CS5529_CONFIG_DOUT(int output)
+{
+ return 1 << (18 + output);
+}
+static inline unsigned int CS5529_CONFIG_AOUT(int output)
+{
+ return 1 << (22 + output);
+}
+enum cs5529_command_bits {
+ CSCMD_POWER_SAVE = 0x1,
+ CSCMD_REGISTER_SELECT_MASK = 0xe,
+ CSCMD_OFFSET_REGISTER = 0x0,
+ CSCMD_GAIN_REGISTER = 0x2,
+ CSCMD_CONFIG_REGISTER = 0x4,
+ CSCMD_READ = 0x10,
+ CSCMD_CONTINUOUS_CONVERSIONS = 0x20,
+ CSCMD_SINGLE_CONVERSION = 0x40,
+ CSCMD_COMMAND = 0x80,
+};
+enum cs5529_status_bits {
+ CSS_ADC_BUSY = 0x1,
+ CSS_OSC_DETECT = 0x2, /* indicates adc error */
+ CSS_OVERRANGE = 0x4,
+};
+#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_4, ai_gain_611x, ai_gain_622x,
+ ai_gain_628x, ai_gain_6143 };
+enum caldac_enum { caldac_none = 0, mb88341, dac8800, dac8043, ad8522,
+ ad8804, ad8842, ad8804_debug
+};
+enum ni_reg_type {
+ ni_reg_normal = 0x0,
+ ni_reg_611x = 0x1,
+ ni_reg_6711 = 0x2,
+ ni_reg_6713 = 0x4,
+ ni_reg_67xx_mask = 0x6,
+ ni_reg_6xxx_mask = 0x7,
+ ni_reg_622x = 0x8,
+ ni_reg_625x = 0x10,
+ ni_reg_628x = 0x18,
+ ni_reg_m_series_mask = 0x18,
+ ni_reg_6143 = 0x20
+};
+
+static const struct comedi_lrange range_ni_E_ao_ext;
+
+enum m_series_register_offsets {
+ M_Offset_CDIO_DMA_Select = 0x7, // write
+ M_Offset_SCXI_Status = 0x7, // read
+ M_Offset_AI_AO_Select = 0x9, // write, same offset as e-series
+ M_Offset_SCXI_Serial_Data_In = 0x9, // read
+ M_Offset_G0_G1_Select = 0xb, // write, same offset as e-series
+ M_Offset_Misc_Command = 0xf,
+ M_Offset_SCXI_Serial_Data_Out = 0x11,
+ M_Offset_SCXI_Control = 0x13,
+ M_Offset_SCXI_Output_Enable = 0x15,
+ M_Offset_AI_FIFO_Data = 0x1c,
+ M_Offset_Static_Digital_Output = 0x24, // write
+ M_Offset_Static_Digital_Input = 0x24, // read
+ M_Offset_DIO_Direction = 0x28,
+ M_Offset_Cal_PWM = 0x40,
+ M_Offset_AI_Config_FIFO_Data = 0x5e,
+ M_Offset_Interrupt_C_Enable = 0x88, // write
+ M_Offset_Interrupt_C_Status = 0x88, // read
+ M_Offset_Analog_Trigger_Control = 0x8c,
+ M_Offset_AO_Serial_Interrupt_Enable = 0xa0,
+ M_Offset_AO_Serial_Interrupt_Ack = 0xa1, // write
+ M_Offset_AO_Serial_Interrupt_Status = 0xa1, // read
+ M_Offset_AO_Calibration = 0xa3,
+ M_Offset_AO_FIFO_Data = 0xa4,
+ M_Offset_PFI_Filter = 0xb0,
+ M_Offset_RTSI_Filter = 0xb4,
+ M_Offset_SCXI_Legacy_Compatibility = 0xbc,
+ M_Offset_Interrupt_A_Ack = 0x104, // write
+ M_Offset_AI_Status_1 = 0x104, // read
+ M_Offset_Interrupt_B_Ack = 0x106, // write
+ M_Offset_AO_Status_1 = 0x106, // read
+ M_Offset_AI_Command_2 = 0x108, // write
+ M_Offset_G01_Status = 0x108, // read
+ M_Offset_AO_Command_2 = 0x10a,
+ M_Offset_AO_Status_2 = 0x10c, // read
+ M_Offset_G0_Command = 0x10c, // write
+ M_Offset_G1_Command = 0x10e, // write
+ M_Offset_G0_HW_Save = 0x110,
+ M_Offset_G0_HW_Save_High = 0x110,
+ M_Offset_AI_Command_1 = 0x110,
+ M_Offset_G0_HW_Save_Low = 0x112,
+ M_Offset_AO_Command_1 = 0x112,
+ M_Offset_G1_HW_Save = 0x114,
+ M_Offset_G1_HW_Save_High = 0x114,
+ M_Offset_G1_HW_Save_Low = 0x116,
+ M_Offset_AI_Mode_1 = 0x118,
+ M_Offset_G0_Save = 0x118,
+ M_Offset_G0_Save_High = 0x118,
+ M_Offset_AI_Mode_2 = 0x11a,
+ M_Offset_G0_Save_Low = 0x11a,
+ M_Offset_AI_SI_Load_A = 0x11c,
+ M_Offset_G1_Save = 0x11c,
+ M_Offset_G1_Save_High = 0x11c,
+ M_Offset_G1_Save_Low = 0x11e,
+ M_Offset_AI_SI_Load_B = 0x120, // write
+ M_Offset_AO_UI_Save = 0x120, // read
+ M_Offset_AI_SC_Load_A = 0x124, // write
+ M_Offset_AO_BC_Save = 0x124, // read
+ M_Offset_AI_SC_Load_B = 0x128, // write
+ M_Offset_AO_UC_Save = 0x128, //read
+ M_Offset_AI_SI2_Load_A = 0x12c,
+ M_Offset_AI_SI2_Load_B = 0x130,
+ M_Offset_G0_Mode = 0x134,
+ M_Offset_G1_Mode = 0x136, // write
+ M_Offset_Joint_Status_1 = 0x136, // read
+ M_Offset_G0_Load_A = 0x138,
+ M_Offset_Joint_Status_2 = 0x13a,
+ M_Offset_G0_Load_B = 0x13c,
+ M_Offset_G1_Load_A = 0x140,
+ M_Offset_G1_Load_B = 0x144,
+ M_Offset_G0_Input_Select = 0x148,
+ M_Offset_G1_Input_Select = 0x14a,
+ M_Offset_AO_Mode_1 = 0x14c,
+ M_Offset_AO_Mode_2 = 0x14e,
+ M_Offset_AO_UI_Load_A = 0x150,
+ M_Offset_AO_UI_Load_B = 0x154,
+ M_Offset_AO_BC_Load_A = 0x158,
+ M_Offset_AO_BC_Load_B = 0x15c,
+ M_Offset_AO_UC_Load_A = 0x160,
+ M_Offset_AO_UC_Load_B = 0x164,
+ M_Offset_Clock_and_FOUT = 0x170,
+ M_Offset_IO_Bidirection_Pin = 0x172,
+ M_Offset_RTSI_Trig_Direction = 0x174,
+ M_Offset_Interrupt_Control = 0x176,
+ M_Offset_AI_Output_Control = 0x178,
+ M_Offset_Analog_Trigger_Etc = 0x17a,
+ M_Offset_AI_START_STOP_Select = 0x17c,
+ M_Offset_AI_Trigger_Select = 0x17e,
+ M_Offset_AI_SI_Save = 0x180, // read
+ M_Offset_AI_DIV_Load_A = 0x180, // write
+ M_Offset_AI_SC_Save = 0x184, // read
+ M_Offset_AO_Start_Select = 0x184, // write
+ M_Offset_AO_Trigger_Select = 0x186,
+ M_Offset_AO_Mode_3 = 0x18c,
+ M_Offset_G0_Autoincrement = 0x188,
+ M_Offset_G1_Autoincrement = 0x18a,
+ M_Offset_Joint_Reset = 0x190,
+ M_Offset_Interrupt_A_Enable = 0x192,
+ M_Offset_Interrupt_B_Enable = 0x196,
+ M_Offset_AI_Personal = 0x19a,
+ M_Offset_AO_Personal = 0x19c,
+ M_Offset_RTSI_Trig_A_Output = 0x19e,
+ M_Offset_RTSI_Trig_B_Output = 0x1a0,
+ M_Offset_RTSI_Shared_MUX = 0x1a2,
+ M_Offset_AO_Output_Control = 0x1ac,
+ M_Offset_AI_Mode_3 = 0x1ae,
+ M_Offset_Configuration_Memory_Clear = 0x1a4,
+ M_Offset_AI_FIFO_Clear = 0x1a6,
+ M_Offset_AO_FIFO_Clear = 0x1a8,
+ M_Offset_G0_Counting_Mode = 0x1b0,
+ M_Offset_G1_Counting_Mode = 0x1b2,
+ M_Offset_G0_Second_Gate = 0x1b4,
+ M_Offset_G1_Second_Gate = 0x1b6,
+ M_Offset_G0_DMA_Config = 0x1b8, // write
+ M_Offset_G0_DMA_Status = 0x1b8, // read
+ M_Offset_G1_DMA_Config = 0x1ba, // write
+ M_Offset_G1_DMA_Status = 0x1ba, // read
+ M_Offset_G0_MSeries_ABZ = 0x1c0,
+ M_Offset_G1_MSeries_ABZ = 0x1c2,
+ M_Offset_Clock_and_Fout2 = 0x1c4,
+ M_Offset_PLL_Control = 0x1c6,
+ M_Offset_PLL_Status = 0x1c8,
+ M_Offset_PFI_Output_Select_1 = 0x1d0,
+ M_Offset_PFI_Output_Select_2 = 0x1d2,
+ M_Offset_PFI_Output_Select_3 = 0x1d4,
+ M_Offset_PFI_Output_Select_4 = 0x1d6,
+ M_Offset_PFI_Output_Select_5 = 0x1d8,
+ M_Offset_PFI_Output_Select_6 = 0x1da,
+ M_Offset_PFI_DI = 0x1dc,
+ M_Offset_PFI_DO = 0x1de,
+ M_Offset_AI_Config_FIFO_Bypass = 0x218,
+ M_Offset_SCXI_DIO_Enable = 0x21c,
+ M_Offset_CDI_FIFO_Data = 0x220, // read
+ M_Offset_CDO_FIFO_Data = 0x220, // write
+ M_Offset_CDIO_Status = 0x224, // read
+ M_Offset_CDIO_Command = 0x224, // write
+ M_Offset_CDI_Mode = 0x228,
+ M_Offset_CDO_Mode = 0x22c,
+ M_Offset_CDI_Mask_Enable = 0x230,
+ M_Offset_CDO_Mask_Enable = 0x234,
+};
+static inline int M_Offset_AO_Waveform_Order(int channel)
+{
+ return 0xc2 + 0x4 * channel;
+};
+static inline int M_Offset_AO_Config_Bank(int channel)
+{
+ return 0xc3 + 0x4 * channel;
+};
+static inline int M_Offset_DAC_Direct_Data(int channel)
+{
+ return 0xc0 + 0x4 * channel;
+}
+static inline int M_Offset_Gen_PWM(int channel)
+{
+ return 0x44 + 0x2 * channel;
+}
+static inline int M_Offset_Static_AI_Control(int i)
+{
+ int offset[] = {
+ 0x64,
+ 0x261,
+ 0x262,
+ 0x263,
+ };
+ if (((unsigned)i) >= sizeof(offset) / sizeof(offset[0])) {
+ rt_printk("%s: invalid channel=%i\n", __FUNCTION__, i);
+ return offset[0];
+ }
+ return offset[i];
+};
+static inline int M_Offset_AO_Reference_Attenuation(int channel)
+{
+ int offset[] = {
+ 0x264,
+ 0x265,
+ 0x266,
+ 0x267
+ };
+ if (((unsigned)channel) >= sizeof(offset) / sizeof(offset[0])) {
+ rt_printk("%s: invalid channel=%i\n", __FUNCTION__, channel);
+ return offset[0];
+ }
+ return offset[channel];
+};
+static inline unsigned M_Offset_PFI_Output_Select(unsigned n)
+{
+ if (n < 1 || n > NUM_PFI_OUTPUT_SELECT_REGS) {
+ rt_printk("%s: invalid pfi output select register=%i\n",
+ __FUNCTION__, n);
+ return M_Offset_PFI_Output_Select_1;
+ }
+ return M_Offset_PFI_Output_Select_1 + (n - 1) * 2;
+}
+
+enum MSeries_AI_Config_FIFO_Data_Bits {
+ MSeries_AI_Config_Channel_Type_Mask = 0x7 << 6,
+ MSeries_AI_Config_Channel_Type_Calibration_Bits = 0x0,
+ MSeries_AI_Config_Channel_Type_Differential_Bits = 0x1 << 6,
+ MSeries_AI_Config_Channel_Type_Common_Ref_Bits = 0x2 << 6,
+ MSeries_AI_Config_Channel_Type_Ground_Ref_Bits = 0x3 << 6,
+ MSeries_AI_Config_Channel_Type_Aux_Bits = 0x5 << 6,
+ MSeries_AI_Config_Channel_Type_Ghost_Bits = 0x7 << 6,
+ MSeries_AI_Config_Polarity_Bit = 0x1000, // 0 for 2's complement encoding
+ MSeries_AI_Config_Dither_Bit = 0x2000,
+ MSeries_AI_Config_Last_Channel_Bit = 0x4000,
+};
+static inline unsigned MSeries_AI_Config_Channel_Bits(unsigned channel)
+{
+ return channel & 0xf;
+}
+static inline unsigned MSeries_AI_Config_Bank_Bits(enum ni_reg_type reg_type,
+ unsigned channel)
+{
+ unsigned bits = channel & 0x30;
+ if (reg_type == ni_reg_622x) {
+ if (channel & 0x40)
+ bits |= 0x400;
+ }
+ return bits;
+}
+static inline unsigned MSeries_AI_Config_Gain_Bits(unsigned range)
+{
+ return (range & 0x7) << 9;
+}
+
+enum MSeries_Clock_and_Fout2_Bits {
+ MSeries_PLL_In_Source_Select_RTSI0_Bits = 0xb,
+ MSeries_PLL_In_Source_Select_Star_Trigger_Bits = 0x14,
+ MSeries_PLL_In_Source_Select_RTSI7_Bits = 0x1b,
+ MSeries_PLL_In_Source_Select_PXI_Clock10 = 0x1d,
+ MSeries_PLL_In_Source_Select_Mask = 0x1f,
+ MSeries_Timebase1_Select_Bit = 0x20, // use PLL for timebase 1
+ MSeries_Timebase3_Select_Bit = 0x40, // use PLL for timebase 3
+ /* use 10MHz instead of 20MHz for RTSI clock frequency. Appears
+ to have no effect, at least on pxi-6281, which always uses
+ 20MHz rtsi clock frequency */
+ MSeries_RTSI_10MHz_Bit = 0x80
+};
+static inline unsigned MSeries_PLL_In_Source_Select_RTSI_Bits(unsigned
+ RTSI_channel)
+{
+ if (RTSI_channel > 7) {
+ rt_printk("%s: bug, invalid RTSI_channel=%i\n", __FUNCTION__,
+ RTSI_channel);
+ return 0;
+ }
+ if (RTSI_channel == 7)
+ return MSeries_PLL_In_Source_Select_RTSI7_Bits;
+ else
+ return MSeries_PLL_In_Source_Select_RTSI0_Bits + RTSI_channel;
+}
+
+enum MSeries_PLL_Control_Bits {
+ MSeries_PLL_Enable_Bit = 0x1000,
+ MSeries_PLL_VCO_Mode_200_325MHz_Bits = 0x0,
+ MSeries_PLL_VCO_Mode_175_225MHz_Bits = 0x2000,
+ MSeries_PLL_VCO_Mode_100_225MHz_Bits = 0x4000,
+ MSeries_PLL_VCO_Mode_75_150MHz_Bits = 0x6000,
+};
+static inline unsigned MSeries_PLL_Divisor_Bits(unsigned divisor)
+{
+ static const unsigned max_divisor = 0x10;
+ if (divisor < 1 || divisor > max_divisor) {
+ rt_printk("%s: bug, invalid divisor=%i\n", __FUNCTION__,
+ divisor);
+ return 0;
+ }
+ return (divisor & 0xf) << 8;
+}
+static inline unsigned MSeries_PLL_Multiplier_Bits(unsigned multiplier)
+{
+ static const unsigned max_multiplier = 0x100;
+ if (multiplier < 1 || multiplier > max_multiplier) {
+ rt_printk("%s: bug, invalid multiplier=%i\n", __FUNCTION__,
+ multiplier);
+ return 0;
+ }
+ return multiplier & 0xff;
+}
+
+enum MSeries_PLL_Status {
+ MSeries_PLL_Locked_Bit = 0x1
+};
+
+enum MSeries_AI_Config_FIFO_Bypass_Bits {
+ MSeries_AI_Bypass_Channel_Mask = 0x7,
+ MSeries_AI_Bypass_Bank_Mask = 0x78,
+ MSeries_AI_Bypass_Cal_Sel_Pos_Mask = 0x380,
+ MSeries_AI_Bypass_Cal_Sel_Neg_Mask = 0x1c00,
+ MSeries_AI_Bypass_Mode_Mux_Mask = 0x6000,
+ MSeries_AO_Bypass_AO_Cal_Sel_Mask = 0x38000,
+ MSeries_AI_Bypass_Gain_Mask = 0x1c0000,
+ MSeries_AI_Bypass_Dither_Bit = 0x200000,
+ MSeries_AI_Bypass_Polarity_Bit = 0x400000, // 0 for 2's complement encoding
+ MSeries_AI_Bypass_Config_FIFO_Bit = 0x80000000
+};
+static inline unsigned MSeries_AI_Bypass_Cal_Sel_Pos_Bits(int
+ calibration_source)
+{
+ return (calibration_source << 7) & MSeries_AI_Bypass_Cal_Sel_Pos_Mask;
+}
+static inline unsigned MSeries_AI_Bypass_Cal_Sel_Neg_Bits(int
+ calibration_source)
+{
+ return (calibration_source << 10) & MSeries_AI_Bypass_Cal_Sel_Pos_Mask;
+}
+static inline unsigned MSeries_AI_Bypass_Gain_Bits(int gain)
+{
+ return (gain << 18) & MSeries_AI_Bypass_Gain_Mask;
+}
+
+enum MSeries_AO_Config_Bank_Bits {
+ MSeries_AO_DAC_Offset_Select_Mask = 0x7,
+ MSeries_AO_DAC_Offset_0V_Bits = 0x0,
+ MSeries_AO_DAC_Offset_5V_Bits = 0x1,
+ MSeries_AO_DAC_Reference_Mask = 0x38,
+ MSeries_AO_DAC_Reference_10V_Internal_Bits = 0x0,
+ MSeries_AO_DAC_Reference_5V_Internal_Bits = 0x8,
+ MSeries_AO_Update_Timed_Bit = 0x40,
+ MSeries_AO_Bipolar_Bit = 0x80 // turns on 2's complement encoding
+};
+
+enum MSeries_AO_Reference_Attenuation_Bits {
+ MSeries_Attenuate_x5_Bit = 0x1
+};
+
+static inline unsigned MSeries_Cal_PWM_High_Time_Bits(unsigned count)
+{
+ return (count << 16) & 0xffff0000;
+}
+
+static inline unsigned MSeries_Cal_PWM_Low_Time_Bits(unsigned count)
+{
+ return count & 0xffff;
+}
+
+static inline unsigned MSeries_PFI_Output_Select_Mask(unsigned channel)
+{
+ return 0x1f << (channel % 3) * 5;
+};
+static inline unsigned MSeries_PFI_Output_Select_Bits(unsigned channel,
+ unsigned source)
+{
+ return (source & 0x1f) << ((channel % 3) * 5);
+};
+
+// inverse to MSeries_PFI_Output_Select_Bits
+static inline unsigned MSeries_PFI_Output_Select_Source(unsigned channel,
+ unsigned bits)
+{
+ return (bits >> ((channel % 3) * 5)) & 0x1f;
+};
+
+enum MSeries_Gi_DMA_Config_Bits {
+ Gi_DMA_BankSW_Error_Bit = 0x10,
+ Gi_DMA_Reset_Bit = 0x8,
+ Gi_DMA_Int_Enable_Bit = 0x4,
+ Gi_DMA_Write_Bit = 0x2,
+ Gi_DMA_Enable_Bit = 0x1,
+};
+
+static inline unsigned MSeries_PFI_Filter_Select_Mask(unsigned channel)
+{
+ return 0x3 << (channel * 2);
+}
+static inline unsigned MSeries_PFI_Filter_Select_Bits(unsigned channel,
+ unsigned filter)
+{
+ return (filter << (channel *
+ 2)) & MSeries_PFI_Filter_Select_Mask(channel);
+}
+
+enum CDIO_DMA_Select_Bits {
+ CDI_DMA_Select_Shift = 0,
+ CDI_DMA_Select_Mask = 0xf,
+ CDO_DMA_Select_Shift = 4,
+ CDO_DMA_Select_Mask = 0xf << CDO_DMA_Select_Shift
+};
+
+enum CDIO_Status_Bits {
+ CDO_FIFO_Empty_Bit = 0x1,
+ CDO_FIFO_Full_Bit = 0x2,
+ CDO_FIFO_Request_Bit = 0x4,
+ CDO_Overrun_Bit = 0x8,
+ CDO_Underflow_Bit = 0x10,
+ CDI_FIFO_Empty_Bit = 0x10000,
+ CDI_FIFO_Full_Bit = 0x20000,
+ CDI_FIFO_Request_Bit = 0x40000,
+ CDI_Overrun_Bit = 0x80000,
+ CDI_Overflow_Bit = 0x100000
+};
+
+enum CDIO_Command_Bits {
+ CDO_Disarm_Bit = 0x1,
+ CDO_Arm_Bit = 0x2,
+ CDI_Disarm_Bit = 0x4,
+ CDI_Arm_Bit = 0x8,
+ CDO_Reset_Bit = 0x10,
+ CDI_Reset_Bit = 0x20,
+ CDO_Error_Interrupt_Enable_Set_Bit = 0x40,
+ CDO_Error_Interrupt_Enable_Clear_Bit = 0x80,
+ CDI_Error_Interrupt_Enable_Set_Bit = 0x100,
+ CDI_Error_Interrupt_Enable_Clear_Bit = 0x200,
+ CDO_FIFO_Request_Interrupt_Enable_Set_Bit = 0x400,
+ CDO_FIFO_Request_Interrupt_Enable_Clear_Bit = 0x800,
+ CDI_FIFO_Request_Interrupt_Enable_Set_Bit = 0x1000,
+ CDI_FIFO_Request_Interrupt_Enable_Clear_Bit = 0x2000,
+ CDO_Error_Interrupt_Confirm_Bit = 0x4000,
+ CDI_Error_Interrupt_Confirm_Bit = 0x8000,
+ CDO_Empty_FIFO_Interrupt_Enable_Set_Bit = 0x10000,
+ CDO_Empty_FIFO_Interrupt_Enable_Clear_Bit = 0x20000,
+ CDO_SW_Update_Bit = 0x80000,
+ CDI_SW_Update_Bit = 0x100000
+};
+
+enum CDI_Mode_Bits {
+ CDI_Sample_Source_Select_Mask = 0x3f,
+ CDI_Halt_On_Error_Bit = 0x200,
+ CDI_Polarity_Bit = 0x400, // sample clock on falling edge
+ CDI_FIFO_Mode_Bit = 0x800, // set for half full mode, clear for not empty mode
+ CDI_Data_Lane_Mask = 0x3000, // data lanes specify which dio channels map to byte or word accesses to the dio fifos
+ CDI_Data_Lane_0_15_Bits = 0x0,
+ CDI_Data_Lane_16_31_Bits = 0x1000,
+ CDI_Data_Lane_0_7_Bits = 0x0,
+ CDI_Data_Lane_8_15_Bits = 0x1000,
+ CDI_Data_Lane_16_23_Bits = 0x2000,
+ CDI_Data_Lane_24_31_Bits = 0x3000
+};
+
+enum CDO_Mode_Bits {
+ CDO_Sample_Source_Select_Mask = 0x3f,
+ CDO_Retransmit_Bit = 0x100,
+ CDO_Halt_On_Error_Bit = 0x200,
+ CDO_Polarity_Bit = 0x400, // sample clock on falling edge
+ CDO_FIFO_Mode_Bit = 0x800, // set for half full mode, clear for not full mode
+ CDO_Data_Lane_Mask = 0x3000, // data lanes specify which dio channels map to byte or word accesses to the dio fifos
+ CDO_Data_Lane_0_15_Bits = 0x0,
+ CDO_Data_Lane_16_31_Bits = 0x1000,
+ CDO_Data_Lane_0_7_Bits = 0x0,
+ CDO_Data_Lane_8_15_Bits = 0x1000,
+ CDO_Data_Lane_16_23_Bits = 0x2000,
+ CDO_Data_Lane_24_31_Bits = 0x3000
+};
+
+enum Interrupt_C_Enable_Bits {
+ Interrupt_Group_C_Enable_Bit = 0x1
+};
+
+enum Interrupt_C_Status_Bits {
+ Interrupt_Group_C_Status_Bit = 0x1
+};
+
+#define M_SERIES_EEPROM_SIZE 1024
+
+typedef struct ni_board_struct {
+ int device_id;
+ int isapnp_id;
+ char *name;
+
+ int n_adchan;
+ int adbits;
+
+ int ai_fifo_depth;
+ unsigned int alwaysdither:1;
+ int gainlkup;
+ int ai_speed;
+
+ int n_aochan;
+ int aobits;
+ int ao_fifo_depth;
+ const struct comedi_lrange *ao_range_table;
+ unsigned ao_speed;
+
+ unsigned num_p0_dio_channels;
+
+ int reg_type;
+ unsigned int ao_unipolar:1;
+ unsigned int has_8255:1;
+ unsigned int has_analog_trig:1;
+
+ enum caldac_enum caldac[3];
+} ni_board;
+
+#define n_ni_boards (sizeof(ni_boards)/sizeof(ni_board))
+
+#define boardtype (*(ni_board *)dev->board_ptr)
+
+#define MAX_N_AO_CHAN 8
+#define NUM_GPCT 2
+
+#define NI_PRIVATE_COMMON \
+ uint16_t (*stc_readw)(struct comedi_device *dev, int register); \
+ uint32_t (*stc_readl)(struct comedi_device *dev, int register); \
+ void (*stc_writew)(struct comedi_device *dev, uint16_t value, int register); \
+ void (*stc_writel)(struct comedi_device *dev, uint32_t value, int register); \
+ \
+ unsigned short dio_output; \
+ unsigned short dio_control; \
+ int ao0p,ao1p; \
+ int lastchan; \
+ int last_do; \
+ int rt_irq; \
+ int irqmask; \
+ int aimode; \
+ int ai_continuous; \
+ int blocksize; \
+ int n_left; \
+ unsigned int ai_calib_source; \
+ unsigned int ai_calib_source_enabled; \
+ spinlock_t window_lock; \
+ spinlock_t soft_reg_copy_lock; \
+ spinlock_t mite_channel_lock; \
+ \
+ int changain_state; \
+ unsigned int changain_spec; \
+ \
+ unsigned int caldac_maxdata_list[MAX_N_CALDACS]; \
+ unsigned short ao[MAX_N_AO_CHAN]; \
+ unsigned short caldacs[MAX_N_CALDACS]; \
+ \
+ unsigned short ai_cmd2; \
+ \
+ unsigned short ao_conf[MAX_N_AO_CHAN]; \
+ 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; \
+ \
+ struct ni_gpct_device *counter_dev; \
+ unsigned short an_trig_etc_reg; \
+ \
+ unsigned ai_offset[512]; \
+ \
+ unsigned long serial_interval_ns; \
+ unsigned char serial_hw_mode; \
+ unsigned short clock_and_fout; \
+ unsigned short clock_and_fout2; \
+ \
+ unsigned short int_a_enable_reg; \
+ unsigned short int_b_enable_reg; \
+ unsigned short io_bidirection_pin_reg; \
+ unsigned short rtsi_trig_direction_reg; \
+ unsigned short rtsi_trig_a_output_reg; \
+ unsigned short rtsi_trig_b_output_reg; \
+ unsigned short pfi_output_select_reg[NUM_PFI_OUTPUT_SELECT_REGS]; \
+ unsigned short ai_ao_select_reg; \
+ unsigned short g0_g1_select_reg; \
+ unsigned short cdio_dma_select_reg; \
+ \
+ unsigned clock_ns; \
+ unsigned clock_source; \
+ \
+ unsigned short atrig_mode; \
+ unsigned short atrig_high; \
+ unsigned short atrig_low; \
+ \
+ unsigned short pwm_up_count; \
+ unsigned short pwm_down_count; \
+ \
+ short ai_fifo_buffer[0x2000]; \
+ uint8_t eeprom_buffer[M_SERIES_EEPROM_SIZE]; \
+ uint32_t serial_number; \
+ \
+ struct mite_struct *mite; \
+ struct mite_channel *ai_mite_chan; \
+ struct mite_channel *ao_mite_chan;\
+ struct mite_channel *cdo_mite_chan;\
+ struct mite_dma_descriptor_ring *ai_mite_ring; \
+ struct mite_dma_descriptor_ring *ao_mite_ring; \
+ struct mite_dma_descriptor_ring *cdo_mite_ring; \
+ struct mite_dma_descriptor_ring *gpct_mite_ring[NUM_GPCT];
+
+#endif /* _COMEDI_NI_STC_H */
diff --git a/drivers/staging/comedi/drivers/ni_tio.c b/drivers/staging/comedi/drivers/ni_tio.c
new file mode 100644
index 000000000000..05a957540398
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_tio.c
@@ -0,0 +1,1691 @@
+/*
+ comedi/drivers/ni_tio.c
+ Support for NI general purpose counters
+
+ Copyright (C) 2006 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ 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.
+*/
+
+/*
+Driver: ni_tio
+Description: National Instruments general purpose counters
+Devices:
+Author: J.P. Mellor <jpmellor@rose-hulman.edu>,
+ Herman.Bruyninckx@mech.kuleuven.ac.be,
+ Wim.Meeussen@mech.kuleuven.ac.be,
+ Klaas.Gadeyne@mech.kuleuven.ac.be,
+ Frank Mori Hess <fmhess@users.sourceforge.net>
+Updated: Thu Nov 16 09:50:32 EST 2006
+Status: works
+
+This module is not used directly by end-users. Rather, it
+is used by other drivers (for example ni_660x and ni_pcimio)
+to provide support for NI's general purpose counters. It was
+originally based on the counter code from ni_660x.c and
+ni_mio_common.c.
+
+References:
+DAQ 660x Register-Level Programmer Manual (NI 370505A-01)
+DAQ 6601/6602 User Manual (NI 322137B-01)
+340934b.pdf DAQ-STC reference manual
+
+*/
+/*
+TODO:
+ Support use of both banks X and Y
+*/
+
+#include "ni_tio_internal.h"
+
+static uint64_t ni_tio_clock_period_ps(const struct ni_gpct *counter,
+ unsigned generic_clock_source);
+static unsigned ni_tio_generic_clock_src_select(const struct ni_gpct *counter);
+
+MODULE_AUTHOR("Comedi <comedi@comedi.org>");
+MODULE_DESCRIPTION("Comedi support for NI general-purpose counters");
+MODULE_LICENSE("GPL");
+
+static inline enum Gi_Counting_Mode_Reg_Bits Gi_Alternate_Sync_Bit(enum
+ ni_gpct_variant variant)
+{
+ switch (variant) {
+ case ni_gpct_variant_e_series:
+ return 0;
+ break;
+ case ni_gpct_variant_m_series:
+ return Gi_M_Series_Alternate_Sync_Bit;
+ break;
+ case ni_gpct_variant_660x:
+ return Gi_660x_Alternate_Sync_Bit;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+static inline enum Gi_Counting_Mode_Reg_Bits Gi_Prescale_X2_Bit(enum
+ ni_gpct_variant variant)
+{
+ switch (variant) {
+ case ni_gpct_variant_e_series:
+ return 0;
+ break;
+ case ni_gpct_variant_m_series:
+ return Gi_M_Series_Prescale_X2_Bit;
+ break;
+ case ni_gpct_variant_660x:
+ return Gi_660x_Prescale_X2_Bit;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+static inline enum Gi_Counting_Mode_Reg_Bits Gi_Prescale_X8_Bit(enum
+ ni_gpct_variant variant)
+{
+ switch (variant) {
+ case ni_gpct_variant_e_series:
+ return 0;
+ break;
+ case ni_gpct_variant_m_series:
+ return Gi_M_Series_Prescale_X8_Bit;
+ break;
+ case ni_gpct_variant_660x:
+ return Gi_660x_Prescale_X8_Bit;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+static inline enum Gi_Counting_Mode_Reg_Bits Gi_HW_Arm_Select_Mask(enum
+ ni_gpct_variant variant)
+{
+ switch (variant) {
+ case ni_gpct_variant_e_series:
+ return 0;
+ break;
+ case ni_gpct_variant_m_series:
+ return Gi_M_Series_HW_Arm_Select_Mask;
+ break;
+ case ni_gpct_variant_660x:
+ return Gi_660x_HW_Arm_Select_Mask;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+/* clock sources for ni_660x boards, get bits with Gi_Source_Select_Bits() */
+enum ni_660x_clock_source {
+ NI_660x_Timebase_1_Clock = 0x0, /* 20MHz */
+ NI_660x_Source_Pin_i_Clock = 0x1,
+ NI_660x_Next_Gate_Clock = 0xa,
+ NI_660x_Timebase_2_Clock = 0x12, /* 100KHz */
+ NI_660x_Next_TC_Clock = 0x13,
+ NI_660x_Timebase_3_Clock = 0x1e, /* 80MHz */
+ NI_660x_Logic_Low_Clock = 0x1f,
+};
+static const unsigned ni_660x_max_rtsi_channel = 6;
+static inline unsigned NI_660x_RTSI_Clock(unsigned n)
+{
+ BUG_ON(n > ni_660x_max_rtsi_channel);
+ return (0xb + n);
+}
+static const unsigned ni_660x_max_source_pin = 7;
+static inline unsigned NI_660x_Source_Pin_Clock(unsigned n)
+{
+ BUG_ON(n > ni_660x_max_source_pin);
+ return (0x2 + n);
+}
+
+/* clock sources for ni e and m series boards, get bits with Gi_Source_Select_Bits() */
+enum ni_m_series_clock_source {
+ NI_M_Series_Timebase_1_Clock = 0x0, /* 20MHz */
+ NI_M_Series_Timebase_2_Clock = 0x12, /* 100KHz */
+ NI_M_Series_Next_TC_Clock = 0x13,
+ NI_M_Series_Next_Gate_Clock = 0x14, /* when Gi_Src_SubSelect = 0 */
+ NI_M_Series_PXI_Star_Trigger_Clock = 0x14, /* when Gi_Src_SubSelect = 1 */
+ NI_M_Series_PXI10_Clock = 0x1d,
+ NI_M_Series_Timebase_3_Clock = 0x1e, /* 80MHz, when Gi_Src_SubSelect = 0 */
+ NI_M_Series_Analog_Trigger_Out_Clock = 0x1e, /* when Gi_Src_SubSelect = 1 */
+ NI_M_Series_Logic_Low_Clock = 0x1f,
+};
+static const unsigned ni_m_series_max_pfi_channel = 15;
+static inline unsigned NI_M_Series_PFI_Clock(unsigned n)
+{
+ BUG_ON(n > ni_m_series_max_pfi_channel);
+ if (n < 10)
+ return 1 + n;
+ else
+ return 0xb + n;
+}
+static const unsigned ni_m_series_max_rtsi_channel = 7;
+static inline unsigned NI_M_Series_RTSI_Clock(unsigned n)
+{
+ BUG_ON(n > ni_m_series_max_rtsi_channel);
+ if (n == 7)
+ return 0x1b;
+ else
+ return 0xb + n;
+}
+
+enum ni_660x_gate_select {
+ NI_660x_Source_Pin_i_Gate_Select = 0x0,
+ NI_660x_Gate_Pin_i_Gate_Select = 0x1,
+ NI_660x_Next_SRC_Gate_Select = 0xa,
+ NI_660x_Next_Out_Gate_Select = 0x14,
+ NI_660x_Logic_Low_Gate_Select = 0x1f,
+};
+static const unsigned ni_660x_max_gate_pin = 7;
+static inline unsigned NI_660x_Gate_Pin_Gate_Select(unsigned n)
+{
+ BUG_ON(n > ni_660x_max_gate_pin);
+ return 0x2 + n;
+}
+static inline unsigned NI_660x_RTSI_Gate_Select(unsigned n)
+{
+ BUG_ON(n > ni_660x_max_rtsi_channel);
+ return 0xb + n;
+}
+
+enum ni_m_series_gate_select {
+ NI_M_Series_Timestamp_Mux_Gate_Select = 0x0,
+ NI_M_Series_AI_START2_Gate_Select = 0x12,
+ NI_M_Series_PXI_Star_Trigger_Gate_Select = 0x13,
+ NI_M_Series_Next_Out_Gate_Select = 0x14,
+ NI_M_Series_AI_START1_Gate_Select = 0x1c,
+ NI_M_Series_Next_SRC_Gate_Select = 0x1d,
+ NI_M_Series_Analog_Trigger_Out_Gate_Select = 0x1e,
+ NI_M_Series_Logic_Low_Gate_Select = 0x1f,
+};
+static inline unsigned NI_M_Series_RTSI_Gate_Select(unsigned n)
+{
+ BUG_ON(n > ni_m_series_max_rtsi_channel);
+ if (n == 7)
+ return 0x1b;
+ return 0xb + n;
+}
+static inline unsigned NI_M_Series_PFI_Gate_Select(unsigned n)
+{
+ BUG_ON(n > ni_m_series_max_pfi_channel);
+ if (n < 10)
+ return 1 + n;
+ return 0xb + n;
+}
+
+static inline unsigned Gi_Source_Select_Bits(unsigned source)
+{
+ return (source << Gi_Source_Select_Shift) & Gi_Source_Select_Mask;
+}
+static inline unsigned Gi_Gate_Select_Bits(unsigned gate_select)
+{
+ return (gate_select << Gi_Gate_Select_Shift) & Gi_Gate_Select_Mask;
+}
+
+enum ni_660x_second_gate_select {
+ NI_660x_Source_Pin_i_Second_Gate_Select = 0x0,
+ NI_660x_Up_Down_Pin_i_Second_Gate_Select = 0x1,
+ NI_660x_Next_SRC_Second_Gate_Select = 0xa,
+ NI_660x_Next_Out_Second_Gate_Select = 0x14,
+ NI_660x_Selected_Gate_Second_Gate_Select = 0x1e,
+ NI_660x_Logic_Low_Second_Gate_Select = 0x1f,
+};
+static const unsigned ni_660x_max_up_down_pin = 7;
+static inline unsigned NI_660x_Up_Down_Pin_Second_Gate_Select(unsigned n)
+{
+ BUG_ON(n > ni_660x_max_up_down_pin);
+ return 0x2 + n;
+}
+static inline unsigned NI_660x_RTSI_Second_Gate_Select(unsigned n)
+{
+ BUG_ON(n > ni_660x_max_rtsi_channel);
+ return 0xb + n;
+}
+
+static const unsigned int counter_status_mask =
+ COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING;
+
+static int __init ni_tio_init_module(void)
+{
+ return 0;
+}
+
+module_init(ni_tio_init_module);
+
+static void __exit ni_tio_cleanup_module(void)
+{
+}
+
+module_exit(ni_tio_cleanup_module);
+
+struct ni_gpct_device *ni_gpct_device_construct(struct comedi_device * dev,
+ void (*write_register) (struct ni_gpct * counter, unsigned bits,
+ enum ni_gpct_register reg),
+ unsigned (*read_register) (struct ni_gpct * counter,
+ enum ni_gpct_register reg), enum ni_gpct_variant variant,
+ unsigned num_counters)
+{
+ unsigned i;
+
+ struct ni_gpct_device *counter_dev =
+ kzalloc(sizeof(struct ni_gpct_device), GFP_KERNEL);
+ if (counter_dev == NULL)
+ return NULL;
+ counter_dev->dev = dev;
+ counter_dev->write_register = write_register;
+ counter_dev->read_register = read_register;
+ counter_dev->variant = variant;
+ spin_lock_init(&counter_dev->regs_lock);
+ BUG_ON(num_counters == 0);
+ counter_dev->counters =
+ kzalloc(sizeof(struct ni_gpct) * num_counters, GFP_KERNEL);
+ if (counter_dev->counters == NULL) {
+ kfree(counter_dev);
+ return NULL;
+ }
+ for (i = 0; i < num_counters; ++i) {
+ counter_dev->counters[i].counter_dev = counter_dev;
+ spin_lock_init(&counter_dev->counters[i].lock);
+ }
+ counter_dev->num_counters = num_counters;
+ return counter_dev;
+}
+
+void ni_gpct_device_destroy(struct ni_gpct_device *counter_dev)
+{
+ if (counter_dev->counters == NULL)
+ return;
+ kfree(counter_dev->counters);
+ kfree(counter_dev);
+}
+
+static int ni_tio_second_gate_registers_present(const struct ni_gpct_device
+ *counter_dev)
+{
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_e_series:
+ return 0;
+ break;
+ case ni_gpct_variant_m_series:
+ case ni_gpct_variant_660x:
+ return 1;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static void ni_tio_reset_count_and_disarm(struct ni_gpct *counter)
+{
+ write_register(counter, Gi_Reset_Bit(counter->counter_index),
+ NITIO_Gxx_Joint_Reset_Reg(counter->counter_index));
+}
+
+void ni_tio_init_counter(struct ni_gpct *counter)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+
+ ni_tio_reset_count_and_disarm(counter);
+ /* initialize counter registers */
+ counter_dev->regs[NITIO_Gi_Autoincrement_Reg(counter->counter_index)] =
+ 0x0;
+ write_register(counter,
+ counter_dev->regs[NITIO_Gi_Autoincrement_Reg(counter->
+ counter_index)],
+ NITIO_Gi_Autoincrement_Reg(counter->counter_index));
+ ni_tio_set_bits(counter, NITIO_Gi_Command_Reg(counter->counter_index),
+ ~0, Gi_Synchronize_Gate_Bit);
+ ni_tio_set_bits(counter, NITIO_Gi_Mode_Reg(counter->counter_index), ~0,
+ 0);
+ counter_dev->regs[NITIO_Gi_LoadA_Reg(counter->counter_index)] = 0x0;
+ write_register(counter,
+ counter_dev->regs[NITIO_Gi_LoadA_Reg(counter->counter_index)],
+ NITIO_Gi_LoadA_Reg(counter->counter_index));
+ counter_dev->regs[NITIO_Gi_LoadB_Reg(counter->counter_index)] = 0x0;
+ write_register(counter,
+ counter_dev->regs[NITIO_Gi_LoadB_Reg(counter->counter_index)],
+ NITIO_Gi_LoadB_Reg(counter->counter_index));
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Input_Select_Reg(counter->counter_index), ~0, 0);
+ if (ni_tio_counting_mode_registers_present(counter_dev)) {
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Counting_Mode_Reg(counter->counter_index), ~0,
+ 0);
+ }
+ if (ni_tio_second_gate_registers_present(counter_dev)) {
+ counter_dev->regs[NITIO_Gi_Second_Gate_Reg(counter->
+ counter_index)] = 0x0;
+ write_register(counter,
+ counter_dev->regs[NITIO_Gi_Second_Gate_Reg(counter->
+ counter_index)],
+ NITIO_Gi_Second_Gate_Reg(counter->counter_index));
+ }
+ ni_tio_set_bits(counter,
+ NITIO_Gi_DMA_Config_Reg(counter->counter_index), ~0, 0x0);
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Interrupt_Enable_Reg(counter->counter_index), ~0, 0x0);
+}
+
+static unsigned int ni_tio_counter_status(struct ni_gpct *counter)
+{
+ unsigned int status = 0;
+ const unsigned bits = read_register(counter,
+ NITIO_Gxx_Status_Reg(counter->counter_index));
+ if (bits & Gi_Armed_Bit(counter->counter_index)) {
+ status |= COMEDI_COUNTER_ARMED;
+ if (bits & Gi_Counting_Bit(counter->counter_index))
+ status |= COMEDI_COUNTER_COUNTING;
+ }
+ return status;
+}
+
+static void ni_tio_set_sync_mode(struct ni_gpct *counter, int force_alt_sync)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ const unsigned counting_mode_reg =
+ NITIO_Gi_Counting_Mode_Reg(counter->counter_index);
+ static const uint64_t min_normal_sync_period_ps = 25000;
+ const uint64_t clock_period_ps = ni_tio_clock_period_ps(counter,
+ ni_tio_generic_clock_src_select(counter));
+
+ if (ni_tio_counting_mode_registers_present(counter_dev) == 0)
+ return;
+
+ switch (ni_tio_get_soft_copy(counter,
+ counting_mode_reg) & Gi_Counting_Mode_Mask) {
+ case Gi_Counting_Mode_QuadratureX1_Bits:
+ case Gi_Counting_Mode_QuadratureX2_Bits:
+ case Gi_Counting_Mode_QuadratureX4_Bits:
+ case Gi_Counting_Mode_Sync_Source_Bits:
+ force_alt_sync = 1;
+ break;
+ default:
+ break;
+ }
+ /* It's not clear what we should do if clock_period is unknown, so we are not
+ using the alt sync bit in that case, but allow the caller to decide by using the
+ force_alt_sync parameter. */
+ if (force_alt_sync ||
+ (clock_period_ps
+ && clock_period_ps < min_normal_sync_period_ps)) {
+ ni_tio_set_bits(counter, counting_mode_reg,
+ Gi_Alternate_Sync_Bit(counter_dev->variant),
+ Gi_Alternate_Sync_Bit(counter_dev->variant));
+ } else {
+ ni_tio_set_bits(counter, counting_mode_reg,
+ Gi_Alternate_Sync_Bit(counter_dev->variant), 0x0);
+ }
+}
+
+static int ni_tio_set_counter_mode(struct ni_gpct *counter, unsigned mode)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned mode_reg_mask;
+ unsigned mode_reg_values;
+ unsigned input_select_bits = 0;
+ /* these bits map directly on to the mode register */
+ static const unsigned mode_reg_direct_mask =
+ NI_GPCT_GATE_ON_BOTH_EDGES_BIT | NI_GPCT_EDGE_GATE_MODE_MASK |
+ NI_GPCT_STOP_MODE_MASK | NI_GPCT_OUTPUT_MODE_MASK |
+ NI_GPCT_HARDWARE_DISARM_MASK | NI_GPCT_LOADING_ON_TC_BIT |
+ NI_GPCT_LOADING_ON_GATE_BIT | NI_GPCT_LOAD_B_SELECT_BIT;
+
+ mode_reg_mask = mode_reg_direct_mask | Gi_Reload_Source_Switching_Bit;
+ mode_reg_values = mode & mode_reg_direct_mask;
+ switch (mode & NI_GPCT_RELOAD_SOURCE_MASK) {
+ case NI_GPCT_RELOAD_SOURCE_FIXED_BITS:
+ break;
+ case NI_GPCT_RELOAD_SOURCE_SWITCHING_BITS:
+ mode_reg_values |= Gi_Reload_Source_Switching_Bit;
+ break;
+ case NI_GPCT_RELOAD_SOURCE_GATE_SELECT_BITS:
+ input_select_bits |= Gi_Gate_Select_Load_Source_Bit;
+ mode_reg_mask |= Gi_Gating_Mode_Mask;
+ mode_reg_values |= Gi_Level_Gating_Bits;
+ break;
+ default:
+ break;
+ }
+ ni_tio_set_bits(counter, NITIO_Gi_Mode_Reg(counter->counter_index),
+ mode_reg_mask, mode_reg_values);
+
+ if (ni_tio_counting_mode_registers_present(counter_dev)) {
+ unsigned counting_mode_bits = 0;
+ counting_mode_bits |=
+ (mode >> NI_GPCT_COUNTING_MODE_SHIFT) &
+ Gi_Counting_Mode_Mask;
+ counting_mode_bits |=
+ ((mode >> NI_GPCT_INDEX_PHASE_BITSHIFT) <<
+ Gi_Index_Phase_Bitshift) & Gi_Index_Phase_Mask;
+ if (mode & NI_GPCT_INDEX_ENABLE_BIT) {
+ counting_mode_bits |= Gi_Index_Mode_Bit;
+ }
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Counting_Mode_Reg(counter->counter_index),
+ Gi_Counting_Mode_Mask | Gi_Index_Phase_Mask |
+ Gi_Index_Mode_Bit, counting_mode_bits);
+ ni_tio_set_sync_mode(counter, 0);
+ }
+
+ ni_tio_set_bits(counter, NITIO_Gi_Command_Reg(counter->counter_index),
+ Gi_Up_Down_Mask,
+ (mode >> NI_GPCT_COUNTING_DIRECTION_SHIFT) << Gi_Up_Down_Shift);
+
+ if (mode & NI_GPCT_OR_GATE_BIT) {
+ input_select_bits |= Gi_Or_Gate_Bit;
+ }
+ if (mode & NI_GPCT_INVERT_OUTPUT_BIT) {
+ input_select_bits |= Gi_Output_Polarity_Bit;
+ }
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Input_Select_Reg(counter->counter_index),
+ Gi_Gate_Select_Load_Source_Bit | Gi_Or_Gate_Bit |
+ Gi_Output_Polarity_Bit, input_select_bits);
+
+ return 0;
+}
+
+int ni_tio_arm(struct ni_gpct *counter, int arm, unsigned start_trigger)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+
+ unsigned command_transient_bits = 0;
+
+ if (arm) {
+ switch (start_trigger) {
+ case NI_GPCT_ARM_IMMEDIATE:
+ command_transient_bits |= Gi_Arm_Bit;
+ break;
+ case NI_GPCT_ARM_PAIRED_IMMEDIATE:
+ command_transient_bits |= Gi_Arm_Bit | Gi_Arm_Copy_Bit;
+ break;
+ default:
+ break;
+ }
+ if (ni_tio_counting_mode_registers_present(counter_dev)) {
+ unsigned counting_mode_bits = 0;
+
+ switch (start_trigger) {
+ case NI_GPCT_ARM_IMMEDIATE:
+ case NI_GPCT_ARM_PAIRED_IMMEDIATE:
+ break;
+ default:
+ if (start_trigger & NI_GPCT_ARM_UNKNOWN) {
+ /* pass-through the least significant bits so we can figure out what select later */
+ unsigned hw_arm_select_bits =
+ (start_trigger <<
+ Gi_HW_Arm_Select_Shift) &
+ Gi_HW_Arm_Select_Mask
+ (counter_dev->variant);
+
+ counting_mode_bits |=
+ Gi_HW_Arm_Enable_Bit |
+ hw_arm_select_bits;
+ } else {
+ return -EINVAL;
+ }
+ break;
+ }
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Counting_Mode_Reg(counter->
+ counter_index),
+ Gi_HW_Arm_Select_Mask(counter_dev->
+ variant) | Gi_HW_Arm_Enable_Bit,
+ counting_mode_bits);
+ }
+ } else {
+ command_transient_bits |= Gi_Disarm_Bit;
+ }
+ ni_tio_set_bits_transient(counter,
+ NITIO_Gi_Command_Reg(counter->counter_index), 0, 0,
+ command_transient_bits);
+ return 0;
+}
+
+static unsigned ni_660x_source_select_bits(unsigned int clock_source)
+{
+ unsigned ni_660x_clock;
+ unsigned i;
+ const unsigned clock_select_bits =
+ clock_source & NI_GPCT_CLOCK_SRC_SELECT_MASK;
+
+ switch (clock_select_bits) {
+ case NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS:
+ ni_660x_clock = NI_660x_Timebase_1_Clock;
+ break;
+ case NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS:
+ ni_660x_clock = NI_660x_Timebase_2_Clock;
+ break;
+ case NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS:
+ ni_660x_clock = NI_660x_Timebase_3_Clock;
+ break;
+ case NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS:
+ ni_660x_clock = NI_660x_Logic_Low_Clock;
+ break;
+ case NI_GPCT_SOURCE_PIN_i_CLOCK_SRC_BITS:
+ ni_660x_clock = NI_660x_Source_Pin_i_Clock;
+ break;
+ case NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS:
+ ni_660x_clock = NI_660x_Next_Gate_Clock;
+ break;
+ case NI_GPCT_NEXT_TC_CLOCK_SRC_BITS:
+ ni_660x_clock = NI_660x_Next_TC_Clock;
+ break;
+ default:
+ for (i = 0; i <= ni_660x_max_rtsi_channel; ++i) {
+ if (clock_select_bits == NI_GPCT_RTSI_CLOCK_SRC_BITS(i)) {
+ ni_660x_clock = NI_660x_RTSI_Clock(i);
+ break;
+ }
+ }
+ if (i <= ni_660x_max_rtsi_channel)
+ break;
+ for (i = 0; i <= ni_660x_max_source_pin; ++i) {
+ if (clock_select_bits ==
+ NI_GPCT_SOURCE_PIN_CLOCK_SRC_BITS(i)) {
+ ni_660x_clock = NI_660x_Source_Pin_Clock(i);
+ break;
+ }
+ }
+ if (i <= ni_660x_max_source_pin)
+ break;
+ ni_660x_clock = 0;
+ BUG();
+ break;
+ }
+ return Gi_Source_Select_Bits(ni_660x_clock);
+}
+
+static unsigned ni_m_series_source_select_bits(unsigned int clock_source)
+{
+ unsigned ni_m_series_clock;
+ unsigned i;
+ const unsigned clock_select_bits =
+ clock_source & NI_GPCT_CLOCK_SRC_SELECT_MASK;
+ switch (clock_select_bits) {
+ case NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_Series_Timebase_1_Clock;
+ break;
+ case NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_Series_Timebase_2_Clock;
+ break;
+ case NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_Series_Timebase_3_Clock;
+ break;
+ case NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_Series_Logic_Low_Clock;
+ break;
+ case NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_Series_Next_Gate_Clock;
+ break;
+ case NI_GPCT_NEXT_TC_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_Series_Next_TC_Clock;
+ break;
+ case NI_GPCT_PXI10_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_Series_PXI10_Clock;
+ break;
+ case NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_Series_PXI_Star_Trigger_Clock;
+ break;
+ case NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_Series_Analog_Trigger_Out_Clock;
+ break;
+ default:
+ for (i = 0; i <= ni_m_series_max_rtsi_channel; ++i) {
+ if (clock_select_bits == NI_GPCT_RTSI_CLOCK_SRC_BITS(i)) {
+ ni_m_series_clock = NI_M_Series_RTSI_Clock(i);
+ break;
+ }
+ }
+ if (i <= ni_m_series_max_rtsi_channel)
+ break;
+ for (i = 0; i <= ni_m_series_max_pfi_channel; ++i) {
+ if (clock_select_bits == NI_GPCT_PFI_CLOCK_SRC_BITS(i)) {
+ ni_m_series_clock = NI_M_Series_PFI_Clock(i);
+ break;
+ }
+ }
+ if (i <= ni_m_series_max_pfi_channel)
+ break;
+ rt_printk("invalid clock source 0x%lx\n",
+ (unsigned long)clock_source);
+ BUG();
+ ni_m_series_clock = 0;
+ break;
+ }
+ return Gi_Source_Select_Bits(ni_m_series_clock);
+};
+
+static void ni_tio_set_source_subselect(struct ni_gpct *counter,
+ unsigned int clock_source)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ const unsigned second_gate_reg =
+ NITIO_Gi_Second_Gate_Reg(counter->counter_index);
+
+ if (counter_dev->variant != ni_gpct_variant_m_series)
+ return;
+ switch (clock_source & NI_GPCT_CLOCK_SRC_SELECT_MASK) {
+ /* Gi_Source_Subselect is zero */
+ case NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS:
+ case NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS:
+ counter_dev->regs[second_gate_reg] &= ~Gi_Source_Subselect_Bit;
+ break;
+ /* Gi_Source_Subselect is one */
+ case NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS:
+ case NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS:
+ counter_dev->regs[second_gate_reg] |= Gi_Source_Subselect_Bit;
+ break;
+ /* Gi_Source_Subselect doesn't matter */
+ default:
+ return;
+ break;
+ }
+ write_register(counter, counter_dev->regs[second_gate_reg],
+ second_gate_reg);
+}
+
+static int ni_tio_set_clock_src(struct ni_gpct *counter,
+ unsigned int clock_source, unsigned int period_ns)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned input_select_bits = 0;
+ static const uint64_t pico_per_nano = 1000;
+
+/*FIXME: validate clock source */
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_660x:
+ input_select_bits |= ni_660x_source_select_bits(clock_source);
+ break;
+ case ni_gpct_variant_e_series:
+ case ni_gpct_variant_m_series:
+ input_select_bits |=
+ ni_m_series_source_select_bits(clock_source);
+ break;
+ default:
+ BUG();
+ break;
+ }
+ if (clock_source & NI_GPCT_INVERT_CLOCK_SRC_BIT)
+ input_select_bits |= Gi_Source_Polarity_Bit;
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Input_Select_Reg(counter->counter_index),
+ Gi_Source_Select_Mask | Gi_Source_Polarity_Bit,
+ input_select_bits);
+ ni_tio_set_source_subselect(counter, clock_source);
+ if (ni_tio_counting_mode_registers_present(counter_dev)) {
+ const unsigned prescaling_mode =
+ clock_source & NI_GPCT_PRESCALE_MODE_CLOCK_SRC_MASK;
+ unsigned counting_mode_bits = 0;
+
+ switch (prescaling_mode) {
+ case NI_GPCT_NO_PRESCALE_CLOCK_SRC_BITS:
+ break;
+ case NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS:
+ counting_mode_bits |=
+ Gi_Prescale_X2_Bit(counter_dev->variant);
+ break;
+ case NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS:
+ counting_mode_bits |=
+ Gi_Prescale_X8_Bit(counter_dev->variant);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Counting_Mode_Reg(counter->counter_index),
+ Gi_Prescale_X2_Bit(counter_dev->
+ variant) | Gi_Prescale_X8_Bit(counter_dev->
+ variant), counting_mode_bits);
+ }
+ counter->clock_period_ps = pico_per_nano * period_ns;
+ ni_tio_set_sync_mode(counter, 0);
+ return 0;
+}
+
+static unsigned ni_tio_clock_src_modifiers(const struct ni_gpct *counter)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ const unsigned counting_mode_bits = ni_tio_get_soft_copy(counter,
+ NITIO_Gi_Counting_Mode_Reg(counter->counter_index));
+ unsigned bits = 0;
+
+ if (ni_tio_get_soft_copy(counter,
+ NITIO_Gi_Input_Select_Reg(counter->
+ counter_index)) & Gi_Source_Polarity_Bit)
+ bits |= NI_GPCT_INVERT_CLOCK_SRC_BIT;
+ if (counting_mode_bits & Gi_Prescale_X2_Bit(counter_dev->variant))
+ bits |= NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS;
+ if (counting_mode_bits & Gi_Prescale_X8_Bit(counter_dev->variant))
+ bits |= NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS;
+ return bits;
+}
+
+static unsigned ni_m_series_clock_src_select(const struct ni_gpct *counter)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ const unsigned second_gate_reg =
+ NITIO_Gi_Second_Gate_Reg(counter->counter_index);
+ unsigned clock_source = 0;
+ unsigned i;
+ const unsigned input_select = (ni_tio_get_soft_copy(counter,
+ NITIO_Gi_Input_Select_Reg(counter->
+ counter_index)) & Gi_Source_Select_Mask) >>
+ Gi_Source_Select_Shift;
+
+ switch (input_select) {
+ case NI_M_Series_Timebase_1_Clock:
+ clock_source = NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS;
+ break;
+ case NI_M_Series_Timebase_2_Clock:
+ clock_source = NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS;
+ break;
+ case NI_M_Series_Timebase_3_Clock:
+ if (counter_dev->
+ regs[second_gate_reg] & Gi_Source_Subselect_Bit)
+ clock_source =
+ NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS;
+ else
+ clock_source = NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS;
+ break;
+ case NI_M_Series_Logic_Low_Clock:
+ clock_source = NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS;
+ break;
+ case NI_M_Series_Next_Gate_Clock:
+ if (counter_dev->
+ regs[second_gate_reg] & Gi_Source_Subselect_Bit)
+ clock_source = NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS;
+ else
+ clock_source = NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS;
+ break;
+ case NI_M_Series_PXI10_Clock:
+ clock_source = NI_GPCT_PXI10_CLOCK_SRC_BITS;
+ break;
+ case NI_M_Series_Next_TC_Clock:
+ clock_source = NI_GPCT_NEXT_TC_CLOCK_SRC_BITS;
+ break;
+ default:
+ for (i = 0; i <= ni_m_series_max_rtsi_channel; ++i) {
+ if (input_select == NI_M_Series_RTSI_Clock(i)) {
+ clock_source = NI_GPCT_RTSI_CLOCK_SRC_BITS(i);
+ break;
+ }
+ }
+ if (i <= ni_m_series_max_rtsi_channel)
+ break;
+ for (i = 0; i <= ni_m_series_max_pfi_channel; ++i) {
+ if (input_select == NI_M_Series_PFI_Clock(i)) {
+ clock_source = NI_GPCT_PFI_CLOCK_SRC_BITS(i);
+ break;
+ }
+ }
+ if (i <= ni_m_series_max_pfi_channel)
+ break;
+ BUG();
+ break;
+ }
+ clock_source |= ni_tio_clock_src_modifiers(counter);
+ return clock_source;
+}
+
+static unsigned ni_660x_clock_src_select(const struct ni_gpct *counter)
+{
+ unsigned clock_source = 0;
+ unsigned i;
+ const unsigned input_select = (ni_tio_get_soft_copy(counter,
+ NITIO_Gi_Input_Select_Reg(counter->
+ counter_index)) & Gi_Source_Select_Mask) >>
+ Gi_Source_Select_Shift;
+
+ switch (input_select) {
+ case NI_660x_Timebase_1_Clock:
+ clock_source = NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS;
+ break;
+ case NI_660x_Timebase_2_Clock:
+ clock_source = NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS;
+ break;
+ case NI_660x_Timebase_3_Clock:
+ clock_source = NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS;
+ break;
+ case NI_660x_Logic_Low_Clock:
+ clock_source = NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS;
+ break;
+ case NI_660x_Source_Pin_i_Clock:
+ clock_source = NI_GPCT_SOURCE_PIN_i_CLOCK_SRC_BITS;
+ break;
+ case NI_660x_Next_Gate_Clock:
+ clock_source = NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS;
+ break;
+ case NI_660x_Next_TC_Clock:
+ clock_source = NI_GPCT_NEXT_TC_CLOCK_SRC_BITS;
+ break;
+ default:
+ for (i = 0; i <= ni_660x_max_rtsi_channel; ++i) {
+ if (input_select == NI_660x_RTSI_Clock(i)) {
+ clock_source = NI_GPCT_RTSI_CLOCK_SRC_BITS(i);
+ break;
+ }
+ }
+ if (i <= ni_660x_max_rtsi_channel)
+ break;
+ for (i = 0; i <= ni_660x_max_source_pin; ++i) {
+ if (input_select == NI_660x_Source_Pin_Clock(i)) {
+ clock_source =
+ NI_GPCT_SOURCE_PIN_CLOCK_SRC_BITS(i);
+ break;
+ }
+ }
+ if (i <= ni_660x_max_source_pin)
+ break;
+ BUG();
+ break;
+ }
+ clock_source |= ni_tio_clock_src_modifiers(counter);
+ return clock_source;
+}
+
+static unsigned ni_tio_generic_clock_src_select(const struct ni_gpct *counter)
+{
+ switch (counter->counter_dev->variant) {
+ case ni_gpct_variant_e_series:
+ case ni_gpct_variant_m_series:
+ return ni_m_series_clock_src_select(counter);
+ break;
+ case ni_gpct_variant_660x:
+ return ni_660x_clock_src_select(counter);
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static uint64_t ni_tio_clock_period_ps(const struct ni_gpct *counter,
+ unsigned generic_clock_source)
+{
+ uint64_t clock_period_ps;
+
+ switch (generic_clock_source & NI_GPCT_CLOCK_SRC_SELECT_MASK) {
+ case NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS:
+ clock_period_ps = 50000;
+ break;
+ case NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS:
+ clock_period_ps = 10000000;
+ break;
+ case NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS:
+ clock_period_ps = 12500;
+ break;
+ case NI_GPCT_PXI10_CLOCK_SRC_BITS:
+ clock_period_ps = 100000;
+ break;
+ default:
+ /* clock period is specified by user with prescaling already taken into account. */
+ return counter->clock_period_ps;
+ break;
+ }
+
+ switch (generic_clock_source & NI_GPCT_PRESCALE_MODE_CLOCK_SRC_MASK) {
+ case NI_GPCT_NO_PRESCALE_CLOCK_SRC_BITS:
+ break;
+ case NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS:
+ clock_period_ps *= 2;
+ break;
+ case NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS:
+ clock_period_ps *= 8;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return clock_period_ps;
+}
+
+static void ni_tio_get_clock_src(struct ni_gpct *counter,
+ unsigned int * clock_source, unsigned int * period_ns)
+{
+ static const unsigned pico_per_nano = 1000;
+ uint64_t temp64;
+ *clock_source = ni_tio_generic_clock_src_select(counter);
+ temp64 = ni_tio_clock_period_ps(counter, *clock_source);
+ do_div(temp64, pico_per_nano);
+ *period_ns = temp64;
+}
+
+static void ni_tio_set_first_gate_modifiers(struct ni_gpct *counter,
+ unsigned int gate_source)
+{
+ const unsigned mode_mask = Gi_Gate_Polarity_Bit | Gi_Gating_Mode_Mask;
+ unsigned mode_values = 0;
+
+ if (gate_source & CR_INVERT) {
+ mode_values |= Gi_Gate_Polarity_Bit;
+ }
+ if (gate_source & CR_EDGE) {
+ mode_values |= Gi_Rising_Edge_Gating_Bits;
+ } else {
+ mode_values |= Gi_Level_Gating_Bits;
+ }
+ ni_tio_set_bits(counter, NITIO_Gi_Mode_Reg(counter->counter_index),
+ mode_mask, mode_values);
+}
+
+static int ni_660x_set_first_gate(struct ni_gpct *counter, unsigned int gate_source)
+{
+ const unsigned selected_gate = CR_CHAN(gate_source);
+ /* bits of selected_gate that may be meaningful to input select register */
+ const unsigned selected_gate_mask = 0x1f;
+ unsigned ni_660x_gate_select;
+ unsigned i;
+
+ switch (selected_gate) {
+ case NI_GPCT_NEXT_SOURCE_GATE_SELECT:
+ ni_660x_gate_select = NI_660x_Next_SRC_Gate_Select;
+ break;
+ case NI_GPCT_NEXT_OUT_GATE_SELECT:
+ case NI_GPCT_LOGIC_LOW_GATE_SELECT:
+ case NI_GPCT_SOURCE_PIN_i_GATE_SELECT:
+ case NI_GPCT_GATE_PIN_i_GATE_SELECT:
+ ni_660x_gate_select = selected_gate & selected_gate_mask;
+ break;
+ default:
+ for (i = 0; i <= ni_660x_max_rtsi_channel; ++i) {
+ if (selected_gate == NI_GPCT_RTSI_GATE_SELECT(i)) {
+ ni_660x_gate_select =
+ selected_gate & selected_gate_mask;
+ break;
+ }
+ }
+ if (i <= ni_660x_max_rtsi_channel)
+ break;
+ for (i = 0; i <= ni_660x_max_gate_pin; ++i) {
+ if (selected_gate == NI_GPCT_GATE_PIN_GATE_SELECT(i)) {
+ ni_660x_gate_select =
+ selected_gate & selected_gate_mask;
+ break;
+ }
+ }
+ if (i <= ni_660x_max_gate_pin)
+ break;
+ return -EINVAL;
+ break;
+ }
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Input_Select_Reg(counter->counter_index),
+ Gi_Gate_Select_Mask, Gi_Gate_Select_Bits(ni_660x_gate_select));
+ return 0;
+}
+
+static int ni_m_series_set_first_gate(struct ni_gpct *counter,
+ unsigned int gate_source)
+{
+ const unsigned selected_gate = CR_CHAN(gate_source);
+ /* bits of selected_gate that may be meaningful to input select register */
+ const unsigned selected_gate_mask = 0x1f;
+ unsigned ni_m_series_gate_select;
+ unsigned i;
+
+ switch (selected_gate) {
+ case NI_GPCT_TIMESTAMP_MUX_GATE_SELECT:
+ case NI_GPCT_AI_START2_GATE_SELECT:
+ case NI_GPCT_PXI_STAR_TRIGGER_GATE_SELECT:
+ case NI_GPCT_NEXT_OUT_GATE_SELECT:
+ case NI_GPCT_AI_START1_GATE_SELECT:
+ case NI_GPCT_NEXT_SOURCE_GATE_SELECT:
+ case NI_GPCT_ANALOG_TRIGGER_OUT_GATE_SELECT:
+ case NI_GPCT_LOGIC_LOW_GATE_SELECT:
+ ni_m_series_gate_select = selected_gate & selected_gate_mask;
+ break;
+ default:
+ for (i = 0; i <= ni_m_series_max_rtsi_channel; ++i) {
+ if (selected_gate == NI_GPCT_RTSI_GATE_SELECT(i)) {
+ ni_m_series_gate_select =
+ selected_gate & selected_gate_mask;
+ break;
+ }
+ }
+ if (i <= ni_m_series_max_rtsi_channel)
+ break;
+ for (i = 0; i <= ni_m_series_max_pfi_channel; ++i) {
+ if (selected_gate == NI_GPCT_PFI_GATE_SELECT(i)) {
+ ni_m_series_gate_select =
+ selected_gate & selected_gate_mask;
+ break;
+ }
+ }
+ if (i <= ni_m_series_max_pfi_channel)
+ break;
+ return -EINVAL;
+ break;
+ }
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Input_Select_Reg(counter->counter_index),
+ Gi_Gate_Select_Mask,
+ Gi_Gate_Select_Bits(ni_m_series_gate_select));
+ return 0;
+}
+
+static int ni_660x_set_second_gate(struct ni_gpct *counter,
+ unsigned int gate_source)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ const unsigned second_gate_reg =
+ NITIO_Gi_Second_Gate_Reg(counter->counter_index);
+ const unsigned selected_second_gate = CR_CHAN(gate_source);
+ /* bits of second_gate that may be meaningful to second gate register */
+ static const unsigned selected_second_gate_mask = 0x1f;
+ unsigned ni_660x_second_gate_select;
+ unsigned i;
+
+ switch (selected_second_gate) {
+ case NI_GPCT_SOURCE_PIN_i_GATE_SELECT:
+ case NI_GPCT_UP_DOWN_PIN_i_GATE_SELECT:
+ case NI_GPCT_SELECTED_GATE_GATE_SELECT:
+ case NI_GPCT_NEXT_OUT_GATE_SELECT:
+ case NI_GPCT_LOGIC_LOW_GATE_SELECT:
+ ni_660x_second_gate_select =
+ selected_second_gate & selected_second_gate_mask;
+ break;
+ case NI_GPCT_NEXT_SOURCE_GATE_SELECT:
+ ni_660x_second_gate_select =
+ NI_660x_Next_SRC_Second_Gate_Select;
+ break;
+ default:
+ for (i = 0; i <= ni_660x_max_rtsi_channel; ++i) {
+ if (selected_second_gate == NI_GPCT_RTSI_GATE_SELECT(i)) {
+ ni_660x_second_gate_select =
+ selected_second_gate &
+ selected_second_gate_mask;
+ break;
+ }
+ }
+ if (i <= ni_660x_max_rtsi_channel)
+ break;
+ for (i = 0; i <= ni_660x_max_up_down_pin; ++i) {
+ if (selected_second_gate ==
+ NI_GPCT_UP_DOWN_PIN_GATE_SELECT(i)) {
+ ni_660x_second_gate_select =
+ selected_second_gate &
+ selected_second_gate_mask;
+ break;
+ }
+ }
+ if (i <= ni_660x_max_up_down_pin)
+ break;
+ return -EINVAL;
+ break;
+ };
+ counter_dev->regs[second_gate_reg] |= Gi_Second_Gate_Mode_Bit;
+ counter_dev->regs[second_gate_reg] &= ~Gi_Second_Gate_Select_Mask;
+ counter_dev->regs[second_gate_reg] |=
+ Gi_Second_Gate_Select_Bits(ni_660x_second_gate_select);
+ write_register(counter, counter_dev->regs[second_gate_reg],
+ second_gate_reg);
+ return 0;
+}
+
+static int ni_m_series_set_second_gate(struct ni_gpct *counter,
+ unsigned int gate_source)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ const unsigned second_gate_reg =
+ NITIO_Gi_Second_Gate_Reg(counter->counter_index);
+ const unsigned selected_second_gate = CR_CHAN(gate_source);
+ /* bits of second_gate that may be meaningful to second gate register */
+ static const unsigned selected_second_gate_mask = 0x1f;
+ unsigned ni_m_series_second_gate_select;
+
+ /* FIXME: We don't know what the m-series second gate codes are, so we'll just pass
+ the bits through for now. */
+ switch (selected_second_gate) {
+ default:
+ ni_m_series_second_gate_select =
+ selected_second_gate & selected_second_gate_mask;
+ break;
+ };
+ counter_dev->regs[second_gate_reg] |= Gi_Second_Gate_Mode_Bit;
+ counter_dev->regs[second_gate_reg] &= ~Gi_Second_Gate_Select_Mask;
+ counter_dev->regs[second_gate_reg] |=
+ Gi_Second_Gate_Select_Bits(ni_m_series_second_gate_select);
+ write_register(counter, counter_dev->regs[second_gate_reg],
+ second_gate_reg);
+ return 0;
+}
+
+int ni_tio_set_gate_src(struct ni_gpct *counter, unsigned gate_index,
+ unsigned int gate_source)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ const unsigned second_gate_reg =
+ NITIO_Gi_Second_Gate_Reg(counter->counter_index);
+
+ switch (gate_index) {
+ case 0:
+ if (CR_CHAN(gate_source) == NI_GPCT_DISABLED_GATE_SELECT) {
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Mode_Reg(counter->counter_index),
+ Gi_Gating_Mode_Mask, Gi_Gating_Disabled_Bits);
+ return 0;
+ }
+ ni_tio_set_first_gate_modifiers(counter, gate_source);
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_e_series:
+ case ni_gpct_variant_m_series:
+ return ni_m_series_set_first_gate(counter, gate_source);
+ break;
+ case ni_gpct_variant_660x:
+ return ni_660x_set_first_gate(counter, gate_source);
+ break;
+ default:
+ BUG();
+ break;
+ }
+ break;
+ case 1:
+ if (ni_tio_second_gate_registers_present(counter_dev) == 0)
+ return -EINVAL;
+ if (CR_CHAN(gate_source) == NI_GPCT_DISABLED_GATE_SELECT) {
+ counter_dev->regs[second_gate_reg] &=
+ ~Gi_Second_Gate_Mode_Bit;
+ write_register(counter,
+ counter_dev->regs[second_gate_reg],
+ second_gate_reg);
+ return 0;
+ }
+ if (gate_source & CR_INVERT) {
+ counter_dev->regs[second_gate_reg] |=
+ Gi_Second_Gate_Polarity_Bit;
+ } else {
+ counter_dev->regs[second_gate_reg] &=
+ ~Gi_Second_Gate_Polarity_Bit;
+ }
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_m_series:
+ return ni_m_series_set_second_gate(counter,
+ gate_source);
+ break;
+ case ni_gpct_variant_660x:
+ return ni_660x_set_second_gate(counter, gate_source);
+ break;
+ default:
+ BUG();
+ break;
+ }
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return 0;
+}
+
+static int ni_tio_set_other_src(struct ni_gpct *counter, unsigned index,
+ unsigned int source)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+
+ if (counter_dev->variant == ni_gpct_variant_m_series) {
+ unsigned int abz_reg, shift, mask;
+
+ abz_reg = NITIO_Gi_ABZ_Reg(counter->counter_index);
+ switch (index) {
+ case NI_GPCT_SOURCE_ENCODER_A:
+ shift = 10;
+ break;
+ case NI_GPCT_SOURCE_ENCODER_B:
+ shift = 5;
+ break;
+ case NI_GPCT_SOURCE_ENCODER_Z:
+ shift = 0;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ mask = 0x1f << shift;
+ if (source > 0x1f) {
+ /* Disable gate */
+ source = 0x1f;
+ }
+ counter_dev->regs[abz_reg] &= ~mask;
+ counter_dev->regs[abz_reg] |= (source << shift) & mask;
+ write_register(counter, counter_dev->regs[abz_reg], abz_reg);
+// rt_printk("%s %x %d %d\n", __FUNCTION__, counter_dev->regs[abz_reg], index, source);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static unsigned ni_660x_first_gate_to_generic_gate_source(unsigned
+ ni_660x_gate_select)
+{
+ unsigned i;
+
+ switch (ni_660x_gate_select) {
+ case NI_660x_Source_Pin_i_Gate_Select:
+ return NI_GPCT_SOURCE_PIN_i_GATE_SELECT;
+ break;
+ case NI_660x_Gate_Pin_i_Gate_Select:
+ return NI_GPCT_GATE_PIN_i_GATE_SELECT;
+ break;
+ case NI_660x_Next_SRC_Gate_Select:
+ return NI_GPCT_NEXT_SOURCE_GATE_SELECT;
+ break;
+ case NI_660x_Next_Out_Gate_Select:
+ return NI_GPCT_NEXT_OUT_GATE_SELECT;
+ break;
+ case NI_660x_Logic_Low_Gate_Select:
+ return NI_GPCT_LOGIC_LOW_GATE_SELECT;
+ break;
+ default:
+ for (i = 0; i <= ni_660x_max_rtsi_channel; ++i) {
+ if (ni_660x_gate_select == NI_660x_RTSI_Gate_Select(i)) {
+ return NI_GPCT_RTSI_GATE_SELECT(i);
+ break;
+ }
+ }
+ if (i <= ni_660x_max_rtsi_channel)
+ break;
+ for (i = 0; i <= ni_660x_max_gate_pin; ++i) {
+ if (ni_660x_gate_select ==
+ NI_660x_Gate_Pin_Gate_Select(i)) {
+ return NI_GPCT_GATE_PIN_GATE_SELECT(i);
+ break;
+ }
+ }
+ if (i <= ni_660x_max_gate_pin)
+ break;
+ BUG();
+ break;
+ }
+ return 0;
+};
+
+static unsigned ni_m_series_first_gate_to_generic_gate_source(unsigned
+ ni_m_series_gate_select)
+{
+ unsigned i;
+
+ switch (ni_m_series_gate_select) {
+ case NI_M_Series_Timestamp_Mux_Gate_Select:
+ return NI_GPCT_TIMESTAMP_MUX_GATE_SELECT;
+ break;
+ case NI_M_Series_AI_START2_Gate_Select:
+ return NI_GPCT_AI_START2_GATE_SELECT;
+ break;
+ case NI_M_Series_PXI_Star_Trigger_Gate_Select:
+ return NI_GPCT_PXI_STAR_TRIGGER_GATE_SELECT;
+ break;
+ case NI_M_Series_Next_Out_Gate_Select:
+ return NI_GPCT_NEXT_OUT_GATE_SELECT;
+ break;
+ case NI_M_Series_AI_START1_Gate_Select:
+ return NI_GPCT_AI_START1_GATE_SELECT;
+ break;
+ case NI_M_Series_Next_SRC_Gate_Select:
+ return NI_GPCT_NEXT_SOURCE_GATE_SELECT;
+ break;
+ case NI_M_Series_Analog_Trigger_Out_Gate_Select:
+ return NI_GPCT_ANALOG_TRIGGER_OUT_GATE_SELECT;
+ break;
+ case NI_M_Series_Logic_Low_Gate_Select:
+ return NI_GPCT_LOGIC_LOW_GATE_SELECT;
+ break;
+ default:
+ for (i = 0; i <= ni_m_series_max_rtsi_channel; ++i) {
+ if (ni_m_series_gate_select ==
+ NI_M_Series_RTSI_Gate_Select(i)) {
+ return NI_GPCT_RTSI_GATE_SELECT(i);
+ break;
+ }
+ }
+ if (i <= ni_m_series_max_rtsi_channel)
+ break;
+ for (i = 0; i <= ni_m_series_max_pfi_channel; ++i) {
+ if (ni_m_series_gate_select ==
+ NI_M_Series_PFI_Gate_Select(i)) {
+ return NI_GPCT_PFI_GATE_SELECT(i);
+ break;
+ }
+ }
+ if (i <= ni_m_series_max_pfi_channel)
+ break;
+ BUG();
+ break;
+ }
+ return 0;
+};
+
+static unsigned ni_660x_second_gate_to_generic_gate_source(unsigned
+ ni_660x_gate_select)
+{
+ unsigned i;
+
+ switch (ni_660x_gate_select) {
+ case NI_660x_Source_Pin_i_Second_Gate_Select:
+ return NI_GPCT_SOURCE_PIN_i_GATE_SELECT;
+ break;
+ case NI_660x_Up_Down_Pin_i_Second_Gate_Select:
+ return NI_GPCT_UP_DOWN_PIN_i_GATE_SELECT;
+ break;
+ case NI_660x_Next_SRC_Second_Gate_Select:
+ return NI_GPCT_NEXT_SOURCE_GATE_SELECT;
+ break;
+ case NI_660x_Next_Out_Second_Gate_Select:
+ return NI_GPCT_NEXT_OUT_GATE_SELECT;
+ break;
+ case NI_660x_Selected_Gate_Second_Gate_Select:
+ return NI_GPCT_SELECTED_GATE_GATE_SELECT;
+ break;
+ case NI_660x_Logic_Low_Second_Gate_Select:
+ return NI_GPCT_LOGIC_LOW_GATE_SELECT;
+ break;
+ default:
+ for (i = 0; i <= ni_660x_max_rtsi_channel; ++i) {
+ if (ni_660x_gate_select ==
+ NI_660x_RTSI_Second_Gate_Select(i)) {
+ return NI_GPCT_RTSI_GATE_SELECT(i);
+ break;
+ }
+ }
+ if (i <= ni_660x_max_rtsi_channel)
+ break;
+ for (i = 0; i <= ni_660x_max_up_down_pin; ++i) {
+ if (ni_660x_gate_select ==
+ NI_660x_Up_Down_Pin_Second_Gate_Select(i)) {
+ return NI_GPCT_UP_DOWN_PIN_GATE_SELECT(i);
+ break;
+ }
+ }
+ if (i <= ni_660x_max_up_down_pin)
+ break;
+ BUG();
+ break;
+ }
+ return 0;
+};
+
+static unsigned ni_m_series_second_gate_to_generic_gate_source(unsigned
+ ni_m_series_gate_select)
+{
+ /*FIXME: the second gate sources for the m series are undocumented, so we just return
+ * the raw bits for now. */
+ switch (ni_m_series_gate_select) {
+ default:
+ return ni_m_series_gate_select;
+ break;
+ }
+ return 0;
+};
+
+static int ni_tio_get_gate_src(struct ni_gpct *counter, unsigned gate_index,
+ unsigned int * gate_source)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ const unsigned mode_bits = ni_tio_get_soft_copy(counter,
+ NITIO_Gi_Mode_Reg(counter->counter_index));
+ const unsigned second_gate_reg =
+ NITIO_Gi_Second_Gate_Reg(counter->counter_index);
+ unsigned gate_select_bits;
+
+ switch (gate_index) {
+ case 0:
+ if ((mode_bits & Gi_Gating_Mode_Mask) ==
+ Gi_Gating_Disabled_Bits) {
+ *gate_source = NI_GPCT_DISABLED_GATE_SELECT;
+ return 0;
+ } else {
+ gate_select_bits =
+ (ni_tio_get_soft_copy(counter,
+ NITIO_Gi_Input_Select_Reg(counter->
+ counter_index)) &
+ Gi_Gate_Select_Mask) >> Gi_Gate_Select_Shift;
+ }
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_e_series:
+ case ni_gpct_variant_m_series:
+ *gate_source =
+ ni_m_series_first_gate_to_generic_gate_source
+ (gate_select_bits);
+ break;
+ case ni_gpct_variant_660x:
+ *gate_source =
+ ni_660x_first_gate_to_generic_gate_source
+ (gate_select_bits);
+ break;
+ default:
+ BUG();
+ break;
+ }
+ if (mode_bits & Gi_Gate_Polarity_Bit) {
+ *gate_source |= CR_INVERT;
+ }
+ if ((mode_bits & Gi_Gating_Mode_Mask) != Gi_Level_Gating_Bits) {
+ *gate_source |= CR_EDGE;
+ }
+ break;
+ case 1:
+ if ((mode_bits & Gi_Gating_Mode_Mask) == Gi_Gating_Disabled_Bits
+ || (counter_dev->
+ regs[second_gate_reg] & Gi_Second_Gate_Mode_Bit)
+ == 0) {
+ *gate_source = NI_GPCT_DISABLED_GATE_SELECT;
+ return 0;
+ } else {
+ gate_select_bits =
+ (counter_dev->
+ regs[second_gate_reg] &
+ Gi_Second_Gate_Select_Mask) >>
+ Gi_Second_Gate_Select_Shift;
+ }
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_e_series:
+ case ni_gpct_variant_m_series:
+ *gate_source =
+ ni_m_series_second_gate_to_generic_gate_source
+ (gate_select_bits);
+ break;
+ case ni_gpct_variant_660x:
+ *gate_source =
+ ni_660x_second_gate_to_generic_gate_source
+ (gate_select_bits);
+ break;
+ default:
+ BUG();
+ break;
+ }
+ if (counter_dev->
+ regs[second_gate_reg] & Gi_Second_Gate_Polarity_Bit) {
+ *gate_source |= CR_INVERT;
+ }
+ /* second gate can't have edge/level mode set independently */
+ if ((mode_bits & Gi_Gating_Mode_Mask) != Gi_Level_Gating_Bits) {
+ *gate_source |= CR_EDGE;
+ }
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return 0;
+}
+
+int ni_tio_insn_config(struct ni_gpct *counter,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ switch (data[0]) {
+ case INSN_CONFIG_SET_COUNTER_MODE:
+ return ni_tio_set_counter_mode(counter, data[1]);
+ break;
+ case INSN_CONFIG_ARM:
+ return ni_tio_arm(counter, 1, data[1]);
+ break;
+ case INSN_CONFIG_DISARM:
+ ni_tio_arm(counter, 0, 0);
+ return 0;
+ break;
+ case INSN_CONFIG_GET_COUNTER_STATUS:
+ data[1] = ni_tio_counter_status(counter);
+ data[2] = counter_status_mask;
+ return 0;
+ break;
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ return ni_tio_set_clock_src(counter, data[1], data[2]);
+ break;
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ ni_tio_get_clock_src(counter, &data[1], &data[2]);
+ return 0;
+ break;
+ case INSN_CONFIG_SET_GATE_SRC:
+ return ni_tio_set_gate_src(counter, data[1], data[2]);
+ break;
+ case INSN_CONFIG_GET_GATE_SRC:
+ return ni_tio_get_gate_src(counter, data[1], &data[2]);
+ break;
+ case INSN_CONFIG_SET_OTHER_SRC:
+ return ni_tio_set_other_src(counter, data[1], data[2]);
+ break;
+ case INSN_CONFIG_RESET:
+ ni_tio_reset_count_and_disarm(counter);
+ return 0;
+ break;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+int ni_tio_rinsn(struct ni_gpct *counter, struct comedi_insn * insn, unsigned int * data)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ const unsigned channel = CR_CHAN(insn->chanspec);
+ unsigned first_read;
+ unsigned second_read;
+ unsigned correct_read;
+
+ if (insn->n < 1)
+ return 0;
+ switch (channel) {
+ case 0:
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Command_Reg(counter->counter_index),
+ Gi_Save_Trace_Bit, 0);
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Command_Reg(counter->counter_index),
+ Gi_Save_Trace_Bit, Gi_Save_Trace_Bit);
+ /* The count doesn't get latched until the next clock edge, so it is possible the count
+ may change (once) while we are reading. Since the read of the SW_Save_Reg isn't
+ atomic (apparently even when it's a 32 bit register according to 660x docs),
+ we need to read twice and make sure the reading hasn't changed. If it has,
+ a third read will be correct since the count value will definitely have latched by then. */
+ first_read =
+ read_register(counter,
+ NITIO_Gi_SW_Save_Reg(counter->counter_index));
+ second_read =
+ read_register(counter,
+ NITIO_Gi_SW_Save_Reg(counter->counter_index));
+ if (first_read != second_read)
+ correct_read =
+ read_register(counter,
+ NITIO_Gi_SW_Save_Reg(counter->counter_index));
+ else
+ correct_read = first_read;
+ data[0] = correct_read;
+ return 0;
+ break;
+ case 1:
+ data[0] =
+ counter_dev->regs[NITIO_Gi_LoadA_Reg(counter->
+ counter_index)];
+ break;
+ case 2:
+ data[0] =
+ counter_dev->regs[NITIO_Gi_LoadB_Reg(counter->
+ counter_index)];
+ break;
+ };
+ return 0;
+}
+
+static unsigned ni_tio_next_load_register(struct ni_gpct *counter)
+{
+ const unsigned bits = read_register(counter,
+ NITIO_Gxx_Status_Reg(counter->counter_index));
+
+ if (bits & Gi_Next_Load_Source_Bit(counter->counter_index)) {
+ return NITIO_Gi_LoadB_Reg(counter->counter_index);
+ } else {
+ return NITIO_Gi_LoadA_Reg(counter->counter_index);
+ }
+}
+
+int ni_tio_winsn(struct ni_gpct *counter, struct comedi_insn * insn, unsigned int * data)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ const unsigned channel = CR_CHAN(insn->chanspec);
+ unsigned load_reg;
+
+ if (insn->n < 1)
+ return 0;
+ switch (channel) {
+ case 0:
+ /* Unsafe if counter is armed. Should probably check status and return -EBUSY if armed. */
+ /* Don't disturb load source select, just use whichever load register is already selected. */
+ load_reg = ni_tio_next_load_register(counter);
+ write_register(counter, data[0], load_reg);
+ ni_tio_set_bits_transient(counter,
+ NITIO_Gi_Command_Reg(counter->counter_index), 0, 0,
+ Gi_Load_Bit);
+ /* restore state of load reg to whatever the user set last set it to */
+ write_register(counter, counter_dev->regs[load_reg], load_reg);
+ break;
+ case 1:
+ counter_dev->regs[NITIO_Gi_LoadA_Reg(counter->counter_index)] =
+ data[0];
+ write_register(counter, data[0],
+ NITIO_Gi_LoadA_Reg(counter->counter_index));
+ break;
+ case 2:
+ counter_dev->regs[NITIO_Gi_LoadB_Reg(counter->counter_index)] =
+ data[0];
+ write_register(counter, data[0],
+ NITIO_Gi_LoadB_Reg(counter->counter_index));
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(ni_tio_rinsn);
+EXPORT_SYMBOL_GPL(ni_tio_winsn);
+EXPORT_SYMBOL_GPL(ni_tio_insn_config);
+EXPORT_SYMBOL_GPL(ni_tio_init_counter);
+EXPORT_SYMBOL_GPL(ni_tio_arm);
+EXPORT_SYMBOL_GPL(ni_tio_set_gate_src);
+EXPORT_SYMBOL_GPL(ni_gpct_device_construct);
+EXPORT_SYMBOL_GPL(ni_gpct_device_destroy);
diff --git a/drivers/staging/comedi/drivers/ni_tio.h b/drivers/staging/comedi/drivers/ni_tio.h
new file mode 100644
index 000000000000..0729d60b01cc
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_tio.h
@@ -0,0 +1,163 @@
+/*
+ drivers/ni_tio.h
+ Header file for NI general purpose counter support code (ni_tio.c)
+
+ COMEDI - Linux Control and Measurement Device Interface
+
+ 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_NI_TIO_H
+#define _COMEDI_NI_TIO_H
+
+#include "../comedidev.h"
+
+// forward declarations
+struct mite_struct;
+struct ni_gpct_device;
+
+enum ni_gpct_register {
+ NITIO_G0_Autoincrement_Reg,
+ NITIO_G1_Autoincrement_Reg,
+ NITIO_G2_Autoincrement_Reg,
+ NITIO_G3_Autoincrement_Reg,
+ NITIO_G0_Command_Reg,
+ NITIO_G1_Command_Reg,
+ NITIO_G2_Command_Reg,
+ NITIO_G3_Command_Reg,
+ NITIO_G0_HW_Save_Reg,
+ NITIO_G1_HW_Save_Reg,
+ NITIO_G2_HW_Save_Reg,
+ NITIO_G3_HW_Save_Reg,
+ NITIO_G0_SW_Save_Reg,
+ NITIO_G1_SW_Save_Reg,
+ NITIO_G2_SW_Save_Reg,
+ NITIO_G3_SW_Save_Reg,
+ NITIO_G0_Mode_Reg,
+ NITIO_G1_Mode_Reg,
+ NITIO_G2_Mode_Reg,
+ NITIO_G3_Mode_Reg,
+ NITIO_G0_LoadA_Reg,
+ NITIO_G1_LoadA_Reg,
+ NITIO_G2_LoadA_Reg,
+ NITIO_G3_LoadA_Reg,
+ NITIO_G0_LoadB_Reg,
+ NITIO_G1_LoadB_Reg,
+ NITIO_G2_LoadB_Reg,
+ NITIO_G3_LoadB_Reg,
+ NITIO_G0_Input_Select_Reg,
+ NITIO_G1_Input_Select_Reg,
+ NITIO_G2_Input_Select_Reg,
+ NITIO_G3_Input_Select_Reg,
+ NITIO_G0_Counting_Mode_Reg,
+ NITIO_G1_Counting_Mode_Reg,
+ NITIO_G2_Counting_Mode_Reg,
+ NITIO_G3_Counting_Mode_Reg,
+ NITIO_G0_Second_Gate_Reg,
+ NITIO_G1_Second_Gate_Reg,
+ NITIO_G2_Second_Gate_Reg,
+ NITIO_G3_Second_Gate_Reg,
+ NITIO_G01_Status_Reg,
+ NITIO_G23_Status_Reg,
+ NITIO_G01_Joint_Reset_Reg,
+ NITIO_G23_Joint_Reset_Reg,
+ NITIO_G01_Joint_Status1_Reg,
+ NITIO_G23_Joint_Status1_Reg,
+ NITIO_G01_Joint_Status2_Reg,
+ NITIO_G23_Joint_Status2_Reg,
+ NITIO_G0_DMA_Config_Reg,
+ NITIO_G1_DMA_Config_Reg,
+ NITIO_G2_DMA_Config_Reg,
+ NITIO_G3_DMA_Config_Reg,
+ NITIO_G0_DMA_Status_Reg,
+ NITIO_G1_DMA_Status_Reg,
+ NITIO_G2_DMA_Status_Reg,
+ NITIO_G3_DMA_Status_Reg,
+ NITIO_G0_ABZ_Reg,
+ NITIO_G1_ABZ_Reg,
+ NITIO_G0_Interrupt_Acknowledge_Reg,
+ NITIO_G1_Interrupt_Acknowledge_Reg,
+ NITIO_G2_Interrupt_Acknowledge_Reg,
+ NITIO_G3_Interrupt_Acknowledge_Reg,
+ NITIO_G0_Status_Reg,
+ NITIO_G1_Status_Reg,
+ NITIO_G2_Status_Reg,
+ NITIO_G3_Status_Reg,
+ NITIO_G0_Interrupt_Enable_Reg,
+ NITIO_G1_Interrupt_Enable_Reg,
+ NITIO_G2_Interrupt_Enable_Reg,
+ NITIO_G3_Interrupt_Enable_Reg,
+ NITIO_Num_Registers,
+};
+
+enum ni_gpct_variant {
+ ni_gpct_variant_e_series,
+ ni_gpct_variant_m_series,
+ ni_gpct_variant_660x
+};
+
+struct ni_gpct {
+ struct ni_gpct_device *counter_dev;
+ unsigned counter_index;
+ unsigned chip_index;
+ uint64_t clock_period_ps; /* clock period in picoseconds */
+ struct mite_channel *mite_chan;
+ spinlock_t lock;
+};
+
+struct ni_gpct_device {
+ struct comedi_device *dev;
+ void (*write_register) (struct ni_gpct * counter, unsigned bits,
+ enum ni_gpct_register reg);
+ unsigned (*read_register) (struct ni_gpct * counter,
+ enum ni_gpct_register reg);
+ enum ni_gpct_variant variant;
+ struct ni_gpct *counters;
+ unsigned num_counters;
+ unsigned regs[NITIO_Num_Registers];
+ spinlock_t regs_lock;
+};
+
+extern struct ni_gpct_device *ni_gpct_device_construct(struct comedi_device * dev,
+ void (*write_register) (struct ni_gpct * counter, unsigned bits,
+ enum ni_gpct_register reg),
+ unsigned (*read_register) (struct ni_gpct * counter,
+ enum ni_gpct_register reg), enum ni_gpct_variant variant,
+ unsigned num_counters);
+extern void ni_gpct_device_destroy(struct ni_gpct_device *counter_dev);
+extern void ni_tio_init_counter(struct ni_gpct *counter);
+extern int ni_tio_rinsn(struct ni_gpct *counter,
+ struct comedi_insn * insn, unsigned int * data);
+extern int ni_tio_insn_config(struct ni_gpct *counter,
+ struct comedi_insn * insn, unsigned int * data);
+extern int ni_tio_winsn(struct ni_gpct *counter,
+ struct comedi_insn * insn, unsigned int * data);
+extern int ni_tio_cmd(struct ni_gpct *counter, struct comedi_async *async);
+extern int ni_tio_cmdtest(struct ni_gpct *counter, struct comedi_cmd * cmd);
+extern int ni_tio_cancel(struct ni_gpct *counter);
+extern void ni_tio_handle_interrupt(struct ni_gpct *counter,
+ struct comedi_subdevice * s);
+extern void ni_tio_set_mite_channel(struct ni_gpct *counter,
+ struct mite_channel *mite_chan);
+extern void ni_tio_acknowledge_and_confirm(struct ni_gpct *counter,
+ int *gate_error, int *tc_error, int *perm_stale_data, int *stale_data);
+
+static inline struct ni_gpct *subdev_to_counter(struct comedi_subdevice * s)
+{
+ return s->private;
+}
+
+#endif /* _COMEDI_NI_TIO_H */
diff --git a/drivers/staging/comedi/drivers/ni_tio_internal.h b/drivers/staging/comedi/drivers/ni_tio_internal.h
new file mode 100644
index 000000000000..ac5b171cbe18
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_tio_internal.h
@@ -0,0 +1,774 @@
+/*
+ drivers/ni_tio_internal.h
+ Header file for NI general purpose counter support code (ni_tio.c and
+ ni_tiocmd.c)
+
+ COMEDI - Linux Control and Measurement Device Interface
+
+ 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_NI_TIO_INTERNAL_H
+#define _COMEDI_NI_TIO_INTERNAL_H
+
+#include "ni_tio.h"
+
+static inline enum ni_gpct_register NITIO_Gi_Autoincrement_Reg(unsigned
+ counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NITIO_G0_Autoincrement_Reg;
+ break;
+ case 1:
+ return NITIO_G1_Autoincrement_Reg;
+ break;
+ case 2:
+ return NITIO_G2_Autoincrement_Reg;
+ break;
+ case 3:
+ return NITIO_G3_Autoincrement_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_Command_Reg(unsigned counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NITIO_G0_Command_Reg;
+ break;
+ case 1:
+ return NITIO_G1_Command_Reg;
+ break;
+ case 2:
+ return NITIO_G2_Command_Reg;
+ break;
+ case 3:
+ return NITIO_G3_Command_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_Counting_Mode_Reg(unsigned
+ counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NITIO_G0_Counting_Mode_Reg;
+ break;
+ case 1:
+ return NITIO_G1_Counting_Mode_Reg;
+ break;
+ case 2:
+ return NITIO_G2_Counting_Mode_Reg;
+ break;
+ case 3:
+ return NITIO_G3_Counting_Mode_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_Input_Select_Reg(unsigned
+ counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NITIO_G0_Input_Select_Reg;
+ break;
+ case 1:
+ return NITIO_G1_Input_Select_Reg;
+ break;
+ case 2:
+ return NITIO_G2_Input_Select_Reg;
+ break;
+ case 3:
+ return NITIO_G3_Input_Select_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gxx_Joint_Reset_Reg(unsigned
+ counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ case 1:
+ return NITIO_G01_Joint_Reset_Reg;
+ break;
+ case 2:
+ case 3:
+ return NITIO_G23_Joint_Reset_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gxx_Joint_Status1_Reg(unsigned
+ counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ case 1:
+ return NITIO_G01_Joint_Status1_Reg;
+ break;
+ case 2:
+ case 3:
+ return NITIO_G23_Joint_Status1_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gxx_Joint_Status2_Reg(unsigned
+ counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ case 1:
+ return NITIO_G01_Joint_Status2_Reg;
+ break;
+ case 2:
+ case 3:
+ return NITIO_G23_Joint_Status2_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gxx_Status_Reg(unsigned counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ case 1:
+ return NITIO_G01_Status_Reg;
+ break;
+ case 2:
+ case 3:
+ return NITIO_G23_Status_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_LoadA_Reg(unsigned counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NITIO_G0_LoadA_Reg;
+ break;
+ case 1:
+ return NITIO_G1_LoadA_Reg;
+ break;
+ case 2:
+ return NITIO_G2_LoadA_Reg;
+ break;
+ case 3:
+ return NITIO_G3_LoadA_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_LoadB_Reg(unsigned counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NITIO_G0_LoadB_Reg;
+ break;
+ case 1:
+ return NITIO_G1_LoadB_Reg;
+ break;
+ case 2:
+ return NITIO_G2_LoadB_Reg;
+ break;
+ case 3:
+ return NITIO_G3_LoadB_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_Mode_Reg(unsigned counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NITIO_G0_Mode_Reg;
+ break;
+ case 1:
+ return NITIO_G1_Mode_Reg;
+ break;
+ case 2:
+ return NITIO_G2_Mode_Reg;
+ break;
+ case 3:
+ return NITIO_G3_Mode_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_SW_Save_Reg(int counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NITIO_G0_SW_Save_Reg;
+ break;
+ case 1:
+ return NITIO_G1_SW_Save_Reg;
+ break;
+ case 2:
+ return NITIO_G2_SW_Save_Reg;
+ break;
+ case 3:
+ return NITIO_G3_SW_Save_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_Second_Gate_Reg(int counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NITIO_G0_Second_Gate_Reg;
+ break;
+ case 1:
+ return NITIO_G1_Second_Gate_Reg;
+ break;
+ case 2:
+ return NITIO_G2_Second_Gate_Reg;
+ break;
+ case 3:
+ return NITIO_G3_Second_Gate_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_DMA_Config_Reg(int counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NITIO_G0_DMA_Config_Reg;
+ break;
+ case 1:
+ return NITIO_G1_DMA_Config_Reg;
+ break;
+ case 2:
+ return NITIO_G2_DMA_Config_Reg;
+ break;
+ case 3:
+ return NITIO_G3_DMA_Config_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_DMA_Status_Reg(int counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NITIO_G0_DMA_Status_Reg;
+ break;
+ case 1:
+ return NITIO_G1_DMA_Status_Reg;
+ break;
+ case 2:
+ return NITIO_G2_DMA_Status_Reg;
+ break;
+ case 3:
+ return NITIO_G3_DMA_Status_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_ABZ_Reg(int counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NITIO_G0_ABZ_Reg;
+ break;
+ case 1:
+ return NITIO_G1_ABZ_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_Interrupt_Acknowledge_Reg(int
+ counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NITIO_G0_Interrupt_Acknowledge_Reg;
+ break;
+ case 1:
+ return NITIO_G1_Interrupt_Acknowledge_Reg;
+ break;
+ case 2:
+ return NITIO_G2_Interrupt_Acknowledge_Reg;
+ break;
+ case 3:
+ return NITIO_G3_Interrupt_Acknowledge_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_Status_Reg(int counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NITIO_G0_Status_Reg;
+ break;
+ case 1:
+ return NITIO_G1_Status_Reg;
+ break;
+ case 2:
+ return NITIO_G2_Status_Reg;
+ break;
+ case 3:
+ return NITIO_G3_Status_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_Interrupt_Enable_Reg(int
+ counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NITIO_G0_Interrupt_Enable_Reg;
+ break;
+ case 1:
+ return NITIO_G1_Interrupt_Enable_Reg;
+ break;
+ case 2:
+ return NITIO_G2_Interrupt_Enable_Reg;
+ break;
+ case 3:
+ return NITIO_G3_Interrupt_Enable_Reg;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+enum Gi_Auto_Increment_Reg_Bits {
+ Gi_Auto_Increment_Mask = 0xff
+};
+
+#define Gi_Up_Down_Shift 5
+enum Gi_Command_Reg_Bits {
+ Gi_Arm_Bit = 0x1,
+ Gi_Save_Trace_Bit = 0x2,
+ Gi_Load_Bit = 0x4,
+ Gi_Disarm_Bit = 0x10,
+ Gi_Up_Down_Mask = 0x3 << Gi_Up_Down_Shift,
+ Gi_Always_Down_Bits = 0x0 << Gi_Up_Down_Shift,
+ Gi_Always_Up_Bits = 0x1 << Gi_Up_Down_Shift,
+ Gi_Up_Down_Hardware_IO_Bits = 0x2 << Gi_Up_Down_Shift,
+ Gi_Up_Down_Hardware_Gate_Bits = 0x3 << Gi_Up_Down_Shift,
+ Gi_Write_Switch_Bit = 0x80,
+ Gi_Synchronize_Gate_Bit = 0x100,
+ Gi_Little_Big_Endian_Bit = 0x200,
+ Gi_Bank_Switch_Start_Bit = 0x400,
+ Gi_Bank_Switch_Mode_Bit = 0x800,
+ Gi_Bank_Switch_Enable_Bit = 0x1000,
+ Gi_Arm_Copy_Bit = 0x2000,
+ Gi_Save_Trace_Copy_Bit = 0x4000,
+ Gi_Disarm_Copy_Bit = 0x8000
+};
+
+#define Gi_Index_Phase_Bitshift 5
+#define Gi_HW_Arm_Select_Shift 8
+enum Gi_Counting_Mode_Reg_Bits {
+ Gi_Counting_Mode_Mask = 0x7,
+ Gi_Counting_Mode_Normal_Bits = 0x0,
+ Gi_Counting_Mode_QuadratureX1_Bits = 0x1,
+ Gi_Counting_Mode_QuadratureX2_Bits = 0x2,
+ Gi_Counting_Mode_QuadratureX4_Bits = 0x3,
+ Gi_Counting_Mode_Two_Pulse_Bits = 0x4,
+ Gi_Counting_Mode_Sync_Source_Bits = 0x6,
+ Gi_Index_Mode_Bit = 0x10,
+ Gi_Index_Phase_Mask = 0x3 << Gi_Index_Phase_Bitshift,
+ Gi_Index_Phase_LowA_LowB = 0x0 << Gi_Index_Phase_Bitshift,
+ Gi_Index_Phase_LowA_HighB = 0x1 << Gi_Index_Phase_Bitshift,
+ Gi_Index_Phase_HighA_LowB = 0x2 << Gi_Index_Phase_Bitshift,
+ Gi_Index_Phase_HighA_HighB = 0x3 << Gi_Index_Phase_Bitshift,
+ Gi_HW_Arm_Enable_Bit = 0x80, /* from m-series example code, not documented in 660x register level manual */
+ Gi_660x_HW_Arm_Select_Mask = 0x7 << Gi_HW_Arm_Select_Shift, /* from m-series example code, not documented in 660x register level manual */
+ Gi_660x_Prescale_X8_Bit = 0x1000,
+ Gi_M_Series_Prescale_X8_Bit = 0x2000,
+ Gi_M_Series_HW_Arm_Select_Mask = 0x1f << Gi_HW_Arm_Select_Shift,
+ /* must be set for clocks over 40MHz, which includes synchronous counting and quadrature modes */
+ Gi_660x_Alternate_Sync_Bit = 0x2000,
+ Gi_M_Series_Alternate_Sync_Bit = 0x4000,
+ Gi_660x_Prescale_X2_Bit = 0x4000, /* from m-series example code, not documented in 660x register level manual */
+ Gi_M_Series_Prescale_X2_Bit = 0x8000,
+};
+
+#define Gi_Source_Select_Shift 2
+#define Gi_Gate_Select_Shift 7
+enum Gi_Input_Select_Bits {
+ Gi_Read_Acknowledges_Irq = 0x1, // not present on 660x
+ Gi_Write_Acknowledges_Irq = 0x2, // not present on 660x
+ Gi_Source_Select_Mask = 0x7c,
+ Gi_Gate_Select_Mask = 0x1f << Gi_Gate_Select_Shift,
+ Gi_Gate_Select_Load_Source_Bit = 0x1000,
+ Gi_Or_Gate_Bit = 0x2000,
+ Gi_Output_Polarity_Bit = 0x4000, /* set to invert */
+ Gi_Source_Polarity_Bit = 0x8000 /* set to invert */
+};
+
+enum Gi_Mode_Bits {
+ Gi_Gating_Mode_Mask = 0x3,
+ Gi_Gating_Disabled_Bits = 0x0,
+ Gi_Level_Gating_Bits = 0x1,
+ Gi_Rising_Edge_Gating_Bits = 0x2,
+ Gi_Falling_Edge_Gating_Bits = 0x3,
+ Gi_Gate_On_Both_Edges_Bit = 0x4, /* used in conjunction with rising edge gating mode */
+ Gi_Trigger_Mode_for_Edge_Gate_Mask = 0x18,
+ Gi_Edge_Gate_Starts_Stops_Bits = 0x0,
+ Gi_Edge_Gate_Stops_Starts_Bits = 0x8,
+ Gi_Edge_Gate_Starts_Bits = 0x10,
+ Gi_Edge_Gate_No_Starts_or_Stops_Bits = 0x18,
+ Gi_Stop_Mode_Mask = 0x60,
+ Gi_Stop_on_Gate_Bits = 0x00,
+ Gi_Stop_on_Gate_or_TC_Bits = 0x20,
+ Gi_Stop_on_Gate_or_Second_TC_Bits = 0x40,
+ Gi_Load_Source_Select_Bit = 0x80,
+ Gi_Output_Mode_Mask = 0x300,
+ Gi_Output_TC_Pulse_Bits = 0x100,
+ Gi_Output_TC_Toggle_Bits = 0x200,
+ Gi_Output_TC_or_Gate_Toggle_Bits = 0x300,
+ Gi_Counting_Once_Mask = 0xc00,
+ Gi_No_Hardware_Disarm_Bits = 0x000,
+ Gi_Disarm_at_TC_Bits = 0x400,
+ Gi_Disarm_at_Gate_Bits = 0x800,
+ Gi_Disarm_at_TC_or_Gate_Bits = 0xc00,
+ Gi_Loading_On_TC_Bit = 0x1000,
+ Gi_Gate_Polarity_Bit = 0x2000,
+ Gi_Loading_On_Gate_Bit = 0x4000,
+ Gi_Reload_Source_Switching_Bit = 0x8000
+};
+
+#define Gi_Second_Gate_Select_Shift 7
+/*FIXME: m-series has a second gate subselect bit */
+/*FIXME: m-series second gate sources are undocumented (by NI)*/
+enum Gi_Second_Gate_Bits {
+ Gi_Second_Gate_Mode_Bit = 0x1,
+ Gi_Second_Gate_Select_Mask = 0x1f << Gi_Second_Gate_Select_Shift,
+ Gi_Second_Gate_Polarity_Bit = 0x2000,
+ Gi_Second_Gate_Subselect_Bit = 0x4000, /* m-series only */
+ Gi_Source_Subselect_Bit = 0x8000 /* m-series only */
+};
+static inline unsigned Gi_Second_Gate_Select_Bits(unsigned second_gate_select)
+{
+ return (second_gate_select << Gi_Second_Gate_Select_Shift) &
+ Gi_Second_Gate_Select_Mask;
+}
+
+enum Gxx_Status_Bits {
+ G0_Save_Bit = 0x1,
+ G1_Save_Bit = 0x2,
+ G0_Counting_Bit = 0x4,
+ G1_Counting_Bit = 0x8,
+ G0_Next_Load_Source_Bit = 0x10,
+ G1_Next_Load_Source_Bit = 0x20,
+ G0_Stale_Data_Bit = 0x40,
+ G1_Stale_Data_Bit = 0x80,
+ G0_Armed_Bit = 0x100,
+ G1_Armed_Bit = 0x200,
+ G0_No_Load_Between_Gates_Bit = 0x400,
+ G1_No_Load_Between_Gates_Bit = 0x800,
+ G0_TC_Error_Bit = 0x1000,
+ G1_TC_Error_Bit = 0x2000,
+ G0_Gate_Error_Bit = 0x4000,
+ G1_Gate_Error_Bit = 0x8000
+};
+static inline enum Gxx_Status_Bits Gi_Counting_Bit(unsigned counter_index)
+{
+ if (counter_index % 2)
+ return G1_Counting_Bit;
+ return G0_Counting_Bit;
+}
+static inline enum Gxx_Status_Bits Gi_Armed_Bit(unsigned counter_index)
+{
+ if (counter_index % 2)
+ return G1_Armed_Bit;
+ return G0_Armed_Bit;
+}
+static inline enum Gxx_Status_Bits Gi_Next_Load_Source_Bit(unsigned
+ counter_index)
+{
+ if (counter_index % 2)
+ return G1_Next_Load_Source_Bit;
+ return G0_Next_Load_Source_Bit;
+}
+static inline enum Gxx_Status_Bits Gi_Stale_Data_Bit(unsigned counter_index)
+{
+ if (counter_index % 2)
+ return G1_Stale_Data_Bit;
+ return G0_Stale_Data_Bit;
+}
+static inline enum Gxx_Status_Bits Gi_TC_Error_Bit(unsigned counter_index)
+{
+ if (counter_index % 2)
+ return G1_TC_Error_Bit;
+ return G0_TC_Error_Bit;
+}
+static inline enum Gxx_Status_Bits Gi_Gate_Error_Bit(unsigned counter_index)
+{
+ if (counter_index % 2)
+ return G1_Gate_Error_Bit;
+ return G0_Gate_Error_Bit;
+}
+
+/* joint reset register bits */
+static inline unsigned Gi_Reset_Bit(unsigned counter_index)
+{
+ return 0x1 << (2 + (counter_index % 2));
+}
+
+enum Gxx_Joint_Status2_Bits {
+ G0_Output_Bit = 0x1,
+ G1_Output_Bit = 0x2,
+ G0_HW_Save_Bit = 0x1000,
+ G1_HW_Save_Bit = 0x2000,
+ G0_Permanent_Stale_Bit = 0x4000,
+ G1_Permanent_Stale_Bit = 0x8000
+};
+static inline enum Gxx_Joint_Status2_Bits Gi_Permanent_Stale_Bit(unsigned
+ counter_index)
+{
+ if (counter_index % 2)
+ return G1_Permanent_Stale_Bit;
+ return G0_Permanent_Stale_Bit;
+}
+
+enum Gi_DMA_Config_Reg_Bits {
+ Gi_DMA_Enable_Bit = 0x1,
+ Gi_DMA_Write_Bit = 0x2,
+ Gi_DMA_Int_Bit = 0x4
+};
+
+enum Gi_DMA_Status_Reg_Bits {
+ Gi_DMA_Readbank_Bit = 0x2000,
+ Gi_DRQ_Error_Bit = 0x4000,
+ Gi_DRQ_Status_Bit = 0x8000
+};
+
+enum G02_Interrupt_Acknowledge_Bits {
+ G0_Gate_Error_Confirm_Bit = 0x20,
+ G0_TC_Error_Confirm_Bit = 0x40
+};
+enum G13_Interrupt_Acknowledge_Bits {
+ G1_Gate_Error_Confirm_Bit = 0x2,
+ G1_TC_Error_Confirm_Bit = 0x4
+};
+static inline unsigned Gi_Gate_Error_Confirm_Bit(unsigned counter_index)
+{
+ if (counter_index % 2)
+ return G1_Gate_Error_Confirm_Bit;
+ return G0_Gate_Error_Confirm_Bit;
+}
+static inline unsigned Gi_TC_Error_Confirm_Bit(unsigned counter_index)
+{
+ if (counter_index % 2)
+ return G1_TC_Error_Confirm_Bit;
+ return G0_TC_Error_Confirm_Bit;
+}
+
+// bits that are the same in G0/G2 and G1/G3 interrupt acknowledge registers
+enum Gxx_Interrupt_Acknowledge_Bits {
+ Gi_TC_Interrupt_Ack_Bit = 0x4000,
+ Gi_Gate_Interrupt_Ack_Bit = 0x8000
+};
+
+enum Gi_Status_Bits {
+ Gi_Gate_Interrupt_Bit = 0x4,
+ Gi_TC_Bit = 0x8,
+ Gi_Interrupt_Bit = 0x8000
+};
+
+enum G02_Interrupt_Enable_Bits {
+ G0_TC_Interrupt_Enable_Bit = 0x40,
+ G0_Gate_Interrupt_Enable_Bit = 0x100
+};
+enum G13_Interrupt_Enable_Bits {
+ G1_TC_Interrupt_Enable_Bit = 0x200,
+ G1_Gate_Interrupt_Enable_Bit = 0x400
+};
+static inline unsigned Gi_Gate_Interrupt_Enable_Bit(unsigned counter_index)
+{
+ unsigned bit;
+
+ if (counter_index % 2) {
+ bit = G1_Gate_Interrupt_Enable_Bit;
+ } else {
+ bit = G0_Gate_Interrupt_Enable_Bit;
+ }
+ return bit;
+}
+
+static inline void write_register(struct ni_gpct *counter, unsigned bits,
+ enum ni_gpct_register reg)
+{
+ BUG_ON(reg >= NITIO_Num_Registers);
+ counter->counter_dev->write_register(counter, bits, reg);
+}
+
+static inline unsigned read_register(struct ni_gpct *counter,
+ enum ni_gpct_register reg)
+{
+ BUG_ON(reg >= NITIO_Num_Registers);
+ return counter->counter_dev->read_register(counter, reg);
+}
+
+static inline int ni_tio_counting_mode_registers_present(
+ const struct ni_gpct_device *counter_dev)
+{
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_e_series:
+ return 0;
+ break;
+ case ni_gpct_variant_m_series:
+ case ni_gpct_variant_660x:
+ return 1;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline void ni_tio_set_bits_transient(struct ni_gpct *counter,
+ enum ni_gpct_register register_index, unsigned bit_mask,
+ unsigned bit_values, unsigned transient_bit_values)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned long flags;
+
+ BUG_ON(register_index >= NITIO_Num_Registers);
+ comedi_spin_lock_irqsave(&counter_dev->regs_lock, flags);
+ counter_dev->regs[register_index] &= ~bit_mask;
+ counter_dev->regs[register_index] |= (bit_values & bit_mask);
+ write_register(counter,
+ counter_dev->regs[register_index] | transient_bit_values,
+ register_index);
+ mmiowb();
+ comedi_spin_unlock_irqrestore(&counter_dev->regs_lock, flags);
+}
+
+/* ni_tio_set_bits( ) is for safely writing to registers whose bits may be
+twiddled in interrupt context, or whose software copy may be read in interrupt context.
+*/
+static inline void ni_tio_set_bits(struct ni_gpct *counter,
+ enum ni_gpct_register register_index, unsigned bit_mask,
+ unsigned bit_values)
+{
+ ni_tio_set_bits_transient(counter, register_index, bit_mask, bit_values,
+ 0x0);
+}
+
+/* ni_tio_get_soft_copy( ) is for safely reading the software copy of a register
+whose bits might be modified in interrupt context, or whose software copy
+might need to be read in interrupt context.
+*/
+static inline unsigned ni_tio_get_soft_copy(const struct ni_gpct *counter,
+ enum ni_gpct_register register_index)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned long flags;
+ unsigned value;
+
+ BUG_ON(register_index >= NITIO_Num_Registers);
+ comedi_spin_lock_irqsave(&counter_dev->regs_lock, flags);
+ value = counter_dev->regs[register_index];
+ comedi_spin_unlock_irqrestore(&counter_dev->regs_lock, flags);
+ return value;
+}
+
+int ni_tio_arm(struct ni_gpct *counter, int arm, unsigned start_trigger);
+int ni_tio_set_gate_src(struct ni_gpct *counter, unsigned gate_index,
+ unsigned int gate_source);
+
+#endif /* _COMEDI_NI_TIO_INTERNAL_H */
diff --git a/drivers/staging/comedi/drivers/ni_tiocmd.c b/drivers/staging/comedi/drivers/ni_tiocmd.c
new file mode 100644
index 000000000000..16a26e73c994
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_tiocmd.c
@@ -0,0 +1,523 @@
+/*
+ comedi/drivers/ni_tiocmd.c
+ Command support for NI general purpose counters
+
+ Copyright (C) 2006 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ 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.
+*/
+
+/*
+Driver: ni_tiocmd
+Description: National Instruments general purpose counters command support
+Devices:
+Author: J.P. Mellor <jpmellor@rose-hulman.edu>,
+ Herman.Bruyninckx@mech.kuleuven.ac.be,
+ Wim.Meeussen@mech.kuleuven.ac.be,
+ Klaas.Gadeyne@mech.kuleuven.ac.be,
+ Frank Mori Hess <fmhess@users.sourceforge.net>
+Updated: Fri, 11 Apr 2008 12:32:35 +0100
+Status: works
+
+This module is not used directly by end-users. Rather, it
+is used by other drivers (for example ni_660x and ni_pcimio)
+to provide command support for NI's general purpose counters.
+It was originally split out of ni_tio.c to stop the 'ni_tio'
+module depending on the 'mite' module.
+
+References:
+DAQ 660x Register-Level Programmer Manual (NI 370505A-01)
+DAQ 6601/6602 User Manual (NI 322137B-01)
+340934b.pdf DAQ-STC reference manual
+
+*/
+/*
+TODO:
+ Support use of both banks X and Y
+*/
+
+#include "ni_tio_internal.h"
+#include "mite.h"
+
+MODULE_AUTHOR("Comedi <comedi@comedi.org>");
+MODULE_DESCRIPTION("Comedi command support for NI general-purpose counters");
+MODULE_LICENSE("GPL");
+
+static void ni_tio_configure_dma(struct ni_gpct *counter, short enable,
+ short read_not_write)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned input_select_bits = 0;
+
+ if (enable) {
+ if (read_not_write) {
+ input_select_bits |= Gi_Read_Acknowledges_Irq;
+ } else {
+ input_select_bits |= Gi_Write_Acknowledges_Irq;
+ }
+ }
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Input_Select_Reg(counter->counter_index),
+ Gi_Read_Acknowledges_Irq | Gi_Write_Acknowledges_Irq,
+ input_select_bits);
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_e_series:
+ break;
+ case ni_gpct_variant_m_series:
+ case ni_gpct_variant_660x:
+ {
+ unsigned gi_dma_config_bits = 0;
+
+ if (enable) {
+ gi_dma_config_bits |= Gi_DMA_Enable_Bit;
+ gi_dma_config_bits |= Gi_DMA_Int_Bit;
+ }
+ if (read_not_write == 0) {
+ gi_dma_config_bits |= Gi_DMA_Write_Bit;
+ }
+ ni_tio_set_bits(counter,
+ NITIO_Gi_DMA_Config_Reg(counter->counter_index),
+ Gi_DMA_Enable_Bit | Gi_DMA_Int_Bit |
+ Gi_DMA_Write_Bit, gi_dma_config_bits);
+ }
+ break;
+ }
+}
+
+static int ni_tio_input_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trignum)
+{
+ unsigned long flags;
+ int retval = 0;
+ struct ni_gpct *counter = s->private;
+
+ BUG_ON(counter == NULL);
+ if (trignum != 0)
+ return -EINVAL;
+
+ comedi_spin_lock_irqsave(&counter->lock, flags);
+ if (counter->mite_chan)
+ mite_dma_arm(counter->mite_chan);
+ else
+ retval = -EIO;
+ comedi_spin_unlock_irqrestore(&counter->lock, flags);
+ if (retval < 0)
+ return retval;
+ retval = ni_tio_arm(counter, 1, NI_GPCT_ARM_IMMEDIATE);
+ s->async->inttrig = NULL;
+
+ return retval;
+}
+
+static int ni_tio_input_cmd(struct ni_gpct *counter, struct comedi_async *async)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ struct comedi_cmd *cmd = &async->cmd;
+ int retval = 0;
+
+ /* write alloc the entire buffer */
+ comedi_buf_write_alloc(async, async->prealloc_bufsz);
+ counter->mite_chan->dir = COMEDI_INPUT;
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_m_series:
+ case ni_gpct_variant_660x:
+ mite_prep_dma(counter->mite_chan, 32, 32);
+ break;
+ case ni_gpct_variant_e_series:
+ mite_prep_dma(counter->mite_chan, 16, 32);
+ break;
+ default:
+ BUG();
+ break;
+ }
+ ni_tio_set_bits(counter, NITIO_Gi_Command_Reg(counter->counter_index),
+ Gi_Save_Trace_Bit, 0);
+ ni_tio_configure_dma(counter, 1, 1);
+ switch (cmd->start_src) {
+ case TRIG_NOW:
+ async->inttrig = NULL;
+ mite_dma_arm(counter->mite_chan);
+ retval = ni_tio_arm(counter, 1, NI_GPCT_ARM_IMMEDIATE);
+ break;
+ case TRIG_INT:
+ async->inttrig = &ni_tio_input_inttrig;
+ break;
+ case TRIG_EXT:
+ async->inttrig = NULL;
+ mite_dma_arm(counter->mite_chan);
+ retval = ni_tio_arm(counter, 1, cmd->start_arg);
+ case TRIG_OTHER:
+ async->inttrig = NULL;
+ mite_dma_arm(counter->mite_chan);
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return retval;
+}
+
+static int ni_tio_output_cmd(struct ni_gpct *counter, struct comedi_async *async)
+{
+ rt_printk("ni_tio: output commands not yet implemented.\n");
+ return -ENOTSUPP;
+
+ counter->mite_chan->dir = COMEDI_OUTPUT;
+ mite_prep_dma(counter->mite_chan, 32, 32);
+ ni_tio_configure_dma(counter, 1, 0);
+ mite_dma_arm(counter->mite_chan);
+ return ni_tio_arm(counter, 1, NI_GPCT_ARM_IMMEDIATE);
+}
+
+static int ni_tio_cmd_setup(struct ni_gpct *counter, struct comedi_async *async)
+{
+ struct comedi_cmd *cmd = &async->cmd;
+ int set_gate_source = 0;
+ unsigned gate_source;
+ int retval = 0;
+
+ if (cmd->scan_begin_src == TRIG_EXT) {
+ set_gate_source = 1;
+ gate_source = cmd->scan_begin_arg;
+ } else if (cmd->convert_src == TRIG_EXT) {
+ set_gate_source = 1;
+ gate_source = cmd->convert_arg;
+ }
+ if (set_gate_source) {
+ retval = ni_tio_set_gate_src(counter, 0, gate_source);
+ }
+ if (cmd->flags & TRIG_WAKE_EOS) {
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Interrupt_Enable_Reg(counter->counter_index),
+ Gi_Gate_Interrupt_Enable_Bit(counter->counter_index),
+ Gi_Gate_Interrupt_Enable_Bit(counter->counter_index));
+ }
+ return retval;
+}
+
+int ni_tio_cmd(struct ni_gpct *counter, struct comedi_async *async)
+{
+ struct comedi_cmd *cmd = &async->cmd;
+ int retval = 0;
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&counter->lock, flags);
+ if (counter->mite_chan == NULL) {
+ rt_printk
+ ("ni_tio: commands only supported with DMA. Interrupt-driven commands not yet implemented.\n");
+ retval = -EIO;
+ } else {
+ retval = ni_tio_cmd_setup(counter, async);
+ if (retval == 0) {
+ if (cmd->flags & CMDF_WRITE) {
+ retval = ni_tio_output_cmd(counter, async);
+ } else {
+ retval = ni_tio_input_cmd(counter, async);
+ }
+ }
+ }
+ comedi_spin_unlock_irqrestore(&counter->lock, flags);
+ return retval;
+}
+
+int ni_tio_cmdtest(struct ni_gpct *counter, struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+ int sources;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ sources = TRIG_NOW | TRIG_INT | TRIG_OTHER;
+ if (ni_tio_counting_mode_registers_present(counter->counter_dev))
+ sources |= TRIG_EXT;
+ cmd->start_src &= sources;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_EXT | TRIG_OTHER;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ sources = TRIG_NOW | TRIG_EXT | TRIG_OTHER;
+ cmd->convert_src &= sources;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique... */
+
+ if (cmd->start_src != TRIG_NOW &&
+ cmd->start_src != TRIG_INT &&
+ cmd->start_src != TRIG_EXT && cmd->start_src != TRIG_OTHER)
+ err++;
+ if (cmd->scan_begin_src != TRIG_FOLLOW &&
+ cmd->scan_begin_src != TRIG_EXT &&
+ cmd->scan_begin_src != TRIG_OTHER)
+ err++;
+ if (cmd->convert_src != TRIG_OTHER &&
+ cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW)
+ err++;
+ if (cmd->stop_src != TRIG_NONE)
+ err++;
+ /* ... and mutually compatible */
+ if (cmd->convert_src != TRIG_NOW && cmd->scan_begin_src != TRIG_FOLLOW)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+ if (cmd->start_src != TRIG_EXT) {
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ }
+ if (cmd->scan_begin_src != TRIG_EXT) {
+ if (cmd->scan_begin_arg) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ }
+ if (cmd->convert_src != TRIG_EXT) {
+ if (cmd->convert_arg) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ if (cmd->stop_src == TRIG_NONE) {
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+int ni_tio_cancel(struct ni_gpct *counter)
+{
+ unsigned long flags;
+
+ ni_tio_arm(counter, 0, 0);
+ comedi_spin_lock_irqsave(&counter->lock, flags);
+ if (counter->mite_chan) {
+ mite_dma_disarm(counter->mite_chan);
+ }
+ comedi_spin_unlock_irqrestore(&counter->lock, flags);
+ ni_tio_configure_dma(counter, 0, 0);
+
+ ni_tio_set_bits(counter,
+ NITIO_Gi_Interrupt_Enable_Reg(counter->counter_index),
+ Gi_Gate_Interrupt_Enable_Bit(counter->counter_index), 0x0);
+ return 0;
+}
+
+ /* During buffered input counter operation for e-series, the gate interrupt is acked
+ automatically by the dma controller, due to the Gi_Read/Write_Acknowledges_IRQ bits
+ in the input select register. */
+static int should_ack_gate(struct ni_gpct *counter)
+{
+ unsigned long flags;
+ int retval = 0;
+
+ switch (counter->counter_dev->variant) {
+ case ni_gpct_variant_m_series:
+ case ni_gpct_variant_660x: // not sure if 660x really supports gate interrupts (the bits are not listed in register-level manual)
+ return 1;
+ break;
+ case ni_gpct_variant_e_series:
+ comedi_spin_lock_irqsave(&counter->lock, flags);
+ {
+ if (counter->mite_chan == NULL ||
+ counter->mite_chan->dir != COMEDI_INPUT ||
+ (mite_done(counter->mite_chan))) {
+ retval = 1;
+ }
+ }
+ comedi_spin_unlock_irqrestore(&counter->lock, flags);
+ break;
+ }
+ return retval;
+}
+
+void ni_tio_acknowledge_and_confirm(struct ni_gpct *counter, int *gate_error,
+ int *tc_error, int *perm_stale_data, int *stale_data)
+{
+ const unsigned short gxx_status = read_register(counter,
+ NITIO_Gxx_Status_Reg(counter->counter_index));
+ const unsigned short gi_status = read_register(counter,
+ NITIO_Gi_Status_Reg(counter->counter_index));
+ unsigned ack = 0;
+
+ if (gate_error)
+ *gate_error = 0;
+ if (tc_error)
+ *tc_error = 0;
+ if (perm_stale_data)
+ *perm_stale_data = 0;
+ if (stale_data)
+ *stale_data = 0;
+
+ if (gxx_status & Gi_Gate_Error_Bit(counter->counter_index)) {
+ ack |= Gi_Gate_Error_Confirm_Bit(counter->counter_index);
+ if (gate_error) {
+ /*660x don't support automatic acknowledgement of gate interrupt via dma read/write
+ and report bogus gate errors */
+ if (counter->counter_dev->variant !=
+ ni_gpct_variant_660x) {
+ *gate_error = 1;
+ }
+ }
+ }
+ if (gxx_status & Gi_TC_Error_Bit(counter->counter_index)) {
+ ack |= Gi_TC_Error_Confirm_Bit(counter->counter_index);
+ if (tc_error)
+ *tc_error = 1;
+ }
+ if (gi_status & Gi_TC_Bit) {
+ ack |= Gi_TC_Interrupt_Ack_Bit;
+ }
+ if (gi_status & Gi_Gate_Interrupt_Bit) {
+ if (should_ack_gate(counter))
+ ack |= Gi_Gate_Interrupt_Ack_Bit;
+ }
+ if (ack)
+ write_register(counter, ack,
+ NITIO_Gi_Interrupt_Acknowledge_Reg(counter->
+ counter_index));
+ if (ni_tio_get_soft_copy(counter,
+ NITIO_Gi_Mode_Reg(counter->
+ counter_index)) & Gi_Loading_On_Gate_Bit) {
+ if (gxx_status & Gi_Stale_Data_Bit(counter->counter_index)) {
+ if (stale_data)
+ *stale_data = 1;
+ }
+ if (read_register(counter,
+ NITIO_Gxx_Joint_Status2_Reg(counter->
+ counter_index)) &
+ Gi_Permanent_Stale_Bit(counter->counter_index)) {
+ rt_printk("%s: Gi_Permanent_Stale_Data detected.\n",
+ __FUNCTION__);
+ if (perm_stale_data)
+ *perm_stale_data = 1;
+ }
+ }
+}
+
+void ni_tio_handle_interrupt(struct ni_gpct *counter, struct comedi_subdevice * s)
+{
+ unsigned gpct_mite_status;
+ unsigned long flags;
+ int gate_error;
+ int tc_error;
+ int perm_stale_data;
+
+ ni_tio_acknowledge_and_confirm(counter, &gate_error, &tc_error,
+ &perm_stale_data, NULL);
+ if (gate_error) {
+ rt_printk("%s: Gi_Gate_Error detected.\n", __FUNCTION__);
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ }
+ if (perm_stale_data) {
+ s->async->events |= COMEDI_CB_ERROR;
+ }
+ switch (counter->counter_dev->variant) {
+ case ni_gpct_variant_m_series:
+ case ni_gpct_variant_660x:
+ if (read_register(counter,
+ NITIO_Gi_DMA_Status_Reg(counter->
+ counter_index)) & Gi_DRQ_Error_Bit) {
+ rt_printk("%s: Gi_DRQ_Error detected.\n", __FUNCTION__);
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ }
+ break;
+ case ni_gpct_variant_e_series:
+ break;
+ }
+ comedi_spin_lock_irqsave(&counter->lock, flags);
+ if (counter->mite_chan == NULL) {
+ comedi_spin_unlock_irqrestore(&counter->lock, flags);
+ return;
+ }
+ gpct_mite_status = mite_get_status(counter->mite_chan);
+ if (gpct_mite_status & CHSR_LINKC) {
+ writel(CHOR_CLRLC,
+ counter->mite_chan->mite->mite_io_addr +
+ MITE_CHOR(counter->mite_chan->channel));
+ }
+ mite_sync_input_dma(counter->mite_chan, s->async);
+ comedi_spin_unlock_irqrestore(&counter->lock, flags);
+}
+
+void ni_tio_set_mite_channel(struct ni_gpct *counter,
+ struct mite_channel *mite_chan)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&counter->lock, flags);
+ counter->mite_chan = mite_chan;
+ comedi_spin_unlock_irqrestore(&counter->lock, flags);
+}
+
+static int __init ni_tiocmd_init_module(void)
+{
+ return 0;
+}
+
+module_init(ni_tiocmd_init_module);
+
+static void __exit ni_tiocmd_cleanup_module(void)
+{
+}
+
+module_exit(ni_tiocmd_cleanup_module);
+
+EXPORT_SYMBOL_GPL(ni_tio_cmd);
+EXPORT_SYMBOL_GPL(ni_tio_cmdtest);
+EXPORT_SYMBOL_GPL(ni_tio_cancel);
+EXPORT_SYMBOL_GPL(ni_tio_handle_interrupt);
+EXPORT_SYMBOL_GPL(ni_tio_set_mite_channel);
+EXPORT_SYMBOL_GPL(ni_tio_acknowledge_and_confirm);
diff --git a/drivers/staging/comedi/drivers/pcl711.c b/drivers/staging/comedi/drivers/pcl711.c
new file mode 100644
index 000000000000..ce42506d47b5
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcl711.c
@@ -0,0 +1,623 @@
+/*
+ comedi/drivers/pcl711.c
+ hardware driver for PC-LabCard PCL-711 and AdSys ACL-8112
+ and compatibles
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+ Janne Jalkanen <jalkanen@cs.hut.fi>
+ Eric Bunn <ebu@cs.hut.fi>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+/*
+Driver: pcl711
+Description: Advantech PCL-711 and 711b, ADLink ACL-8112
+Author: ds, Janne Jalkanen <jalkanen@cs.hut.fi>, Eric Bunn <ebu@cs.hut.fi>
+Status: mostly complete
+Devices: [Advantech] PCL-711 (pcl711), PCL-711B (pcl711b),
+ [AdLink] ACL-8112HG (acl8112hg), ACL-8112DG (acl8112dg)
+
+Since these boards do not have DMA or FIFOs, only immediate mode is
+supported.
+
+*/
+
+/*
+ Dave Andruczyk <dave@tech.buffalostate.edu> also wrote a
+ driver for the PCL-711. I used a few ideas from his driver
+ here. His driver also has more comments, if you are
+ interested in understanding how this driver works.
+ http://tech.buffalostate.edu/~dave/driver/
+
+ The ACL-8112 driver was hacked from the sources of the PCL-711
+ driver (the 744 chip used on the 8112 is almost the same as
+ the 711b chip, but it has more I/O channels) by
+ Janne Jalkanen (jalkanen@cs.hut.fi) and
+ Erik Bunn (ebu@cs.hut.fi). Remerged with the PCL-711 driver
+ by ds.
+
+ [acl-8112]
+ This driver supports both TRIGNOW and TRIGCLK,
+ but does not yet support DMA transfers. It also supports
+ both high (HG) and low (DG) versions of the card, though
+ the HG version has been untested.
+
+ */
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+
+#include "8253.h"
+
+#define PCL711_SIZE 16
+
+#define PCL711_CTR0 0
+#define PCL711_CTR1 1
+#define PCL711_CTR2 2
+#define PCL711_CTRCTL 3
+#define PCL711_AD_LO 4
+#define PCL711_DA0_LO 4
+#define PCL711_AD_HI 5
+#define PCL711_DA0_HI 5
+#define PCL711_DI_LO 6
+#define PCL711_DA1_LO 6
+#define PCL711_DI_HI 7
+#define PCL711_DA1_HI 7
+#define PCL711_CLRINTR 8
+#define PCL711_GAIN 9
+#define PCL711_MUX 10
+#define PCL711_MODE 11
+#define PCL711_SOFTTRIG 12
+#define PCL711_DO_LO 13
+#define PCL711_DO_HI 14
+
+static const struct comedi_lrange range_pcl711b_ai = { 5, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ BIP_RANGE(0.3125)
+ }
+};
+static const struct comedi_lrange range_acl8112hg_ai = { 12, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.005),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01),
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.01)
+ }
+};
+static const struct comedi_lrange range_acl8112dg_ai = { 9, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ BIP_RANGE(10)
+ }
+};
+
+/*
+ * flags
+ */
+
+#define PCL711_TIMEOUT 100
+#define PCL711_DRDY 0x10
+
+static const int i8253_osc_base = 500; /* 2 Mhz */
+
+struct pcl711_board {
+
+ const char *name;
+ int is_pcl711b;
+ int is_8112;
+ int is_dg;
+ int n_ranges;
+ int n_aichan;
+ int n_aochan;
+ int maxirq;
+ const struct comedi_lrange *ai_range_type;
+};
+
+
+static const struct pcl711_board boardtypes[] = {
+ {"pcl711", 0, 0, 0, 5, 8, 1, 0, &range_bipolar5},
+ {"pcl711b", 1, 0, 0, 5, 8, 1, 7, &range_pcl711b_ai},
+ {"acl8112hg", 0, 1, 0, 12, 16, 2, 15, &range_acl8112hg_ai},
+ {"acl8112dg", 0, 1, 1, 9, 16, 2, 15, &range_acl8112dg_ai},
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl711_board))
+#define this_board ((const struct pcl711_board *)dev->board_ptr)
+
+static int pcl711_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcl711_detach(struct comedi_device * dev);
+static struct comedi_driver driver_pcl711 = {
+ driver_name:"pcl711",
+ module:THIS_MODULE,
+ attach:pcl711_attach,
+ detach:pcl711_detach,
+ board_name:&boardtypes[0].name,
+ num_names:n_boardtypes,
+ offset:sizeof(struct pcl711_board),
+};
+
+COMEDI_INITCLEANUP(driver_pcl711);
+
+struct pcl711_private {
+
+ int board;
+ int adchan;
+ int ntrig;
+ int aip[8];
+ int mode;
+ unsigned int ao_readback[2];
+ unsigned int divisor1;
+ unsigned int divisor2;
+};
+
+
+#define devpriv ((struct pcl711_private *)dev->private)
+
+static irqreturn_t pcl711_interrupt(int irq, void *d PT_REGS_ARG)
+{
+ int lo, hi;
+ int data;
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+
+ if (!dev->attached) {
+ comedi_error(dev, "spurious interrupt");
+ return IRQ_HANDLED;
+ }
+
+ hi = inb(dev->iobase + PCL711_AD_HI);
+ lo = inb(dev->iobase + PCL711_AD_LO);
+ outb(0, dev->iobase + PCL711_CLRINTR);
+
+ data = (hi << 8) | lo;
+
+ /* FIXME! Nothing else sets ntrig! */
+ if (!(--devpriv->ntrig)) {
+ if (this_board->is_8112) {
+ outb(1, dev->iobase + PCL711_MODE);
+ } else {
+ outb(0, dev->iobase + PCL711_MODE);
+ }
+
+ s->async->events |= COMEDI_CB_EOA;
+ }
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+}
+
+static void pcl711_set_changain(struct 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_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i, n;
+ int hi, lo;
+
+ pcl711_set_changain(dev, insn->chanspec);
+
+ for (n = 0; n < insn->n; n++) {
+ /*
+ * Write the correct mode (software polling) and start polling by writing
+ * to the trigger register
+ */
+ outb(1, dev->iobase + PCL711_MODE);
+
+ if (this_board->is_8112) {
+ } else {
+ outb(0, dev->iobase + PCL711_SOFTTRIG);
+ }
+
+ i = PCL711_TIMEOUT;
+ while (--i) {
+ hi = inb(dev->iobase + PCL711_AD_HI);
+ if (!(hi & PCL711_DRDY))
+ goto ok;
+ comedi_udelay(1);
+ }
+ rt_printk("comedi%d: pcl711: A/D timeout\n", dev->minor);
+ return -ETIME;
+
+ ok:
+ lo = inb(dev->iobase + PCL711_AD_LO);
+
+ data[n] = ((hi & 0xf) << 8) | lo;
+ }
+
+ return n;
+}
+
+static int pcl711_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int tmp;
+ int err = 0;
+
+ /* step 1 */
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2 */
+
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_EXT)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3 */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+ if (cmd->scan_begin_src == TRIG_EXT) {
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ } else {
+#define MAX_SPEED 1000
+#define TIMER_BASE 100
+ if (cmd->scan_begin_arg < MAX_SPEED) {
+ cmd->scan_begin_arg = MAX_SPEED;
+ err++;
+ }
+ }
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_NONE) {
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ } else {
+ /* ignore */
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4 */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE,
+ &devpriv->divisor1, &devpriv->divisor2,
+ &cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int pcl711_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int timer1, timer2;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ pcl711_set_changain(dev, cmd->chanlist[0]);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /*
+ * 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
+ */
+
+ i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
+ &cmd->scan_begin_arg, TRIG_ROUND_NEAREST);
+
+ outb(0x74, dev->iobase + PCL711_CTRCTL);
+ outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
+ outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
+ outb(0xb4, dev->iobase + PCL711_CTRCTL);
+ outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
+ outb((timer2 >> 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);
+ } else {
+ /* external trigger */
+ outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
+ }
+
+ return 0;
+}
+
+/*
+ analog output
+*/
+static int pcl711_ao_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (n = 0; n < insn->n; n++) {
+ outb((data[n] & 0xff),
+ dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
+ outb((data[n] >> 8),
+ dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
+
+ devpriv->ao_readback[chan] = data[n];
+ }
+
+ return n;
+}
+
+static int pcl711_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (n = 0; n < insn->n; n++) {
+ data[n] = devpriv->ao_readback[chan];
+ }
+
+ return n;
+
+}
+
+/* Digital port read - Untested on 8112 */
+static int pcl711_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ data[1] = inb(dev->iobase + PCL711_DI_LO) |
+ (inb(dev->iobase + PCL711_DI_HI) << 8);
+
+ return 2;
+}
+
+/* Digital port write - Untested on 8112 */
+static int pcl711_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+ }
+ if (data[0] & 0x00ff)
+ outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
+ if (data[0] & 0xff00)
+ outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
+
+ data[1] = s->state;
+
+ return 2;
+}
+
+/* Free any resources that we have claimed */
+static int pcl711_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: pcl711: remove\n", dev->minor);
+
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+
+ if (dev->iobase)
+ release_region(dev->iobase, PCL711_SIZE);
+
+ return 0;
+}
+
+/* Initialization */
+static int pcl711_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int ret;
+ unsigned long iobase;
+ unsigned int irq;
+ struct comedi_subdevice *s;
+
+ /* claim our I/O space */
+
+ iobase = it->options[0];
+ printk("comedi%d: pcl711: 0x%04lx ", dev->minor, iobase);
+ if (!request_region(iobase, PCL711_SIZE, "pcl711")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ /* there should be a sanity check here */
+
+ /* set up some name stuff */
+ dev->board_name = this_board->name;
+
+ /* grab our IRQ */
+ irq = it->options[1];
+ if (irq > this_board->maxirq) {
+ printk("irq out of range\n");
+ return -EINVAL;
+ }
+ if (irq) {
+ if (comedi_request_irq(irq, pcl711_interrupt, 0, "pcl711", dev)) {
+ printk("unable to allocate irq %u\n", irq);
+ return -EINVAL;
+ } else {
+ printk("( irq = %u )\n", irq);
+ }
+ }
+ dev->irq = irq;
+
+ if ((ret = alloc_subdevices(dev, 4)) < 0)
+ return ret;
+ if ((ret = alloc_private(dev, sizeof(struct pcl711_private))) < 0)
+ return ret;
+
+ s = dev->subdevices + 0;
+ /* AI subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = this_board->n_aichan;
+ s->maxdata = 0xfff;
+ s->len_chanlist = 1;
+ s->range_table = this_board->ai_range_type;
+ s->insn_read = pcl711_ai_insn;
+ if (irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->do_cmdtest = pcl711_ai_cmdtest;
+ s->do_cmd = pcl711_ai_cmd;
+ }
+
+ s++;
+ /* AO subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = this_board->n_aochan;
+ s->maxdata = 0xfff;
+ s->len_chanlist = 1;
+ s->range_table = &range_bipolar5;
+ s->insn_write = pcl711_ao_insn;
+ s->insn_read = pcl711_ao_insn_read;
+
+ 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_table = &range_digital;
+ s->insn_bits = pcl711_di_insn_bits;
+
+ s++;
+ /* 16-bit digital out */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->len_chanlist = 16;
+ s->range_table = &range_digital;
+ s->state = 0;
+ s->insn_bits = pcl711_do_insn_bits;
+
+ /*
+ 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;
+}
diff --git a/drivers/staging/comedi/drivers/pcl724.c b/drivers/staging/comedi/drivers/pcl724.c
new file mode 100644
index 000000000000..cd1e784f25f9
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcl724.c
@@ -0,0 +1,222 @@
+/*
+ comedi/drivers/pcl724.c
+
+ Michal Dobes <dobes@tesnet.cz>
+
+ hardware driver for Advantech cards:
+ card: PCL-724, PCL-722, PCL-731
+ driver: pcl724, pcl722, pcl731
+ and ADLink cards:
+ card: ACL-7122, ACL-7124, PET-48DIO
+ driver: acl7122, acl7124, pet48dio
+
+ Options for PCL-724, PCL-731, ACL-7124 and PET-48DIO:
+ [0] - IO Base
+
+ Options for PCL-722 and ACL-7122:
+ [0] - IO Base
+ [1] - IRQ (0=disable IRQ) IRQ isn't supported at this time!
+ [2] -number of DIO:
+ 0, 144: 144 DIO configuration
+ 1, 96: 96 DIO configuration
+*/
+/*
+Driver: pcl724
+Description: Advantech PCL-724, PCL-722, PCL-731 ADLink ACL-7122, ACL-7124,
+ PET-48DIO
+Author: Michal Dobes <dobes@tesnet.cz>
+Devices: [Advantech] PCL-724 (pcl724), PCL-722 (pcl722), PCL-731 (pcl731),
+ [ADLink] ACL-7122 (acl7122), ACL-7124 (acl7124), PET-48DIO (pet48dio)
+Status: untested
+
+This is driver for digital I/O boards PCL-722/724/731 with 144/24/48 DIO
+and for digital I/O boards ACL-7122/7124/PET-48DIO with 144/24/48 DIO.
+It need 8255.o for operations and only immediate mode is supported.
+See the source for configuration details.
+*/
+/*
+ * check_driver overrides:
+ * struct comedi_insn
+ */
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+
+#include "8255.h"
+
+#define PCL722_SIZE 32
+#define PCL722_96_SIZE 16
+#define PCL724_SIZE 4
+#define PCL731_SIZE 8
+#define PET48_SIZE 2
+
+#define SIZE_8255 4
+
+// #define PCL724_IRQ 1 /* no IRQ support now */
+
+static int pcl724_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcl724_detach(struct comedi_device * dev);
+
+struct pcl724_board {
+
+ const char *name; // board name
+ int dio; // num of DIO
+ int numofports; // num of 8255 subdevices
+ unsigned int IRQbits; // allowed interrupts
+ unsigned int io_range; // len of IO space
+ char can_have96;
+ char is_pet48;
+};
+
+
+static const struct pcl724_board boardtypes[] = {
+ {"pcl724", 24, 1, 0x00fc, PCL724_SIZE, 0, 0,},
+ {"pcl722", 144, 6, 0x00fc, PCL722_SIZE, 1, 0,},
+ {"pcl731", 48, 2, 0x9cfc, PCL731_SIZE, 0, 0,},
+ {"acl7122", 144, 6, 0x9ee8, PCL722_SIZE, 1, 0,},
+ {"acl7124", 24, 1, 0x00fc, PCL724_SIZE, 0, 0,},
+ {"pet48dio", 48, 2, 0x9eb8, PET48_SIZE, 0, 1,},
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl724_board))
+#define this_board ((const struct pcl724_board *)dev->board_ptr)
+
+static struct comedi_driver driver_pcl724 = {
+ driver_name:"pcl724",
+ module:THIS_MODULE,
+ attach:pcl724_attach,
+ detach:pcl724_detach,
+ board_name:&boardtypes[0].name,
+ num_names:n_boardtypes,
+ offset:sizeof(struct pcl724_board),
+};
+
+COMEDI_INITCLEANUP(driver_pcl724);
+
+static int subdev_8255_cb(int dir, int port, int data, unsigned long arg)
+{
+ unsigned long iobase = arg;
+
+ if (dir) {
+ outb(data, iobase + port);
+ return 0;
+ } else {
+ return inb(iobase + port);
+ }
+}
+
+static int subdev_8255mapped_cb(int dir, int port, int data,
+ unsigned long iobase)
+{
+ int movport = SIZE_8255 * (iobase >> 12);
+
+ iobase &= 0x0fff;
+
+ if (dir) {
+ outb(port + movport, iobase);
+ outb(data, iobase + 1);
+ return 0;
+ } else {
+ outb(port + movport, iobase);
+ return inb(iobase + 1);
+ }
+}
+
+static int pcl724_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ unsigned long iobase;
+ unsigned int iorange;
+ int ret, i, n_subdevices;
+#ifdef PCL724_IRQ
+ unsigned int irq;
+#endif
+
+ iobase = it->options[0];
+ iorange = this_board->io_range;
+ if ((this_board->can_have96) && ((it->options[1] == 1)
+ || (it->options[1] == 96)))
+ iorange = PCL722_96_SIZE; // PCL-724 in 96 DIO configuration
+ printk("comedi%d: pcl724: board=%s, 0x%03lx ", dev->minor,
+ this_board->name, iobase);
+ if (!request_region(iobase, iorange, "pcl724")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+
+ dev->iobase = iobase;
+
+ dev->board_name = this_board->name;
+
+#ifdef PCL724_IRQ
+ irq = 0;
+ if (this_board->IRQbits != 0) { /* board support IRQ */
+ irq = it->options[1];
+ if (irq) { /* we want to use IRQ */
+ if (((1 << irq) & this_board->IRQbits) == 0) {
+ rt_printk
+ (", IRQ %u is out of allowed range, DISABLING IT",
+ irq);
+ irq = 0; /* Bad IRQ */
+ } else {
+ if (comedi_request_irq(irq, interrupt_pcl724, 0,
+ "pcl724", dev)) {
+ rt_printk
+ (", unable to allocate IRQ %u, DISABLING IT",
+ irq);
+ irq = 0; /* Can't use IRQ */
+ } else {
+ rt_printk(", irq=%u", irq);
+ }
+ }
+ }
+ }
+
+ dev->irq = irq;
+#endif
+
+ printk("\n");
+
+ n_subdevices = this_board->numofports;
+ if ((this_board->can_have96) && ((it->options[1] == 1)
+ || (it->options[1] == 96)))
+ n_subdevices = 4; // PCL-724 in 96 DIO configuration
+
+ if ((ret = alloc_subdevices(dev, n_subdevices)) < 0)
+ return ret;
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ if (this_board->is_pet48) {
+ subdev_8255_init(dev, dev->subdevices + i,
+ subdev_8255mapped_cb,
+ (unsigned long)(dev->iobase + i * 0x1000));
+ } else
+ subdev_8255_init(dev, dev->subdevices + i,
+ subdev_8255_cb,
+ (unsigned long)(dev->iobase + SIZE_8255 * i));
+ };
+
+ return 0;
+}
+
+static int pcl724_detach(struct comedi_device * dev)
+{
+ int i;
+
+// printk("comedi%d: pcl724: remove\n",dev->minor);
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ subdev_8255_cleanup(dev, dev->subdevices + i);
+ }
+
+#ifdef PCL724_IRQ
+ if (dev->irq) {
+ comedi_free_irq(dev->irq, dev);
+ }
+#endif
+
+ release_region(dev->iobase, this_board->io_range);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/pcl725.c b/drivers/staging/comedi/drivers/pcl725.c
new file mode 100644
index 000000000000..0766ba02bd03
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcl725.c
@@ -0,0 +1,111 @@
+/*
+ * comedi/drivers/pcl725.c
+ * Driver for PCL725 and clones
+ * David A. Schleef
+ */
+/*
+Driver: pcl725
+Description: Advantech PCL-725 (& compatibles)
+Author: ds
+Status: unknown
+Devices: [Advantech] PCL-725 (pcl725)
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+#define PCL725_SIZE 2
+
+#define PCL725_DO 0
+#define PCL725_DI 1
+
+static int pcl725_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcl725_detach(struct comedi_device * dev);
+static struct comedi_driver driver_pcl725 = {
+ driver_name:"pcl725",
+ module:THIS_MODULE,
+ attach:pcl725_attach,
+ detach:pcl725_detach,
+};
+
+COMEDI_INITCLEANUP(driver_pcl725);
+
+static int pcl725_do_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ outb(s->state, dev->iobase + PCL725_DO);
+ }
+
+ data[1] = s->state;
+
+ return 2;
+}
+
+static int pcl725_di_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ data[1] = inb(dev->iobase + PCL725_DI);
+
+ return 2;
+}
+
+static int pcl725_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+
+ iobase = it->options[0];
+ printk("comedi%d: pcl725: 0x%04lx ", dev->minor, iobase);
+ if (!request_region(iobase, PCL725_SIZE, "pcl725")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->board_name = "pcl725";
+ dev->iobase = iobase;
+ dev->irq = 0;
+
+ if (alloc_subdevices(dev, 2) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ /* do */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->maxdata = 1;
+ s->n_chan = 8;
+ s->insn_bits = pcl725_do_insn;
+ s->range_table = &range_digital;
+
+ s = dev->subdevices + 1;
+ /* di */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->maxdata = 1;
+ s->n_chan = 8;
+ s->insn_bits = pcl725_di_insn;
+ s->range_table = &range_digital;
+
+ printk("\n");
+
+ return 0;
+}
+
+static int pcl725_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: pcl725: remove\n", dev->minor);
+
+ if (dev->iobase)
+ release_region(dev->iobase, PCL725_SIZE);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/pcl726.c b/drivers/staging/comedi/drivers/pcl726.c
new file mode 100644
index 000000000000..6dbd33d7907b
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcl726.c
@@ -0,0 +1,377 @@
+/*
+ comedi/drivers/pcl726.c
+
+ hardware driver for Advantech cards:
+ card: PCL-726, PCL-727, PCL-728
+ driver: pcl726, pcl727, pcl728
+ and for ADLink cards:
+ card: ACL-6126, ACL-6128
+ driver: acl6126, acl6128
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: pcl726
+Description: Advantech PCL-726 & compatibles
+Author: ds
+Status: untested
+Devices: [Advantech] PCL-726 (pcl726), PCL-727 (pcl727), PCL-728 (pcl728),
+ [ADLink] ACL-6126 (acl6126), ACL-6128 (acl6128)
+
+Interrupts are not supported.
+
+ Options for PCL-726:
+ [0] - IO Base
+ [2]...[7] - D/A output range for channel 1-6:
+ 0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V,
+ 4: 4-20mA, 5: unknown (external reference)
+
+ Options for PCL-727:
+ [0] - IO Base
+ [2]...[13] - D/A output range for channel 1-12:
+ 0: 0-5V, 1: 0-10V, 2: +/-5V,
+ 3: 4-20mA
+
+ Options for PCL-728 and ACL-6128:
+ [0] - IO Base
+ [2], [3] - D/A output range for channel 1 and 2:
+ 0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V,
+ 4: 4-20mA, 5: 0-20mA
+
+ Options for ACL-6126:
+ [0] - IO Base
+ [1] - IRQ (0=disable, 3, 5, 6, 7, 9, 10, 11, 12, 15) (currently ignored)
+ [2]...[7] - D/A output range for channel 1-6:
+ 0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V,
+ 4: 4-20mA
+*/
+
+/*
+ Thanks to Circuit Specialists for having programming info (!) on
+ their web page. (http://www.cir.com/)
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+#undef ACL6126_IRQ /* no interrupt support (yet) */
+
+#define PCL726_SIZE 16
+#define PCL727_SIZE 32
+#define PCL728_SIZE 8
+
+#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
+
+#define PCL727_DO_HI 24
+#define PCL727_DO_LO 25
+#define PCL727_DI_HI 0
+#define PCL727_DI_LO 1
+
+static const struct comedi_lrange range_4_20mA = { 1, {RANGE_mA(4, 20)} };
+static const struct comedi_lrange range_0_20mA = { 1, {RANGE_mA(0, 20)} };
+
+static const struct comedi_lrange *const rangelist_726[] = {
+ &range_unipolar5, &range_unipolar10,
+ &range_bipolar5, &range_bipolar10,
+ &range_4_20mA, &range_unknown
+};
+
+static const struct comedi_lrange *const rangelist_727[] = {
+ &range_unipolar5, &range_unipolar10,
+ &range_bipolar5,
+ &range_4_20mA
+};
+
+static const struct comedi_lrange *const rangelist_728[] = {
+ &range_unipolar5, &range_unipolar10,
+ &range_bipolar5, &range_bipolar10,
+ &range_4_20mA, &range_0_20mA
+};
+
+static int pcl726_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcl726_detach(struct comedi_device * dev);
+
+struct pcl726_board {
+
+ const char *name; // driver name
+ int n_aochan; // num of D/A chans
+ int num_of_ranges; // num of ranges
+ unsigned int IRQbits; // allowed interrupts
+ unsigned int io_range; // len of IO space
+ char have_dio; // 1=card have DI/DO ports
+ int di_hi; // ports for DI/DO operations
+ int di_lo;
+ int do_hi;
+ int do_lo;
+ const struct comedi_lrange *const *range_type_list; // list of supported ranges
+};
+
+
+static const struct pcl726_board boardtypes[] = {
+ {"pcl726", 6, 6, 0x0000, PCL726_SIZE, 1,
+ PCL726_DI_HI, PCL726_DI_LO, PCL726_DO_HI, PCL726_DO_LO,
+ &rangelist_726[0],},
+ {"pcl727", 12, 4, 0x0000, PCL727_SIZE, 1,
+ PCL727_DI_HI, PCL727_DI_LO, PCL727_DO_HI, PCL727_DO_LO,
+ &rangelist_727[0],},
+ {"pcl728", 2, 6, 0x0000, PCL728_SIZE, 0,
+ 0, 0, 0, 0,
+ &rangelist_728[0],},
+ {"acl6126", 6, 5, 0x96e8, PCL726_SIZE, 1,
+ PCL726_DI_HI, PCL726_DI_LO, PCL726_DO_HI, PCL726_DO_LO,
+ &rangelist_726[0],},
+ {"acl6128", 2, 6, 0x0000, PCL728_SIZE, 0,
+ 0, 0, 0, 0,
+ &rangelist_728[0],},
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl726_board))
+#define this_board ((const struct pcl726_board *)dev->board_ptr)
+
+static struct comedi_driver driver_pcl726 = {
+ driver_name:"pcl726",
+ module:THIS_MODULE,
+ attach:pcl726_attach,
+ detach:pcl726_detach,
+ board_name:&boardtypes[0].name,
+ num_names:n_boardtypes,
+ offset:sizeof(struct pcl726_board),
+};
+
+COMEDI_INITCLEANUP(driver_pcl726);
+
+struct pcl726_private {
+
+ int bipolar[12];
+ const struct comedi_lrange *rangelist[12];
+ unsigned int ao_readback[12];
+};
+
+#define devpriv ((struct pcl726_private *)dev->private)
+
+static int pcl726_ao_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int hi, lo;
+ int n;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (n = 0; n < insn->n; n++) {
+ lo = data[n] & 0xff;
+ hi = (data[n] >> 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);
+ devpriv->ao_readback[chan] = data[n];
+ }
+
+ return n;
+}
+
+static int pcl726_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+ int n;
+
+ for (n = 0; n < insn->n; n++) {
+ data[n] = devpriv->ao_readback[chan];
+ }
+ return n;
+}
+
+static int pcl726_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ data[1] = inb(dev->iobase + this_board->di_lo) |
+ (inb(dev->iobase + this_board->di_hi) << 8);
+
+ return 2;
+}
+
+static int pcl726_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+ }
+ if (data[1] & 0x00ff)
+ outb(s->state & 0xff, dev->iobase + this_board->do_lo);
+ if (data[1] & 0xff00)
+ outb((s->state >> 8), dev->iobase + this_board->do_hi);
+
+ data[1] = s->state;
+
+ return 2;
+}
+
+static int pcl726_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+ unsigned int iorange;
+ int ret, i;
+#ifdef ACL6126_IRQ
+ unsigned int irq;
+#endif
+
+ iobase = it->options[0];
+ iorange = this_board->io_range;
+ printk("comedi%d: pcl726: board=%s, 0x%03lx ", dev->minor,
+ this_board->name, iobase);
+ if (!request_region(iobase, iorange, "pcl726")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+
+ dev->iobase = iobase;
+
+ dev->board_name = this_board->name;
+
+ if ((ret = alloc_private(dev, sizeof(struct pcl726_private))) < 0)
+ return -ENOMEM;
+
+ for (i = 0; i < 12; i++) {
+ devpriv->bipolar[i] = 0;
+ devpriv->rangelist[i] = &range_unknown;
+ }
+
+#ifdef ACL6126_IRQ
+ irq = 0;
+ if (boardtypes[board].IRQbits != 0) { /* board support IRQ */
+ irq = it->options[1];
+ devpriv->first_chan = 2;
+ if (irq) { /* we want to use IRQ */
+ if (((1 << irq) & boardtypes[board].IRQbits) == 0) {
+ rt_printk
+ (", IRQ %d is out of allowed range, DISABLING IT",
+ irq);
+ irq = 0; /* Bad IRQ */
+ } else {
+ if (comedi_request_irq(irq, interrupt_pcl818, 0,
+ "pcl726", dev)) {
+ rt_printk
+ (", unable to allocate IRQ %d, DISABLING IT",
+ irq);
+ irq = 0; /* Can't use IRQ */
+ } else {
+ rt_printk(", irq=%d", irq);
+ }
+ }
+ }
+ }
+
+ dev->irq = irq;
+#endif
+
+ printk("\n");
+
+ if ((ret = alloc_subdevices(dev, 3)) < 0)
+ return ret;
+
+ s = dev->subdevices + 0;
+ /* ao */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = this_board->n_aochan;
+ s->maxdata = 0xfff;
+ s->len_chanlist = 1;
+ s->insn_write = pcl726_ao_insn;
+ s->insn_read = pcl726_ao_insn_read;
+ s->range_table_list = devpriv->rangelist;
+ for (i = 0; i < this_board->n_aochan; i++) {
+ int j;
+
+ j = it->options[2 + 1];
+ if ((j < 0) || (j >= this_board->num_of_ranges)) {
+ printk("Invalid range for channel %d! Must be 0<=%d<%d\n", i, j, this_board->num_of_ranges - 1);
+ j = 0;
+ }
+ devpriv->rangelist[i] = this_board->range_type_list[j];
+ if (devpriv->rangelist[i]->range[0].min ==
+ -devpriv->rangelist[i]->range[0].max)
+ devpriv->bipolar[i] = 1; /* bipolar range */
+ }
+
+ s = dev->subdevices + 1;
+ /* di */
+ if (!this_board->have_dio) {
+ s->type = COMEDI_SUBD_UNUSED;
+ } else {
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->len_chanlist = 1;
+ s->insn_bits = pcl726_di_insn_bits;
+ s->range_table = &range_digital;
+ }
+
+ s = dev->subdevices + 2;
+ /* do */
+ if (!this_board->have_dio) {
+ s->type = COMEDI_SUBD_UNUSED;
+ } else {
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->len_chanlist = 1;
+ s->insn_bits = pcl726_do_insn_bits;
+ s->range_table = &range_digital;
+ }
+
+ return 0;
+}
+
+static int pcl726_detach(struct comedi_device * dev)
+{
+// printk("comedi%d: pcl726: remove\n",dev->minor);
+
+#ifdef ACL6126_IRQ
+ if (dev->irq) {
+ comedi_free_irq(dev->irq, dev);
+ }
+#endif
+
+ if (dev->iobase)
+ release_region(dev->iobase, this_board->io_range);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/pcl730.c b/drivers/staging/comedi/drivers/pcl730.c
new file mode 100644
index 000000000000..f2c6d7c5693c
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcl730.c
@@ -0,0 +1,168 @@
+/*
+ * comedi/drivers/pcl730.c
+ * Driver for Advantech PCL-730 and clones
+ * José Luis Sánchez
+ */
+/*
+Driver: pcl730
+Description: Advantech PCL-730 (& compatibles)
+Author: José Luis Sánchez (jsanchezv@teleline.es)
+Status: untested
+Devices: [Advantech] PCL-730 (pcl730), [ICP] ISO-730 (iso730),
+ [Adlink] ACL-7130 (acl7130)
+
+Interrupts are not supported.
+The ACL-7130 card have an 8254 timer/counter not supported by this driver.
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+#define PCL730_SIZE 4
+#define ACL7130_SIZE 8
+#define PCL730_IDIO_LO 0 /* Isolated Digital I/O low byte (ID0-ID7) */
+#define PCL730_IDIO_HI 1 /* Isolated Digital I/O high byte (ID8-ID15) */
+#define PCL730_DIO_LO 2 /* TTL Digital I/O low byte (D0-D7) */
+#define PCL730_DIO_HI 3 /* TTL Digital I/O high byte (D8-D15) */
+
+static int pcl730_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcl730_detach(struct comedi_device * dev);
+
+struct pcl730_board {
+
+ const char *name; // board name
+ unsigned int io_range; // len of I/O space
+};
+
+
+static const struct pcl730_board boardtypes[] = {
+ {"pcl730", PCL730_SIZE,},
+ {"iso730", PCL730_SIZE,},
+ {"acl7130", ACL7130_SIZE,},
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl730_board))
+#define this_board ((const struct pcl730_board *)dev->board_ptr)
+
+static struct comedi_driver driver_pcl730 = {
+ driver_name:"pcl730",
+ module:THIS_MODULE,
+ attach:pcl730_attach,
+ detach:pcl730_detach,
+ board_name:&boardtypes[0].name,
+ num_names:n_boardtypes,
+ offset:sizeof(struct pcl730_board),
+};
+
+COMEDI_INITCLEANUP(driver_pcl730);
+
+static int pcl730_do_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ }
+ if (data[0] & 0x00ff)
+ outb(s->state & 0xff,
+ dev->iobase + ((unsigned long)s->private));
+ if (data[0] & 0xff00)
+ outb((s->state >> 8),
+ dev->iobase + ((unsigned long)s->private) + 1);
+
+ data[1] = s->state;
+
+ return 2;
+}
+
+static int pcl730_di_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ data[1] = inb(dev->iobase + ((unsigned long)s->private)) |
+ (inb(dev->iobase + ((unsigned long)s->private) + 1) << 8);
+
+ return 2;
+}
+
+static int pcl730_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+ unsigned int iorange;
+
+ iobase = it->options[0];
+ iorange = this_board->io_range;
+ printk("comedi%d: pcl730: board=%s 0x%04lx ", dev->minor,
+ this_board->name, iobase);
+ if (!request_region(iobase, iorange, "pcl730")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->board_name = this_board->name;
+ dev->iobase = iobase;
+ dev->irq = 0;
+
+ if (alloc_subdevices(dev, 4) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ /* Isolated do */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->maxdata = 1;
+ s->n_chan = 16;
+ s->insn_bits = pcl730_do_insn;
+ s->range_table = &range_digital;
+ s->private = (void *)PCL730_IDIO_LO;
+
+ s = dev->subdevices + 1;
+ /* Isolated di */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->maxdata = 1;
+ s->n_chan = 16;
+ s->insn_bits = pcl730_di_insn;
+ s->range_table = &range_digital;
+ s->private = (void *)PCL730_IDIO_LO;
+
+ s = dev->subdevices + 2;
+ /* TTL do */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->maxdata = 1;
+ s->n_chan = 16;
+ s->insn_bits = pcl730_do_insn;
+ s->range_table = &range_digital;
+ s->private = (void *)PCL730_DIO_LO;
+
+ s = dev->subdevices + 3;
+ /* TTL di */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->maxdata = 1;
+ s->n_chan = 16;
+ s->insn_bits = pcl730_di_insn;
+ s->range_table = &range_digital;
+ s->private = (void *)PCL730_DIO_LO;
+
+ printk("\n");
+
+ return 0;
+}
+
+static int pcl730_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: pcl730: remove\n", dev->minor);
+
+ if (dev->iobase)
+ release_region(dev->iobase, this_board->io_range);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/pcl812.c b/drivers/staging/comedi/drivers/pcl812.c
new file mode 100644
index 000000000000..11dfd230e565
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcl812.c
@@ -0,0 +1,1605 @@
+/*
+ * comedi/drivers/pcl812.c
+ *
+ * Author: Michal Dobes <dobes@tesnet.cz>
+ *
+ * hardware driver for Advantech cards
+ * card: PCL-812, PCL-812PG, PCL-813, PCL-813B
+ * driver: pcl812, pcl812pg, pcl813, pcl813b
+ * and for ADlink cards
+ * card: ACL-8112DG, ACL-8112HG, ACL-8112PG, ACL-8113, ACL-8216
+ * driver: acl8112dg, acl8112hg, acl8112pg, acl8113, acl8216
+ * and for ICP DAS cards
+ * card: ISO-813, A-821PGH, A-821PGL, A-821PGL-NDA, A-822PGH, A-822PGL,
+ * driver: iso813, a821pgh, a-821pgl, a-821pglnda, a822pgh, a822pgl,
+ * card: A-823PGH, A-823PGL, A-826PG
+ * driver: a823pgh, a823pgl, a826pg
+ */
+/*
+Driver: pcl812
+Description: Advantech PCL-812/PG, PCL-813/B,
+ ADLink ACL-8112DG/HG/PG, ACL-8113, ACL-8216,
+ ICP DAS A-821PGH/PGL/PGL-NDA, A-822PGH/PGL, A-823PGH/PGL, A-826PG,
+ ICP DAS ISO-813
+Author: Michal Dobes <dobes@tesnet.cz>
+Devices: [Advantech] PCL-812 (pcl812), PCL-812PG (pcl812pg),
+ PCL-813 (pcl813), PCL-813B (pcl813b), [ADLink] ACL-8112DG (acl8112dg),
+ ACL-8112HG (acl8112hg), ACL-8113 (acl-8113), ACL-8216 (acl8216),
+ [ICP] ISO-813 (iso813), A-821PGH (a821pgh), A-821PGL (a821pgl),
+ A-821PGL-NDA (a821pclnda), A-822PGH (a822pgh), A-822PGL (a822pgl),
+ A-823PGH (a823pgh), A-823PGL (a823pgl), A-826PG (a826pg)
+Updated: Mon, 06 Aug 2007 12:03:15 +0100
+Status: works (I hope. My board fire up under my hands
+ and I cann't test all features.)
+
+This driver supports insn and cmd interfaces. Some boards support only insn
+becouse their hardware don't allow more (PCL-813/B, ACL-8113, ISO-813).
+Data transfer over DMA is supported only when you measure only one
+channel, this is too hardware limitation of these boards.
+
+Options for PCL-812:
+ [0] - IO Base
+ [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15)
+ [2] - DMA (0=disable, 1, 3)
+ [3] - 0=trigger source is internal 8253 with 2MHz clock
+ 1=trigger source is external
+ [4] - 0=A/D input range is +/-10V
+ 1=A/D input range is +/-5V
+ 2=A/D input range is +/-2.5V
+ 3=A/D input range is +/-1.25V
+ 4=A/D input range is +/-0.625V
+ 5=A/D input range is +/-0.3125V
+ [5] - 0=D/A outputs 0-5V (internal reference -5V)
+ 1=D/A outputs 0-10V (internal reference -10V)
+ 2=D/A outputs unknow (external reference)
+
+Options for PCL-812PG, ACL-8112PG:
+ [0] - IO Base
+ [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15)
+ [2] - DMA (0=disable, 1, 3)
+ [3] - 0=trigger source is internal 8253 with 2MHz clock
+ 1=trigger source is external
+ [4] - 0=A/D have max +/-5V input
+ 1=A/D have max +/-10V input
+ [5] - 0=D/A outputs 0-5V (internal reference -5V)
+ 1=D/A outputs 0-10V (internal reference -10V)
+ 2=D/A outputs unknow (external reference)
+
+Options for ACL-8112DG/HG, A-822PGL/PGH, A-823PGL/PGH, ACL-8216, A-826PG:
+ [0] - IO Base
+ [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15)
+ [2] - DMA (0=disable, 1, 3)
+ [3] - 0=trigger source is internal 8253 with 2MHz clock
+ 1=trigger source is external
+ [4] - 0=A/D channels are S.E.
+ 1=A/D channels are DIFF
+ [5] - 0=D/A outputs 0-5V (internal reference -5V)
+ 1=D/A outputs 0-10V (internal reference -10V)
+ 2=D/A outputs unknow (external reference)
+
+Options for A-821PGL/PGH:
+ [0] - IO Base
+ [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
+ [2] - 0=A/D channels are S.E.
+ 1=A/D channels are DIFF
+ [3] - 0=D/A output 0-5V (internal reference -5V)
+ 1=D/A output 0-10V (internal reference -10V)
+
+Options for A-821PGL-NDA:
+ [0] - IO Base
+ [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
+ [2] - 0=A/D channels are S.E.
+ 1=A/D channels are DIFF
+
+Options for PCL-813:
+ [0] - IO Base
+
+Options for PCL-813B:
+ [0] - IO Base
+ [1] - 0= bipolar inputs
+ 1= unipolar inputs
+
+Options for ACL-8113, ISO-813:
+ [0] - IO Base
+ [1] - 0= 10V bipolar inputs
+ 1= 10V unipolar inputs
+ 2= 20V bipolar inputs
+ 3= 20V unipolar inputs
+*/
+
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <asm/dma.h>
+
+#include "8253.h"
+
+#undef PCL812_EXTDEBUG /* if this is defined then a lot of messages is printed */
+
+// hardware types of the cards
+#define boardPCL812PG 0 /* and ACL-8112PG */
+#define boardPCL813B 1
+#define boardPCL812 2
+#define boardPCL813 3
+#define boardISO813 5
+#define boardACL8113 6
+#define boardACL8112 7 /* ACL-8112DG/HG, A-822PGL/PGH, A-823PGL/PGH */
+#define boardACL8216 8 /* and ICP DAS A-826PG */
+#define boardA821 9 /* PGH, PGL, PGL/NDA versions */
+
+#define PCLx1x_IORANGE 16
+
+#define PCL812_CTR0 0
+#define PCL812_CTR1 1
+#define PCL812_CTR2 2
+#define PCL812_CTRCTL 3
+#define PCL812_AD_LO 4
+#define PCL812_DA1_LO 4
+#define PCL812_AD_HI 5
+#define PCL812_DA1_HI 5
+#define PCL812_DA2_LO 6
+#define PCL812_DI_LO 6
+#define PCL812_DA2_HI 7
+#define PCL812_DI_HI 7
+#define PCL812_CLRINT 8
+#define PCL812_GAIN 9
+#define PCL812_MUX 10
+#define PCL812_MODE 11
+#define PCL812_CNTENABLE 10
+#define PCL812_SOFTTRIG 12
+#define PCL812_DO_LO 13
+#define PCL812_DO_HI 14
+
+#define PCL812_DRDY 0x10 /* =0 data ready */
+
+#define ACL8216_STATUS 8 /* 5. bit signalize data ready */
+
+#define ACL8216_DRDY 0x20 /* =0 data ready */
+
+#define MAX_CHANLIST_LEN 256 /* length of scan list */
+
+static const struct comedi_lrange range_pcl812pg_ai = { 5, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ BIP_RANGE(0.3125),
+ }
+};
+static const struct comedi_lrange range_pcl812pg2_ai = { 5, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ }
+};
+static const struct comedi_lrange range812_bipolar1_25 = { 1, {
+ BIP_RANGE(1.25),
+ }
+};
+static const struct comedi_lrange range812_bipolar0_625 = { 1, {
+ BIP_RANGE(0.625),
+ }
+};
+static const struct comedi_lrange range812_bipolar0_3125 = { 1, {
+ BIP_RANGE(0.3125),
+ }
+};
+static const struct comedi_lrange range_pcl813b_ai = { 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ }
+};
+static const struct comedi_lrange range_pcl813b2_ai = { 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ }
+};
+static const struct comedi_lrange range_iso813_1_ai = { 5, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ BIP_RANGE(0.3125),
+ }
+};
+static const struct comedi_lrange range_iso813_1_2_ai = { 5, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ UNI_RANGE(0.625),
+ }
+};
+static const struct comedi_lrange range_iso813_2_ai = { 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ }
+};
+static const struct comedi_lrange range_iso813_2_2_ai = { 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ }
+};
+static const struct comedi_lrange range_acl8113_1_ai = { 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ }
+};
+static const struct comedi_lrange range_acl8113_1_2_ai = { 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ }
+};
+static const struct comedi_lrange range_acl8113_2_ai = { 3, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ }
+};
+static const struct comedi_lrange range_acl8113_2_2_ai = { 3, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ }
+};
+static const struct comedi_lrange range_acl8112dg_ai = { 9, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ BIP_RANGE(10),
+ }
+};
+static const struct comedi_lrange range_acl8112hg_ai = { 12, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.005),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01),
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.01),
+ }
+};
+static const struct comedi_lrange range_a821pgh_ai = { 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.005),
+ }
+};
+
+static int pcl812_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcl812_detach(struct comedi_device * dev);
+
+struct pcl812_board {
+
+ const char *name; // board name
+ int board_type; // type of this board
+ int n_aichan; // num of AI chans in S.E.
+ int n_aichan_diff; // DIFF num of chans
+ int n_aochan; // num of DA chans
+ int n_dichan; // DI and DO chans
+ int n_dochan;
+ int ai_maxdata; // AI resolution
+ unsigned int ai_ns_min; // max sample speed of card v ns
+ unsigned int i8254_osc_base; // clock base
+ const struct comedi_lrange *rangelist_ai; // rangelist for A/D
+ const struct comedi_lrange *rangelist_ao; // rangelist for D/A
+ unsigned int IRQbits; // allowed IRQ
+ unsigned char DMAbits; // allowed DMA chans
+ unsigned char io_range; // iorange for this board
+ unsigned char haveMPC508; // 1=board use MPC508A multiplexor
+};
+
+
+static const struct pcl812_board boardtypes[] = {
+ {"pcl812", boardPCL812, 16, 0, 2, 16, 16, 0x0fff,
+ 33000, 500, &range_bipolar10, &range_unipolar5,
+ 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
+ {"pcl812pg", boardPCL812PG, 16, 0, 2, 16, 16, 0x0fff,
+ 33000, 500, &range_pcl812pg_ai, &range_unipolar5,
+ 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
+ {"acl8112pg", boardPCL812PG, 16, 0, 2, 16, 16, 0x0fff,
+ 10000, 500, &range_pcl812pg_ai, &range_unipolar5,
+ 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
+ {"acl8112dg", boardACL8112, 16, 8, 2, 16, 16, 0x0fff,
+ 10000, 500, &range_acl8112dg_ai, &range_unipolar5,
+ 0xdcfc, 0x0a, PCLx1x_IORANGE, 1},
+ {"acl8112hg", boardACL8112, 16, 8, 2, 16, 16, 0x0fff,
+ 10000, 500, &range_acl8112hg_ai, &range_unipolar5,
+ 0xdcfc, 0x0a, PCLx1x_IORANGE, 1},
+ {"a821pgl", boardA821, 16, 8, 1, 16, 16, 0x0fff,
+ 10000, 500, &range_pcl813b_ai, &range_unipolar5,
+ 0x000c, 0x00, PCLx1x_IORANGE, 0},
+ {"a821pglnda", boardA821, 16, 8, 0, 0, 0, 0x0fff,
+ 10000, 500, &range_pcl813b_ai, NULL,
+ 0x000c, 0x00, PCLx1x_IORANGE, 0},
+ {"a821pgh", boardA821, 16, 8, 1, 16, 16, 0x0fff,
+ 10000, 500, &range_a821pgh_ai, &range_unipolar5,
+ 0x000c, 0x00, PCLx1x_IORANGE, 0},
+ {"a822pgl", boardACL8112, 16, 8, 2, 16, 16, 0x0fff,
+ 10000, 500, &range_acl8112dg_ai, &range_unipolar5,
+ 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
+ {"a822pgh", boardACL8112, 16, 8, 2, 16, 16, 0x0fff,
+ 10000, 500, &range_acl8112hg_ai, &range_unipolar5,
+ 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
+ {"a823pgl", boardACL8112, 16, 8, 2, 16, 16, 0x0fff,
+ 8000, 500, &range_acl8112dg_ai, &range_unipolar5,
+ 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
+ {"a823pgh", boardACL8112, 16, 8, 2, 16, 16, 0x0fff,
+ 8000, 500, &range_acl8112hg_ai, &range_unipolar5,
+ 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
+ {"pcl813", boardPCL813, 32, 0, 0, 0, 0, 0x0fff,
+ 0, 0, &range_pcl813b_ai, NULL,
+ 0x0000, 0x00, PCLx1x_IORANGE, 0},
+ {"pcl813b", boardPCL813B, 32, 0, 0, 0, 0, 0x0fff,
+ 0, 0, &range_pcl813b_ai, NULL,
+ 0x0000, 0x00, PCLx1x_IORANGE, 0},
+ {"acl8113", boardACL8113, 32, 0, 0, 0, 0, 0x0fff,
+ 0, 0, &range_acl8113_1_ai, NULL,
+ 0x0000, 0x00, PCLx1x_IORANGE, 0},
+ {"iso813", boardISO813, 32, 0, 0, 0, 0, 0x0fff,
+ 0, 0, &range_iso813_1_ai, NULL,
+ 0x0000, 0x00, PCLx1x_IORANGE, 0},
+ {"acl8216", boardACL8216, 16, 8, 2, 16, 16, 0xffff,
+ 10000, 500, &range_pcl813b2_ai, &range_unipolar5,
+ 0xdcfc, 0x0a, PCLx1x_IORANGE, 1},
+ {"a826pg", boardACL8216, 16, 8, 2, 16, 16, 0xffff,
+ 10000, 500, &range_pcl813b2_ai, &range_unipolar5,
+ 0xdcfc, 0x0a, PCLx1x_IORANGE, 0},
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl812_board))
+#define this_board ((const struct pcl812_board *)dev->board_ptr)
+
+static struct comedi_driver driver_pcl812 = {
+ driver_name:"pcl812",
+ module:THIS_MODULE,
+ attach:pcl812_attach,
+ detach:pcl812_detach,
+ board_name:&boardtypes[0].name,
+ num_names:n_boardtypes,
+ offset:sizeof(struct pcl812_board),
+};
+
+COMEDI_INITCLEANUP(driver_pcl812);
+
+struct pcl812_private {
+
+ unsigned char valid; // =1 device is OK
+ unsigned char dma; // >0 use dma ( usedDMA channel)
+ unsigned char use_diff; // =1 diff inputs
+ unsigned char use_MPC; // 1=board uses MPC508A multiplexor
+ unsigned char use_ext_trg; // 1=board uses external trigger
+ unsigned char range_correction; // =1 we must add 1 to range number
+ unsigned char old_chan_reg; // lastly used chan/gain pair
+ unsigned char old_gain_reg;
+ unsigned char mode_reg_int; // there is stored INT number for some card
+ unsigned char ai_neverending; // =1 we do unlimited AI
+ unsigned char ai_eos; // 1=EOS wake up
+ unsigned char ai_dma; // =1 we use DMA
+ unsigned int ai_poll_ptr; // how many sampes transfer poll
+ unsigned int ai_scans; // len of scanlist
+ unsigned int ai_act_scan; // how many scans we finished
+ unsigned int ai_chanlist[MAX_CHANLIST_LEN]; // our copy of channel/range list
+ unsigned int ai_n_chan; // how many channels is measured
+ unsigned int ai_flags; // flaglist
+ unsigned int ai_data_len; // len of data buffer
+ short *ai_data; // data buffer
+ unsigned int ai_is16b; // =1 we have 16 bit card
+ unsigned long dmabuf[2]; // PTR to DMA buf
+ unsigned int dmapages[2]; // how many pages we have allocated
+ unsigned int hwdmaptr[2]; // HW PTR to DMA buf
+ unsigned int hwdmasize[2]; // DMA buf size in bytes
+ unsigned int dmabytestomove[2]; // how many bytes DMA transfer
+ int next_dma_buf; // which buffer is next to use
+ unsigned int dma_runs_to_end; // how many times we must switch DMA buffers
+ unsigned int last_dma_run; // how many bytes to transfer on last DMA buffer
+ unsigned int max_812_ai_mode0_rangewait; // setling time for gain
+ unsigned int ao_readback[2]; // data for AO readback
+};
+
+
+#define devpriv ((struct pcl812_private *)dev->private)
+
+/*
+==============================================================================
+*/
+static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
+ unsigned int divisor2);
+static void setup_range_channel(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int rangechan, char wait);
+static int pcl812_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+/*
+==============================================================================
+*/
+static int pcl812_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int timeout, hi;
+
+ outb(devpriv->mode_reg_int | 1, dev->iobase + PCL812_MODE); /* select software trigger */
+ setup_range_channel(dev, s, insn->chanspec, 1); // select channel and renge
+ for (n = 0; n < insn->n; n++) {
+ outb(255, dev->iobase + PCL812_SOFTTRIG); /* start conversion */
+ comedi_udelay(5);
+ timeout = 50; /* wait max 50us, it must finish under 33us */
+ while (timeout--) {
+ hi = inb(dev->iobase + PCL812_AD_HI);
+ if (!(hi & PCL812_DRDY))
+ goto conv_finish;
+ comedi_udelay(1);
+ }
+ rt_printk
+ ("comedi%d: pcl812: (%s at 0x%lx) A/D insn read timeout\n",
+ dev->minor, dev->board_name, dev->iobase);
+ outb(devpriv->mode_reg_int | 0, dev->iobase + PCL812_MODE);
+ return -ETIME;
+
+ conv_finish:
+ data[n] = ((hi & 0xf) << 8) | inb(dev->iobase + PCL812_AD_LO);
+ }
+ outb(devpriv->mode_reg_int | 0, dev->iobase + PCL812_MODE);
+ return n;
+}
+
+/*
+==============================================================================
+*/
+static int acl8216_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int timeout;
+
+ outb(1, dev->iobase + PCL812_MODE); /* select software trigger */
+ setup_range_channel(dev, s, insn->chanspec, 1); // select channel and renge
+ for (n = 0; n < insn->n; n++) {
+ outb(255, dev->iobase + PCL812_SOFTTRIG); /* start conversion */
+ comedi_udelay(5);
+ timeout = 50; /* wait max 50us, it must finish under 33us */
+ while (timeout--) {
+ if (!(inb(dev->iobase + ACL8216_STATUS) & ACL8216_DRDY))
+ goto conv_finish;
+ comedi_udelay(1);
+ }
+ rt_printk
+ ("comedi%d: pcl812: (%s at 0x%lx) A/D insn read timeout\n",
+ dev->minor, dev->board_name, dev->iobase);
+ outb(0, dev->iobase + PCL812_MODE);
+ return -ETIME;
+
+ conv_finish:
+ data[n] =
+ (inb(dev->iobase +
+ PCL812_AD_HI) << 8) | inb(dev->iobase +
+ PCL812_AD_LO);
+ }
+ outb(0, dev->iobase + PCL812_MODE);
+ return n;
+}
+
+/*
+==============================================================================
+*/
+static int pcl812_ao_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ outb((data[i] & 0xff),
+ dev->iobase + (chan ? PCL812_DA2_LO : PCL812_DA1_LO));
+ outb((data[i] >> 8) & 0x0f,
+ dev->iobase + (chan ? PCL812_DA2_HI : PCL812_DA1_HI));
+ devpriv->ao_readback[chan] = data[i];
+ }
+
+ return i;
+}
+
+/*
+==============================================================================
+*/
+static int pcl812_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ data[i] = devpriv->ao_readback[chan];
+ }
+
+ return i;
+}
+
+/*
+==============================================================================
+*/
+static int pcl812_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ data[1] = inb(dev->iobase + PCL812_DI_LO);
+ data[1] |= inb(dev->iobase + PCL812_DI_HI) << 8;
+
+ return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pcl812_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+ outb(s->state & 0xff, dev->iobase + PCL812_DO_LO);
+ outb((s->state >> 8), dev->iobase + PCL812_DO_HI);
+ }
+ data[1] = s->state;
+
+ return 2;
+}
+
+#ifdef PCL812_EXTDEBUG
+/*
+==============================================================================
+*/
+static void pcl812_cmdtest_out(int e, struct comedi_cmd * cmd)
+{
+ rt_printk("pcl812 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
+ cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
+ rt_printk("pcl812 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
+ cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
+ rt_printk("pcl812 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
+ cmd->scan_end_src);
+ rt_printk("pcl812 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e,
+ cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
+}
+#endif
+
+/*
+==============================================================================
+*/
+static int pcl812_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp, divisor1, divisor2;
+
+#ifdef PCL812_EXTDEBUG
+ rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...)\n");
+ pcl812_cmdtest_out(-1, cmd);
+#endif
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_FOLLOW;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ if (devpriv->use_ext_trg) {
+ cmd->convert_src &= TRIG_EXT;
+ } else {
+ cmd->convert_src &= TRIG_TIMER;
+ }
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err) {
+#ifdef PCL812_EXTDEBUG
+ pcl812_cmdtest_out(1, cmd);
+ rt_printk
+ ("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=1\n",
+ err);
+#endif
+ return 1;
+ }
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (cmd->start_src != TRIG_NOW) {
+ cmd->start_src = TRIG_NOW;
+ err++;
+ }
+
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ cmd->scan_begin_src = TRIG_FOLLOW;
+ err++;
+ }
+
+ if (devpriv->use_ext_trg) {
+ if (cmd->convert_src != TRIG_EXT) {
+ cmd->convert_src = TRIG_EXT;
+ err++;
+ }
+ } else {
+ if (cmd->convert_src != TRIG_TIMER) {
+ cmd->convert_src = TRIG_TIMER;
+ err++;
+ }
+ }
+
+ if (cmd->scan_end_src != TRIG_COUNT) {
+ cmd->scan_end_src = TRIG_COUNT;
+ err++;
+ }
+
+ if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
+ err++;
+
+ if (err) {
+#ifdef PCL812_EXTDEBUG
+ pcl812_cmdtest_out(2, cmd);
+ rt_printk
+ ("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=2\n",
+ err);
+#endif
+ return 2;
+ }
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < this_board->ai_ns_min) {
+ cmd->convert_arg = this_board->ai_ns_min;
+ err++;
+ }
+ } else { /* TRIG_EXT */
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ }
+
+ if (!cmd->chanlist_len) {
+ cmd->chanlist_len = 1;
+ err++;
+ }
+ if (cmd->chanlist_len > MAX_CHANLIST_LEN) {
+ cmd->chanlist_len = this_board->n_aichan;
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (!cmd->stop_arg) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ } else { /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err) {
+#ifdef PCL812_EXTDEBUG
+ pcl812_cmdtest_out(3, cmd);
+ rt_printk
+ ("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=3\n",
+ err);
+#endif
+ return 3;
+ }
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
+ &divisor2, &cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (cmd->convert_arg < this_board->ai_ns_min)
+ cmd->convert_arg = this_board->ai_ns_min;
+ if (tmp != cmd->convert_arg)
+ err++;
+ }
+
+ if (err) {
+#ifdef PCL812_EXTDEBUG
+ rt_printk
+ ("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=4\n",
+ err);
+#endif
+ return 4;
+ }
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pcl812_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned int divisor1 = 0, divisor2 = 0, i, dma_flags, bytes;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+#ifdef PCL812_EXTDEBUG
+ rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmd(...)\n");
+#endif
+
+ if (cmd->start_src != TRIG_NOW)
+ return -EINVAL;
+ if (cmd->scan_begin_src != TRIG_FOLLOW)
+ return -EINVAL;
+ if (devpriv->use_ext_trg) {
+ if (cmd->convert_src != TRIG_EXT)
+ return -EINVAL;
+ } else {
+ if (cmd->convert_src != TRIG_TIMER)
+ return -EINVAL;
+ }
+ if (cmd->scan_end_src != TRIG_COUNT)
+ return -EINVAL;
+ if (cmd->scan_end_arg != cmd->chanlist_len)
+ return -EINVAL;
+ if (cmd->chanlist_len > MAX_CHANLIST_LEN)
+ return -EINVAL;
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < this_board->ai_ns_min)
+ cmd->convert_arg = this_board->ai_ns_min;
+ i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
+ &divisor1, &divisor2, &cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ }
+
+ start_pacer(dev, -1, 0, 0); // stop pacer
+
+ devpriv->ai_n_chan = cmd->chanlist_len;
+ memcpy(devpriv->ai_chanlist, cmd->chanlist,
+ sizeof(unsigned int) * cmd->scan_end_arg);
+ setup_range_channel(dev, s, devpriv->ai_chanlist[0], 1); // select first channel and range
+
+ if (devpriv->dma) { // check if we can use DMA transfer
+ devpriv->ai_dma = 1;
+ for (i = 1; i < devpriv->ai_n_chan; i++)
+ if (devpriv->ai_chanlist[0] != devpriv->ai_chanlist[i]) {
+ devpriv->ai_dma = 0; // we cann't use DMA :-(
+ break;
+ }
+ } else
+ devpriv->ai_dma = 0;
+
+ devpriv->ai_flags = cmd->flags;
+ devpriv->ai_data_len = s->async->prealloc_bufsz;
+ devpriv->ai_data = s->async->prealloc_buf;
+ if (cmd->stop_src == TRIG_COUNT) {
+ devpriv->ai_scans = cmd->stop_arg;
+ devpriv->ai_neverending = 0;
+ } else {
+ devpriv->ai_scans = 0;
+ devpriv->ai_neverending = 1;
+ }
+
+ devpriv->ai_act_scan = 0;
+ devpriv->ai_poll_ptr = 0;
+ s->async->cur_chan = 0;
+
+ if ((devpriv->ai_flags & TRIG_WAKE_EOS)) { // don't we want wake up every scan?
+ devpriv->ai_eos = 1;
+ if (devpriv->ai_n_chan == 1)
+ devpriv->ai_dma = 0; // DMA is useless for this situation
+ }
+
+ if (devpriv->ai_dma) {
+ if (devpriv->ai_eos) { // we use EOS, so adapt DMA buffer to one scan
+ devpriv->dmabytestomove[0] =
+ devpriv->ai_n_chan * sizeof(short);
+ devpriv->dmabytestomove[1] =
+ devpriv->ai_n_chan * sizeof(short);
+ devpriv->dma_runs_to_end = 1;
+ } else {
+ devpriv->dmabytestomove[0] = devpriv->hwdmasize[0];
+ devpriv->dmabytestomove[1] = devpriv->hwdmasize[1];
+ if (devpriv->ai_data_len < devpriv->hwdmasize[0])
+ devpriv->dmabytestomove[0] =
+ devpriv->ai_data_len;
+ if (devpriv->ai_data_len < devpriv->hwdmasize[1])
+ devpriv->dmabytestomove[1] =
+ devpriv->ai_data_len;
+ if (devpriv->ai_neverending) {
+ devpriv->dma_runs_to_end = 1;
+ } else {
+ bytes = devpriv->ai_n_chan * devpriv->ai_scans * sizeof(short); // how many samples we must transfer?
+ devpriv->dma_runs_to_end = bytes / devpriv->dmabytestomove[0]; // how many DMA pages we must fill
+ devpriv->last_dma_run = bytes % devpriv->dmabytestomove[0]; //on last dma transfer must be moved
+ if (devpriv->dma_runs_to_end == 0)
+ devpriv->dmabytestomove[0] =
+ devpriv->last_dma_run;
+ devpriv->dma_runs_to_end--;
+ }
+ }
+ if (devpriv->dmabytestomove[0] > devpriv->hwdmasize[0]) {
+ devpriv->dmabytestomove[0] = devpriv->hwdmasize[0];
+ devpriv->ai_eos = 0;
+ }
+ if (devpriv->dmabytestomove[1] > devpriv->hwdmasize[1]) {
+ devpriv->dmabytestomove[1] = devpriv->hwdmasize[1];
+ devpriv->ai_eos = 0;
+ }
+ devpriv->next_dma_buf = 0;
+ set_dma_mode(devpriv->dma, DMA_MODE_READ);
+ dma_flags = claim_dma_lock();
+ clear_dma_ff(devpriv->dma);
+ set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
+ set_dma_count(devpriv->dma, devpriv->dmabytestomove[0]);
+ release_dma_lock(dma_flags);
+ enable_dma(devpriv->dma);
+#ifdef PCL812_EXTDEBUG
+ rt_printk
+ ("pcl812 EDBG: DMA %d PTR 0x%0x/0x%0x LEN %u/%u EOS %d\n",
+ devpriv->dma, devpriv->hwdmaptr[0],
+ devpriv->hwdmaptr[1], devpriv->dmabytestomove[0],
+ devpriv->dmabytestomove[1], devpriv->ai_eos);
+#endif
+ }
+
+ switch (cmd->convert_src) {
+ case TRIG_TIMER:
+ start_pacer(dev, 1, divisor1, divisor2);
+ break;
+ }
+
+ if (devpriv->ai_dma) {
+ outb(devpriv->mode_reg_int | 2, dev->iobase + PCL812_MODE); // let's go!
+ } else {
+ outb(devpriv->mode_reg_int | 6, dev->iobase + PCL812_MODE); // let's go!
+ }
+
+#ifdef PCL812_EXTDEBUG
+ rt_printk("pcl812 EDBG: END: pcl812_ai_cmd(...)\n");
+#endif
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static irqreturn_t interrupt_pcl812_ai_int(int irq, void *d)
+{
+ char err = 1;
+ unsigned int mask, timeout;
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+
+ s->async->events = 0;
+
+ timeout = 50; /* wait max 50us, it must finish under 33us */
+ if (devpriv->ai_is16b) {
+ mask = 0xffff;
+ while (timeout--) {
+ if (!(inb(dev->iobase + ACL8216_STATUS) & ACL8216_DRDY)) {
+ err = 0;
+ break;
+ }
+ comedi_udelay(1);
+ }
+ } else {
+ mask = 0x0fff;
+ while (timeout--) {
+ if (!(inb(dev->iobase + PCL812_AD_HI) & PCL812_DRDY)) {
+ err = 0;
+ break;
+ }
+ comedi_udelay(1);
+ }
+ }
+
+ if (err) {
+ rt_printk
+ ("comedi%d: pcl812: (%s at 0x%lx) A/D cmd IRQ without DRDY!\n",
+ dev->minor, dev->board_name, dev->iobase);
+ pcl812_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+ }
+
+ comedi_buf_put(s->async,
+ ((inb(dev->iobase + PCL812_AD_HI) << 8) | inb(dev->iobase +
+ PCL812_AD_LO)) & mask);
+
+ outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */
+
+ if (s->async->cur_chan == 0) { /* one scan done */
+ devpriv->ai_act_scan++;
+ if (!(devpriv->ai_neverending))
+ if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
+ pcl812_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ }
+ }
+
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+}
+
+/*
+==============================================================================
+*/
+static void transfer_from_dma_buf(struct comedi_device * dev, struct comedi_subdevice * s,
+ short * ptr, unsigned int bufptr, unsigned int len)
+{
+ unsigned int i;
+
+ s->async->events = 0;
+ for (i = len; i; i--) {
+ comedi_buf_put(s->async, ptr[bufptr++]); // get one sample
+
+ if (s->async->cur_chan == 0) {
+ devpriv->ai_act_scan++;
+ if (!devpriv->ai_neverending)
+ if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
+ pcl812_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ break;
+ }
+ }
+ }
+
+ comedi_event(dev, s);
+}
+
+/*
+==============================================================================
+*/
+static irqreturn_t interrupt_pcl812_ai_dma(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+ unsigned long dma_flags;
+ int len, bufptr;
+ short *ptr;
+
+#ifdef PCL812_EXTDEBUG
+ rt_printk("pcl812 EDBG: BGN: interrupt_pcl812_ai_dma(...)\n");
+#endif
+ ptr = (short *) devpriv->dmabuf[devpriv->next_dma_buf];
+ len = (devpriv->dmabytestomove[devpriv->next_dma_buf] >> 1) -
+ devpriv->ai_poll_ptr;
+
+ devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
+ disable_dma(devpriv->dma);
+ set_dma_mode(devpriv->dma, DMA_MODE_READ);
+ dma_flags = claim_dma_lock();
+ set_dma_addr(devpriv->dma, devpriv->hwdmaptr[devpriv->next_dma_buf]);
+ if (devpriv->ai_eos) {
+ set_dma_count(devpriv->dma,
+ devpriv->dmabytestomove[devpriv->next_dma_buf]);
+ } else {
+ if (devpriv->dma_runs_to_end) {
+ set_dma_count(devpriv->dma,
+ devpriv->dmabytestomove[devpriv->next_dma_buf]);
+ } else {
+ set_dma_count(devpriv->dma, devpriv->last_dma_run);
+ }
+ devpriv->dma_runs_to_end--;
+ }
+ release_dma_lock(dma_flags);
+ enable_dma(devpriv->dma);
+
+ outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */
+
+ bufptr = devpriv->ai_poll_ptr;
+ devpriv->ai_poll_ptr = 0;
+
+ transfer_from_dma_buf(dev, s, ptr, bufptr, len);
+
+#ifdef PCL812_EXTDEBUG
+ rt_printk("pcl812 EDBG: END: interrupt_pcl812_ai_dma(...)\n");
+#endif
+ return IRQ_HANDLED;
+}
+
+/*
+==============================================================================
+*/
+static irqreturn_t interrupt_pcl812(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+
+ if (!dev->attached) {
+ comedi_error(dev, "spurious interrupt");
+ return IRQ_HANDLED;
+ }
+ if (devpriv->ai_dma) {
+ return interrupt_pcl812_ai_dma(irq, d);
+ } else {
+ return interrupt_pcl812_ai_int(irq, d);
+ };
+}
+
+/*
+==============================================================================
+*/
+static int pcl812_ai_poll(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long flags;
+ unsigned int top1, top2, i;
+
+ if (!devpriv->ai_dma)
+ return 0; // poll is valid only for DMA transfer
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+
+ for (i = 0; i < 10; i++) {
+ top1 = get_dma_residue(devpriv->ai_dma); // where is now DMA
+ top2 = get_dma_residue(devpriv->ai_dma);
+ if (top1 == top2)
+ break;
+ }
+
+ if (top1 != top2) {
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ return 0;
+ }
+
+ top1 = devpriv->dmabytestomove[1 - devpriv->next_dma_buf] - top1; // where is now DMA in buffer
+ top1 >>= 1; // sample position
+ top2 = top1 - devpriv->ai_poll_ptr;
+ if (top2 < 1) { // no new samples
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ return 0;
+ }
+
+ transfer_from_dma_buf(dev, s,
+ (void *)devpriv->dmabuf[1 - devpriv->next_dma_buf],
+ devpriv->ai_poll_ptr, top2);
+
+ devpriv->ai_poll_ptr = top1; // new buffer position
+
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return s->async->buf_write_count - s->async->buf_read_count;
+}
+
+/*
+==============================================================================
+*/
+static void setup_range_channel(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int rangechan, char wait)
+{
+ unsigned char chan_reg = CR_CHAN(rangechan); // normal board
+ unsigned char gain_reg = CR_RANGE(rangechan) + devpriv->range_correction; // gain index
+
+ if ((chan_reg == devpriv->old_chan_reg)
+ && (gain_reg == devpriv->old_gain_reg))
+ return; // we can return, no change
+
+ devpriv->old_chan_reg = chan_reg;
+ devpriv->old_gain_reg = gain_reg;
+
+ if (devpriv->use_MPC) {
+ if (devpriv->use_diff) {
+ chan_reg = chan_reg | 0x30; // DIFF inputs
+ } else {
+ if (chan_reg & 0x80) {
+ chan_reg = chan_reg | 0x20; // SE inputs 8-15
+ } else {
+ chan_reg = chan_reg | 0x10; // SE inputs 0-7
+ }
+ }
+ }
+
+ outb(chan_reg, dev->iobase + PCL812_MUX); /* select channel */
+ outb(gain_reg, dev->iobase + PCL812_GAIN); /* select gain */
+
+ if (wait) {
+ comedi_udelay(devpriv->max_812_ai_mode0_rangewait); // XXX this depends on selected range and can be very long for some high gain ranges!
+ }
+}
+
+/*
+==============================================================================
+*/
+static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
+ unsigned int divisor2)
+{
+#ifdef PCL812_EXTDEBUG
+ rt_printk("pcl812 EDBG: BGN: start_pacer(%d,%u,%u)\n", mode, divisor1,
+ divisor2);
+#endif
+ outb(0xb4, dev->iobase + PCL812_CTRCTL);
+ outb(0x74, dev->iobase + PCL812_CTRCTL);
+ comedi_udelay(1);
+
+ if (mode == 1) {
+ outb(divisor2 & 0xff, dev->iobase + PCL812_CTR2);
+ outb((divisor2 >> 8) & 0xff, dev->iobase + PCL812_CTR2);
+ outb(divisor1 & 0xff, dev->iobase + PCL812_CTR1);
+ outb((divisor1 >> 8) & 0xff, dev->iobase + PCL812_CTR1);
+ }
+#ifdef PCL812_EXTDEBUG
+ rt_printk("pcl812 EDBG: END: start_pacer(...)\n");
+#endif
+}
+
+/*
+==============================================================================
+*/
+static void free_resources(struct comedi_device * dev)
+{
+
+ if (dev->private) {
+ if (devpriv->dmabuf[0])
+ free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
+ if (devpriv->dmabuf[1])
+ free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
+ if (devpriv->dma)
+ free_dma(devpriv->dma);
+ }
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+ if (dev->iobase)
+ release_region(dev->iobase, this_board->io_range);
+}
+
+/*
+==============================================================================
+*/
+static int pcl812_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+#ifdef PCL812_EXTDEBUG
+ rt_printk("pcl812 EDBG: BGN: pcl812_ai_cancel(...)\n");
+#endif
+ if (devpriv->ai_dma)
+ disable_dma(devpriv->dma);
+ outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */
+ outb(devpriv->mode_reg_int | 0, dev->iobase + PCL812_MODE); /* Stop A/D */
+ start_pacer(dev, -1, 0, 0); // stop 8254
+ outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */
+#ifdef PCL812_EXTDEBUG
+ rt_printk("pcl812 EDBG: END: pcl812_ai_cancel(...)\n");
+#endif
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static void pcl812_reset(struct comedi_device * dev)
+{
+#ifdef PCL812_EXTDEBUG
+ rt_printk("pcl812 EDBG: BGN: pcl812_reset(...)\n");
+#endif
+ outb(0, dev->iobase + PCL812_MUX);
+ outb(0 + devpriv->range_correction, dev->iobase + PCL812_GAIN);
+ devpriv->old_chan_reg = -1; // invalidate chain/gain memory
+ devpriv->old_gain_reg = -1;
+
+ switch (this_board->board_type) {
+ case boardPCL812PG:
+ case boardPCL812:
+ case boardACL8112:
+ case boardACL8216:
+ outb(0, dev->iobase + PCL812_DA2_LO);
+ outb(0, dev->iobase + PCL812_DA2_HI);
+ case boardA821:
+ outb(0, dev->iobase + PCL812_DA1_LO);
+ outb(0, dev->iobase + PCL812_DA1_HI);
+ start_pacer(dev, -1, 0, 0); // stop 8254
+ outb(0, dev->iobase + PCL812_DO_HI);
+ outb(0, dev->iobase + PCL812_DO_LO);
+ outb(devpriv->mode_reg_int | 0, dev->iobase + PCL812_MODE);
+ outb(0, dev->iobase + PCL812_CLRINT);
+ break;
+ case boardPCL813B:
+ case boardPCL813:
+ case boardISO813:
+ case boardACL8113:
+ comedi_udelay(5);
+ break;
+ }
+ comedi_udelay(5);
+#ifdef PCL812_EXTDEBUG
+ rt_printk("pcl812 EDBG: END: pcl812_reset(...)\n");
+#endif
+}
+
+/*
+==============================================================================
+*/
+static int pcl812_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int ret, subdev;
+ unsigned long iobase;
+ unsigned int irq;
+ unsigned int dma;
+ unsigned long pages;
+ struct comedi_subdevice *s;
+ int n_subdevices;
+
+ iobase = it->options[0];
+ printk("comedi%d: pcl812: board=%s, ioport=0x%03lx", dev->minor,
+ this_board->name, iobase);
+
+ if (!request_region(iobase, this_board->io_range, "pcl812")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ if ((ret = alloc_private(dev, sizeof(struct pcl812_private))) < 0) {
+ free_resources(dev);
+ return ret; /* Can't alloc mem */
+ }
+
+ dev->board_name = this_board->name;
+
+ irq = 0;
+ if (this_board->IRQbits != 0) { /* board support IRQ */
+ irq = it->options[1];
+ if (irq) { /* we want to use IRQ */
+ if (((1 << irq) & this_board->IRQbits) == 0) {
+ printk(", IRQ %u is out of allowed range, DISABLING IT", irq);
+ irq = 0; /* Bad IRQ */
+ } else {
+ if (comedi_request_irq(irq, interrupt_pcl812, 0,
+ "pcl812", dev)) {
+ printk(", unable to allocate IRQ %u, DISABLING IT", irq);
+ irq = 0; /* Can't use IRQ */
+ } else {
+ printk(", irq=%u", irq);
+ }
+ }
+ }
+ }
+
+ dev->irq = irq;
+
+ dma = 0;
+ devpriv->dma = dma;
+ if (!dev->irq)
+ goto no_dma; /* if we haven't IRQ, we can't use DMA */
+ if (this_board->DMAbits != 0) { /* board support DMA */
+ dma = it->options[2];
+ if (((1 << dma) & this_board->DMAbits) == 0) {
+ printk(", DMA is out of allowed range, FAIL!\n");
+ return -EINVAL; /* Bad DMA */
+ }
+ ret = request_dma(dma, "pcl812");
+ if (ret) {
+ printk(", unable to allocate DMA %u, FAIL!\n", dma);
+ return -EBUSY; /* DMA isn't free */
+ }
+ devpriv->dma = dma;
+ printk(", dma=%u", dma);
+ pages = 1; /* we want 8KB */
+ devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
+ if (!devpriv->dmabuf[0]) {
+ printk(", unable to allocate DMA buffer, FAIL!\n");
+ /* maybe experiment with try_to_free_pages() will help .... */
+ free_resources(dev);
+ return -EBUSY; /* no buffer :-( */
+ }
+ devpriv->dmapages[0] = pages;
+ devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
+ devpriv->hwdmasize[0] = PAGE_SIZE * (1 << pages);
+ devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
+ if (!devpriv->dmabuf[1]) {
+ printk(", unable to allocate DMA buffer, FAIL!\n");
+ free_resources(dev);
+ return -EBUSY;
+ }
+ devpriv->dmapages[1] = pages;
+ devpriv->hwdmaptr[1] = virt_to_bus((void *)devpriv->dmabuf[1]);
+ devpriv->hwdmasize[1] = PAGE_SIZE * (1 << pages);
+ }
+ no_dma:
+
+ n_subdevices = 0;
+ if (this_board->n_aichan > 0)
+ n_subdevices++;
+ if (this_board->n_aochan > 0)
+ n_subdevices++;
+ if (this_board->n_dichan > 0)
+ n_subdevices++;
+ if (this_board->n_dochan > 0)
+ n_subdevices++;
+
+ if ((ret = alloc_subdevices(dev, n_subdevices)) < 0) {
+ free_resources(dev);
+ return ret;
+ }
+
+ subdev = 0;
+
+ /* analog input */
+ if (this_board->n_aichan > 0) {
+ s = dev->subdevices + subdev;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE;
+ switch (this_board->board_type) {
+ case boardA821:
+ if (it->options[2] == 1) {
+ s->n_chan = this_board->n_aichan_diff;
+ s->subdev_flags |= SDF_DIFF;
+ devpriv->use_diff = 1;
+ } else {
+ s->n_chan = this_board->n_aichan;
+ s->subdev_flags |= SDF_GROUND;
+ }
+ break;
+ case boardACL8112:
+ case boardACL8216:
+ if (it->options[4] == 1) {
+ s->n_chan = this_board->n_aichan_diff;
+ s->subdev_flags |= SDF_DIFF;
+ devpriv->use_diff = 1;
+ } else {
+ s->n_chan = this_board->n_aichan;
+ s->subdev_flags |= SDF_GROUND;
+ }
+ break;
+ default:
+ s->n_chan = this_board->n_aichan;
+ s->subdev_flags |= SDF_GROUND;
+ break;
+ }
+ s->maxdata = this_board->ai_maxdata;
+ s->len_chanlist = MAX_CHANLIST_LEN;
+ s->range_table = this_board->rangelist_ai;
+ if (this_board->board_type == boardACL8216) {
+ s->insn_read = acl8216_ai_insn_read;
+ } else {
+ s->insn_read = pcl812_ai_insn_read;
+ }
+ devpriv->use_MPC = this_board->haveMPC508;
+ s->cancel = pcl812_ai_cancel;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->do_cmdtest = pcl812_ai_cmdtest;
+ s->do_cmd = pcl812_ai_cmd;
+ s->poll = pcl812_ai_poll;
+ }
+ switch (this_board->board_type) {
+ case boardPCL812PG:
+ if (it->options[4] == 1)
+ s->range_table = &range_pcl812pg2_ai;
+ break;
+ case boardPCL812:
+ switch (it->options[4]) {
+ case 0:
+ s->range_table = &range_bipolar10;
+ break;
+ case 1:
+ s->range_table = &range_bipolar5;
+ break;
+ case 2:
+ s->range_table = &range_bipolar2_5;
+ break;
+ case 3:
+ s->range_table = &range812_bipolar1_25;
+ break;
+ case 4:
+ s->range_table = &range812_bipolar0_625;
+ break;
+ case 5:
+ s->range_table = &range812_bipolar0_3125;
+ break;
+ default:
+ s->range_table = &range_bipolar10;
+ break;
+ printk(", incorrect range number %d, changing to 0 (+/-10V)", it->options[4]);
+ break;
+ }
+ break;
+ break;
+ case boardPCL813B:
+ if (it->options[1] == 1)
+ s->range_table = &range_pcl813b2_ai;
+ break;
+ case boardISO813:
+ switch (it->options[1]) {
+ case 0:
+ s->range_table = &range_iso813_1_ai;
+ break;
+ case 1:
+ s->range_table = &range_iso813_1_2_ai;
+ break;
+ case 2:
+ s->range_table = &range_iso813_2_ai;
+ devpriv->range_correction = 1;
+ break;
+ case 3:
+ s->range_table = &range_iso813_2_2_ai;
+ devpriv->range_correction = 1;
+ break;
+ default:
+ s->range_table = &range_iso813_1_ai;
+ break;
+ printk(", incorrect range number %d, changing to 0 ", it->options[1]);
+ break;
+ }
+ break;
+ case boardACL8113:
+ switch (it->options[1]) {
+ case 0:
+ s->range_table = &range_acl8113_1_ai;
+ break;
+ case 1:
+ s->range_table = &range_acl8113_1_2_ai;
+ break;
+ case 2:
+ s->range_table = &range_acl8113_2_ai;
+ devpriv->range_correction = 1;
+ break;
+ case 3:
+ s->range_table = &range_acl8113_2_2_ai;
+ devpriv->range_correction = 1;
+ break;
+ default:
+ s->range_table = &range_acl8113_1_ai;
+ break;
+ printk(", incorrect range number %d, changing to 0 ", it->options[1]);
+ break;
+ }
+ break;
+ }
+ subdev++;
+ }
+
+ /* analog output */
+ if (this_board->n_aochan > 0) {
+ s = dev->subdevices + subdev;
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = this_board->n_aochan;
+ s->maxdata = 0xfff;
+ s->len_chanlist = 1;
+ s->range_table = this_board->rangelist_ao;
+ s->insn_read = pcl812_ao_insn_read;
+ s->insn_write = pcl812_ao_insn_write;
+ switch (this_board->board_type) {
+ case boardA821:
+ if (it->options[3] == 1)
+ s->range_table = &range_unipolar10;
+ break;
+ case boardPCL812:
+ case boardACL8112:
+ case boardPCL812PG:
+ case boardACL8216:
+ if (it->options[5] == 1)
+ s->range_table = &range_unipolar10;
+ if (it->options[5] == 2)
+ s->range_table = &range_unknown;
+ break;
+ }
+ subdev++;
+ }
+
+ /* digital input */
+ if (this_board->n_dichan > 0) {
+ s = dev->subdevices + subdev;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = this_board->n_dichan;
+ s->maxdata = 1;
+ s->len_chanlist = this_board->n_dichan;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl812_di_insn_bits;
+ subdev++;
+ }
+
+ /* digital output */
+ if (this_board->n_dochan > 0) {
+ s = dev->subdevices + subdev;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = this_board->n_dochan;
+ s->maxdata = 1;
+ s->len_chanlist = this_board->n_dochan;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl812_do_insn_bits;
+ subdev++;
+ }
+
+ switch (this_board->board_type) {
+ case boardACL8216:
+ devpriv->ai_is16b = 1;
+ case boardPCL812PG:
+ case boardPCL812:
+ case boardACL8112:
+ devpriv->max_812_ai_mode0_rangewait = 1;
+ if (it->options[3] > 0)
+ devpriv->use_ext_trg = 1; // we use external trigger
+ case boardA821:
+ devpriv->max_812_ai_mode0_rangewait = 1;
+ devpriv->mode_reg_int = (irq << 4) & 0xf0;
+ break;
+ case boardPCL813B:
+ case boardPCL813:
+ case boardISO813:
+ case boardACL8113:
+ devpriv->max_812_ai_mode0_rangewait = 5; /* maybe there must by greatest timeout */
+ break;
+ }
+
+ printk("\n");
+ devpriv->valid = 1;
+
+ pcl812_reset(dev);
+
+ return 0;
+}
+
+/*
+==============================================================================
+ */
+static int pcl812_detach(struct comedi_device * dev)
+{
+
+#ifdef PCL812_EXTDEBUG
+ rt_printk("comedi%d: pcl812: remove\n", dev->minor);
+#endif
+ free_resources(dev);
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/pcl816.c b/drivers/staging/comedi/drivers/pcl816.c
new file mode 100644
index 000000000000..515ba74cc7fe
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcl816.c
@@ -0,0 +1,1251 @@
+/*
+ comedi/drivers/pcl816.c
+
+ Author: Juan Grigera <juan@grigera.com.ar>
+ based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812
+
+ hardware driver for Advantech cards:
+ card: PCL-816, PCL814B
+ driver: pcl816
+*/
+/*
+Driver: pcl816
+Description: Advantech PCL-816 cards, PCL-814
+Author: Juan Grigera <juan@grigera.com.ar>
+Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
+Status: works
+Updated: Tue, 2 Apr 2002 23:15:21 -0800
+
+PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
+Differences are at resolution (16 vs 12 bits).
+
+The driver support AI command mode, other subdevices not written.
+
+Analog output and digital input and output are not supported.
+
+Configuration Options:
+ [0] - IO Base
+ [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
+ [2] - DMA (0=disable, 1, 3)
+ [3] - 0, 10=10MHz clock for 8254
+ 1= 1MHz clock for 8254
+
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+#include <linux/mc146818rtc.h>
+#include <linux/delay.h>
+#include <asm/dma.h>
+
+#include "8253.h"
+
+#define DEBUG(x) x
+
+// boards constants
+// IO space len
+#define PCLx1x_RANGE 16
+
+//#define outb(x,y) printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y)
+
+// INTEL 8254 counters
+#define PCL816_CTR0 4
+#define PCL816_CTR1 5
+#define PCL816_CTR2 6
+// R: counter read-back register W: counter control
+#define PCL816_CTRCTL 7
+
+// R: A/D high byte W: A/D range control
+#define PCL816_RANGE 9
+// W: clear INT request
+#define PCL816_CLRINT 10
+// R: next mux scan channel W: mux scan channel & range control pointer
+#define PCL816_MUX 11
+// R/W: operation control register
+#define PCL816_CONTROL 12
+
+// R: return status byte W: set DMA/IRQ
+#define PCL816_STATUS 13
+#define PCL816_STATUS_DRDY_MASK 0x80
+
+// R: low byte of A/D W: soft A/D trigger
+#define PCL816_AD_LO 8
+// R: high byte of A/D W: A/D range control
+#define PCL816_AD_HI 9
+
+// type of interrupt handler
+#define INT_TYPE_AI1_INT 1
+#define INT_TYPE_AI1_DMA 2
+#define INT_TYPE_AI3_INT 4
+#define INT_TYPE_AI3_DMA 5
+#ifdef unused
+#define INT_TYPE_AI1_DMA_RTC 9
+#define INT_TYPE_AI3_DMA_RTC 10
+
+// RTC stuff...
+#define RTC_IRQ 8
+#define RTC_IO_EXTENT 0x10
+#endif
+
+#define MAGIC_DMA_WORD 0x5a5a
+
+static const struct comedi_lrange range_pcl816 = { 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ }
+};
+struct pcl816_board {
+
+ const char *name; // board name
+ int n_ranges; // len of range list
+ int n_aichan; // num of A/D chans in diferencial mode
+ unsigned int ai_ns_min; // minimal alllowed delay between samples (in ns)
+ int n_aochan; // num of D/A chans
+ int n_dichan; // num of DI chans
+ int n_dochan; // num of DO chans
+ const struct comedi_lrange *ai_range_type; // default A/D rangelist
+ const struct comedi_lrange *ao_range_type; // dafault D/A rangelist
+ unsigned int io_range; // len of IO space
+ unsigned int IRQbits; // allowed interrupts
+ unsigned int DMAbits; // allowed DMA chans
+ int ai_maxdata; // maxdata for A/D
+ int ao_maxdata; // maxdata for D/A
+ int ai_chanlist; // allowed len of channel list A/D
+ int ao_chanlist; // allowed len of channel list D/A
+ int i8254_osc_base; // 1/frequency of on board oscilator in ns
+};
+
+
+static const struct pcl816_board boardtypes[] = {
+ {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
+ &range_pcl816, PCLx1x_RANGE,
+ 0x00fc, // IRQ mask
+ 0x0a, // DMA mask
+ 0xffff, // 16-bit card
+ 0xffff, // D/A maxdata
+ 1024,
+ 1, // ao chan list
+ 100},
+ {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
+ &range_pcl816, PCLx1x_RANGE,
+ 0x00fc,
+ 0x0a,
+ 0x3fff, /* 14 bit card */
+ 0x3fff,
+ 1024,
+ 1,
+ 100},
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl816_board))
+#define devpriv ((struct pcl816_private *)dev->private)
+#define this_board ((const struct pcl816_board *)dev->board_ptr)
+
+static int pcl816_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcl816_detach(struct comedi_device * dev);
+
+#ifdef unused
+static int RTC_lock = 0; /* RTC lock */
+static int RTC_timer_lock = 0; /* RTC int lock */
+#endif
+
+static struct comedi_driver driver_pcl816 = {
+ driver_name:"pcl816",
+ module:THIS_MODULE,
+ attach:pcl816_attach,
+ detach:pcl816_detach,
+ board_name:&boardtypes[0].name,
+ num_names:n_boardtypes,
+ offset:sizeof(struct pcl816_board),
+};
+
+COMEDI_INITCLEANUP(driver_pcl816);
+
+struct pcl816_private {
+
+ unsigned int dma; // used DMA, 0=don't use DMA
+ int dma_rtc; // 1=RTC used with DMA, 0=no RTC alloc
+#ifdef unused
+ unsigned long rtc_iobase; // RTC port region
+ unsigned int rtc_iosize;
+ unsigned int rtc_irq;
+#endif
+ unsigned long dmabuf[2]; // pointers to begin of DMA buffers
+ unsigned int dmapages[2]; // len of DMA buffers in PAGE_SIZEs
+ unsigned int hwdmaptr[2]; // hardware address of DMA buffers
+ unsigned int hwdmasize[2]; // len of DMA buffers in Bytes
+ unsigned int dmasamplsize; // size in samples hwdmasize[0]/2
+ unsigned int last_top_dma; // DMA pointer in last RTC int
+ int next_dma_buf; // which DMA buffer will be used next round
+ long dma_runs_to_end; // how many we must permorm DMA transfer to end of record
+ unsigned long last_dma_run; // how many bytes we must transfer on last DMA page
+
+ unsigned int ai_scans; // len of scanlist
+ unsigned char ai_neverending; // if=1, then we do neverending record (you must use cancel())
+ int irq_free; // 1=have allocated IRQ
+ int irq_blocked; // 1=IRQ now uses any subdev
+#ifdef unused
+ int rtc_irq_blocked; // 1=we now do AI with DMA&RTC
+#endif
+ int irq_was_now_closed; // when IRQ finish, there's stored int816_mode for last interrupt
+ int int816_mode; // who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma
+ struct comedi_subdevice *last_int_sub; // ptr to subdevice which now finish
+ int ai_act_scan; // how many scans we finished
+ unsigned int ai_act_chanlist[16]; // MUX setting for actual AI operations
+ unsigned int ai_act_chanlist_len; // how long is actual MUX list
+ unsigned int ai_act_chanlist_pos; // actual position in MUX list
+ unsigned int ai_poll_ptr; // how many sampes transfer poll
+ struct comedi_subdevice *sub_ai; // ptr to AI subdevice
+#ifdef unused
+ struct timer_list rtc_irq_timer; // timer for RTC sanity check
+ unsigned long rtc_freq; // RTC int freq
+#endif
+};
+
+
+/*
+==============================================================================
+*/
+static int check_and_setup_channel_list(struct comedi_device * dev,
+ struct comedi_subdevice * s, unsigned int *chanlist, int chanlen);
+static int pcl816_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
+ unsigned int divisor2);
+#ifdef unused
+static int set_rtc_irq_bit(unsigned char bit);
+#endif
+
+static int pcl816_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int pcl816_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+
+/*
+==============================================================================
+ ANALOG INPUT MODE0, 816 cards, slow version
+*/
+static int pcl816_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int timeout;
+
+ DPRINTK("mode 0 analog input\n");
+ // software trigger, DMA and INT off
+ outb(0, dev->iobase + PCL816_CONTROL);
+ // clear INT (conversion end) flag
+ outb(0, dev->iobase + PCL816_CLRINT);
+
+ // Set the input channel
+ outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
+ outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE); /* select gain */
+
+ for (n = 0; n < insn->n; n++) {
+
+ outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
+
+ timeout = 100;
+ while (timeout--) {
+ if (!(inb(dev->iobase + PCL816_STATUS) &
+ PCL816_STATUS_DRDY_MASK)) {
+ // return read value
+ data[n] =
+ ((inb(dev->iobase +
+ PCL816_AD_HI) << 8) |
+ (inb(dev->iobase + PCL816_AD_LO)));
+
+ outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
+ break;
+ }
+ comedi_udelay(1);
+ }
+ // Return timeout error
+ if (!timeout) {
+ comedi_error(dev, "A/D insn timeout\n");
+ data[0] = 0;
+ outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
+ return -EIO;
+ }
+
+ }
+ return n;
+}
+
+/*
+==============================================================================
+ analog input interrupt mode 1 & 3, 818 cards
+ one sample per interrupt version
+*/
+static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+ int low, hi;
+ int timeout = 50; /* wait max 50us */
+
+ while (timeout--) {
+ if (!(inb(dev->iobase + PCL816_STATUS) &
+ PCL816_STATUS_DRDY_MASK))
+ break;
+ comedi_udelay(1);
+ }
+ if (!timeout) { // timeout, bail error
+ outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
+ comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
+ pcl816_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+
+ }
+
+ // get the sample
+ low = inb(dev->iobase + PCL816_AD_LO);
+ hi = inb(dev->iobase + PCL816_AD_HI);
+
+ comedi_buf_put(s->async, (hi << 8) | low);
+
+ outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
+
+ if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
+ devpriv->ai_act_chanlist_pos = 0;
+
+ if (s->async->cur_chan == 0) {
+ devpriv->ai_act_scan++;
+ }
+
+ if (!devpriv->ai_neverending)
+ if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
+ /* all data sampled */
+ pcl816_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ }
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+}
+
+/*
+==============================================================================
+ analog input dma mode 1 & 3, 816 cards
+*/
+static void transfer_from_dma_buf(struct comedi_device * dev, struct comedi_subdevice * s,
+ short * ptr, unsigned int bufptr, unsigned int len)
+{
+ int i;
+
+ s->async->events = 0;
+
+ for (i = 0; i < len; i++) {
+
+ comedi_buf_put(s->async, ptr[bufptr++]);
+
+ if (++devpriv->ai_act_chanlist_pos >=
+ devpriv->ai_act_chanlist_len) {
+ devpriv->ai_act_chanlist_pos = 0;
+ devpriv->ai_act_scan++;
+ }
+
+ if (!devpriv->ai_neverending)
+ if (devpriv->ai_act_scan >= devpriv->ai_scans) { // all data sampled
+ pcl816_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ s->async->events |= COMEDI_CB_BLOCK;
+ break;
+ }
+ }
+
+ comedi_event(dev, s);
+}
+
+static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+ int len, bufptr, this_dma_buf;
+ unsigned long dma_flags;
+ short *ptr;
+
+ disable_dma(devpriv->dma);
+ this_dma_buf = devpriv->next_dma_buf;
+
+ if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) { // switch dma bufs
+
+ devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
+ set_dma_mode(devpriv->dma, DMA_MODE_READ);
+ dma_flags = claim_dma_lock();
+// clear_dma_ff (devpriv->dma);
+ set_dma_addr(devpriv->dma,
+ devpriv->hwdmaptr[devpriv->next_dma_buf]);
+ if (devpriv->dma_runs_to_end) {
+ set_dma_count(devpriv->dma,
+ devpriv->hwdmasize[devpriv->next_dma_buf]);
+ } else {
+ set_dma_count(devpriv->dma, devpriv->last_dma_run);
+ }
+ release_dma_lock(dma_flags);
+ enable_dma(devpriv->dma);
+ }
+
+ devpriv->dma_runs_to_end--;
+ outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
+
+ ptr = (short *) devpriv->dmabuf[this_dma_buf];
+
+ len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
+ bufptr = devpriv->ai_poll_ptr;
+ devpriv->ai_poll_ptr = 0;
+
+ transfer_from_dma_buf(dev, s, ptr, bufptr, len);
+ return IRQ_HANDLED;
+}
+
+/*
+==============================================================================
+ INT procedure
+*/
+static irqreturn_t interrupt_pcl816(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+ DPRINTK("<I>");
+
+ if (!dev->attached) {
+ comedi_error(dev, "premature interrupt");
+ return IRQ_HANDLED;
+ }
+
+ switch (devpriv->int816_mode) {
+ case INT_TYPE_AI1_DMA:
+ case INT_TYPE_AI3_DMA:
+ return interrupt_pcl816_ai_mode13_dma(irq, d);
+ case INT_TYPE_AI1_INT:
+ case INT_TYPE_AI3_INT:
+ return interrupt_pcl816_ai_mode13_int(irq, d);
+ }
+
+ outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
+ if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
+ (!devpriv->int816_mode)) {
+ if (devpriv->irq_was_now_closed) {
+ devpriv->irq_was_now_closed = 0;
+ // comedi_error(dev,"last IRQ..");
+ return IRQ_HANDLED;
+ }
+ comedi_error(dev, "bad IRQ!");
+ return IRQ_NONE;
+ }
+ comedi_error(dev, "IRQ from unknow source!");
+ return IRQ_NONE;
+}
+
+/*
+==============================================================================
+ COMMAND MODE
+*/
+static void pcl816_cmdtest_out(int e, struct comedi_cmd * cmd)
+{
+ rt_printk("pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
+ cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
+ rt_printk("pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
+ cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
+ rt_printk("pcl816 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
+ cmd->scan_end_src);
+ rt_printk("pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e,
+ cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
+}
+
+/*
+==============================================================================
+*/
+static int pcl816_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp, divisor1, divisor2;
+
+ DEBUG(rt_printk("pcl816 pcl812_ai_cmdtest\n");
+ pcl816_cmdtest_out(-1, cmd););
+
+ /* step 1: make sure trigger sources are trivially valid */
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_FOLLOW;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ if (!cmd->convert_src & (TRIG_EXT | TRIG_TIMER))
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err) {
+ return 1;
+ }
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (cmd->start_src != TRIG_NOW) {
+ cmd->start_src = TRIG_NOW;
+ err++;
+ }
+
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ cmd->scan_begin_src = TRIG_FOLLOW;
+ err++;
+ }
+
+ if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
+ cmd->convert_src = TRIG_TIMER;
+ err++;
+ }
+
+ if (cmd->scan_end_src != TRIG_COUNT) {
+ cmd->scan_end_src = TRIG_COUNT;
+ err++;
+ }
+
+ if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
+ err++;
+
+ if (err) {
+ return 2;
+ }
+
+ /* step 3: make sure arguments are trivially compatible */
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < this_board->ai_ns_min) {
+ cmd->convert_arg = this_board->ai_ns_min;
+ err++;
+ }
+ } else { /* TRIG_EXT */
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ }
+
+ if (!cmd->chanlist_len) {
+ cmd->chanlist_len = 1;
+ err++;
+ }
+ if (cmd->chanlist_len > this_board->n_aichan) {
+ cmd->chanlist_len = this_board->n_aichan;
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (!cmd->stop_arg) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ } else { /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err) {
+ return 3;
+ }
+
+ /* step 4: fix up any arguments */
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
+ &divisor1, &divisor2, &cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (cmd->convert_arg < this_board->ai_ns_min)
+ cmd->convert_arg = this_board->ai_ns_min;
+ if (tmp != cmd->convert_arg)
+ err++;
+ }
+
+ if (err) {
+ return 4;
+ }
+
+ return 0;
+}
+
+static int pcl816_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (cmd->start_src != TRIG_NOW)
+ return -EINVAL;
+ if (cmd->scan_begin_src != TRIG_FOLLOW)
+ return -EINVAL;
+ if (cmd->scan_end_src != TRIG_COUNT)
+ return -EINVAL;
+ if (cmd->scan_end_arg != cmd->chanlist_len)
+ return -EINVAL;
+// if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL;
+ if (devpriv->irq_blocked)
+ return -EBUSY;
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < this_board->ai_ns_min)
+ cmd->convert_arg = this_board->ai_ns_min;
+
+ i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
+ &divisor2, &cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (divisor1 == 1) { // PCL816 crash if any divisor is set to 1
+ divisor1 = 2;
+ divisor2 /= 2;
+ }
+ if (divisor2 == 1) {
+ divisor2 = 2;
+ divisor1 /= 2;
+ }
+ }
+
+ start_pacer(dev, -1, 0, 0); // stop pacer
+
+ if (!check_and_setup_channel_list(dev, s, cmd->chanlist,
+ cmd->chanlist_len))
+ return -EINVAL;
+ comedi_udelay(1);
+
+ devpriv->ai_act_scan = 0;
+ s->async->cur_chan = 0;
+ devpriv->irq_blocked = 1;
+ devpriv->ai_poll_ptr = 0;
+ devpriv->irq_was_now_closed = 0;
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ devpriv->ai_scans = cmd->stop_arg;
+ devpriv->ai_neverending = 0;
+ } else {
+ devpriv->ai_scans = 0;
+ devpriv->ai_neverending = 1;
+ }
+
+ if ((cmd->flags & TRIG_WAKE_EOS)) { // don't we want wake up every scan?
+ printk("pl816: You wankt WAKE_EOS but I dont want handle it");
+ // devpriv->ai_eos=1;
+ //if (devpriv->ai_n_chan==1)
+ // devpriv->dma=0; // DMA is useless for this situation
+ }
+
+ if (devpriv->dma) {
+ bytes = devpriv->hwdmasize[0];
+ if (!devpriv->ai_neverending) {
+ bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short); // how many
+ devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; // how many DMA pages we must fill
+ devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; //on last dma transfer must be moved
+ devpriv->dma_runs_to_end--;
+ if (devpriv->dma_runs_to_end >= 0)
+ bytes = devpriv->hwdmasize[0];
+ } else
+ devpriv->dma_runs_to_end = -1;
+
+ devpriv->next_dma_buf = 0;
+ set_dma_mode(devpriv->dma, DMA_MODE_READ);
+ dma_flags = claim_dma_lock();
+ clear_dma_ff(devpriv->dma);
+ set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
+ set_dma_count(devpriv->dma, bytes);
+ release_dma_lock(dma_flags);
+ enable_dma(devpriv->dma);
+ }
+
+ start_pacer(dev, 1, divisor1, divisor2);
+ dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
+
+ switch (cmd->convert_src) {
+ case TRIG_TIMER:
+ devpriv->int816_mode = INT_TYPE_AI1_DMA;
+ outb(0x32, dev->iobase + PCL816_CONTROL); // Pacer+IRQ+DMA
+ outb(dmairq, dev->iobase + PCL816_STATUS); // write irq and DMA to card
+ break;
+
+ default:
+ devpriv->int816_mode = INT_TYPE_AI3_DMA;
+ outb(0x34, dev->iobase + PCL816_CONTROL); // Ext trig+IRQ+DMA
+ outb(dmairq, dev->iobase + PCL816_STATUS); // write irq to card
+ break;
+ }
+
+ DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
+ return 0;
+}
+
+static int pcl816_ai_poll(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long flags;
+ unsigned int top1, top2, i;
+
+ if (!devpriv->dma)
+ return 0; // poll is valid only for DMA transfer
+
+ comedi_spin_lock_irqsave(&dev->spinlock, flags);
+
+ for (i = 0; i < 20; i++) {
+ top1 = get_dma_residue(devpriv->dma); // where is now DMA
+ top2 = get_dma_residue(devpriv->dma);
+ if (top1 == top2)
+ break;
+ }
+ if (top1 != top2) {
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ return 0;
+ }
+
+ top1 = devpriv->hwdmasize[0] - top1; // where is now DMA in buffer
+ top1 >>= 1; // sample position
+ top2 = top1 - devpriv->ai_poll_ptr;
+ if (top2 < 1) { // no new samples
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+ return 0;
+ }
+
+ transfer_from_dma_buf(dev, s,
+ (short *) devpriv->dmabuf[devpriv->next_dma_buf],
+ devpriv->ai_poll_ptr, top2);
+
+ devpriv->ai_poll_ptr = top1; // new buffer position
+ comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return s->async->buf_write_count - s->async->buf_read_count;
+}
+
+/*
+==============================================================================
+ cancel any mode 1-4 AI
+*/
+static int pcl816_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+// DEBUG(rt_printk("pcl816_ai_cancel()\n");)
+
+ if (devpriv->irq_blocked > 0) {
+ switch (devpriv->int816_mode) {
+#ifdef unused
+ case INT_TYPE_AI1_DMA_RTC:
+ case INT_TYPE_AI3_DMA_RTC:
+ set_rtc_irq_bit(0); // stop RTC
+ del_timer(&devpriv->rtc_irq_timer);
+#endif
+ case INT_TYPE_AI1_DMA:
+ case INT_TYPE_AI3_DMA:
+ disable_dma(devpriv->dma);
+ case INT_TYPE_AI1_INT:
+ case INT_TYPE_AI3_INT:
+ outb(inb(dev->iobase + PCL816_CONTROL) & 0x73, dev->iobase + PCL816_CONTROL); /* Stop A/D */
+ comedi_udelay(1);
+ outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
+ outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
+ outb(0x70, dev->iobase + PCL816_CTRCTL);
+ outb(0, dev->iobase + PCL816_AD_LO);
+ inb(dev->iobase + PCL816_AD_LO);
+ inb(dev->iobase + PCL816_AD_HI);
+ outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
+ outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
+ devpriv->irq_blocked = 0;
+ devpriv->irq_was_now_closed = devpriv->int816_mode;
+ devpriv->int816_mode = 0;
+ devpriv->last_int_sub = s;
+// s->busy = 0;
+ break;
+ }
+ }
+
+ DEBUG(rt_printk("comedi: pcl816_ai_cancel() successful\n");
+ )
+ return 0;
+}
+
+/*
+==============================================================================
+ chech for PCL816
+*/
+static int pcl816_check(unsigned long iobase)
+{
+ outb(0x00, iobase + PCL816_MUX);
+ comedi_udelay(1);
+ if (inb(iobase + PCL816_MUX) != 0x00)
+ return 1; //there isn't card
+ outb(0x55, iobase + PCL816_MUX);
+ comedi_udelay(1);
+ if (inb(iobase + PCL816_MUX) != 0x55)
+ return 1; //there isn't card
+ outb(0x00, iobase + PCL816_MUX);
+ comedi_udelay(1);
+ outb(0x18, iobase + PCL816_CONTROL);
+ comedi_udelay(1);
+ if (inb(iobase + PCL816_CONTROL) != 0x18)
+ return 1; //there isn't card
+ return 0; // ok, card exist
+}
+
+/*
+==============================================================================
+ reset whole PCL-816 cards
+*/
+static void pcl816_reset(struct comedi_device * dev)
+{
+// outb (0, dev->iobase + PCL818_DA_LO); // DAC=0V
+// outb (0, dev->iobase + PCL818_DA_HI);
+// comedi_udelay (1);
+// outb (0, dev->iobase + PCL818_DO_HI); // DO=$0000
+// outb (0, dev->iobase + PCL818_DO_LO);
+// comedi_udelay (1);
+ outb(0, dev->iobase + PCL816_CONTROL);
+ outb(0, dev->iobase + PCL816_MUX);
+ outb(0, dev->iobase + PCL816_CLRINT);
+ outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
+ outb(0x70, dev->iobase + PCL816_CTRCTL);
+ outb(0x30, dev->iobase + PCL816_CTRCTL);
+ outb(0, dev->iobase + PCL816_RANGE);
+}
+
+/*
+==============================================================================
+ Start/stop pacer onboard pacer
+*/
+static void
+start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
+ unsigned int divisor2)
+{
+ outb(0x32, dev->iobase + PCL816_CTRCTL);
+ outb(0xff, dev->iobase + PCL816_CTR0);
+ outb(0x00, dev->iobase + PCL816_CTR0);
+ comedi_udelay(1);
+ outb(0xb4, dev->iobase + PCL816_CTRCTL); // set counter 2 as mode 3
+ outb(0x74, dev->iobase + PCL816_CTRCTL); // set counter 1 as mode 3
+ comedi_udelay(1);
+
+ if (mode == 1) {
+ DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
+ divisor2);
+ outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
+ outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
+ outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
+ outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
+ }
+
+ /* clear pending interrupts (just in case) */
+// outb(0, dev->iobase + PCL816_CLRINT);
+}
+
+/*
+==============================================================================
+ Check if channel list from user is builded correctly
+ If it's ok, then program scan/gain logic
+*/
+static int
+check_and_setup_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int *chanlist, int chanlen)
+{
+ unsigned int chansegment[16];
+ unsigned int i, nowmustbechan, seglen, segpos;
+
+ // correct channel and range number check itself comedi/range.c
+ if (chanlen < 1) {
+ comedi_error(dev, "range/channel list is empty!");
+ return 0;
+ }
+
+ if (chanlen > 1) {
+ chansegment[0] = chanlist[0]; // first channel is everytime ok
+ for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
+ // build part of chanlist
+ DEBUG(rt_printk("%d. %d %d\n", i, CR_CHAN(chanlist[i]),
+ CR_RANGE(chanlist[i]));
+ )
+ if (chanlist[0] == chanlist[i])
+ break; // we detect loop, this must by finish
+ nowmustbechan =
+ (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
+ if (nowmustbechan != CR_CHAN(chanlist[i])) {
+ // channel list isn't continous :-(
+ rt_printk
+ ("comedi%d: pcl816: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
+ dev->minor, i, CR_CHAN(chanlist[i]),
+ nowmustbechan, CR_CHAN(chanlist[0]));
+ return 0;
+ }
+ chansegment[i] = chanlist[i]; // well, this is next correct channel in list
+ }
+
+ for (i = 0, segpos = 0; i < chanlen; i++) { // check whole chanlist
+ DEBUG(rt_printk("%d %d=%d %d\n",
+ CR_CHAN(chansegment[i % seglen]),
+ CR_RANGE(chansegment[i % seglen]),
+ CR_CHAN(chanlist[i]),
+ CR_RANGE(chanlist[i]));
+ )
+ if (chanlist[i] != chansegment[i % seglen]) {
+ rt_printk
+ ("comedi%d: pcl816: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
+ dev->minor, i, CR_CHAN(chansegment[i]),
+ CR_RANGE(chansegment[i]),
+ CR_AREF(chansegment[i]),
+ CR_CHAN(chanlist[i % seglen]),
+ CR_RANGE(chanlist[i % seglen]),
+ CR_AREF(chansegment[i % seglen]));
+ return 0; // chan/gain list is strange
+ }
+ }
+ } else {
+ seglen = 1;
+ }
+
+ devpriv->ai_act_chanlist_len = seglen;
+ devpriv->ai_act_chanlist_pos = 0;
+
+ for (i = 0; i < seglen; i++) { // store range list to card
+ devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
+ outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
+ outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE); /* select gain */
+ }
+
+ comedi_udelay(1);
+
+ outb(devpriv->ai_act_chanlist[0] | (devpriv->ai_act_chanlist[seglen - 1] << 4), dev->iobase + PCL816_MUX); /* select channel interval to scan */
+
+ return 1; // we can serve this with MUX logic
+}
+
+#ifdef unused
+/*
+==============================================================================
+ Enable(1)/disable(0) periodic interrupts from RTC
+*/
+static int set_rtc_irq_bit(unsigned char bit)
+{
+ unsigned char val;
+ unsigned long flags;
+
+ if (bit == 1) {
+ RTC_timer_lock++;
+ if (RTC_timer_lock > 1)
+ return 0;
+ } else {
+ RTC_timer_lock--;
+ if (RTC_timer_lock < 0)
+ RTC_timer_lock = 0;
+ if (RTC_timer_lock > 0)
+ return 0;
+ }
+
+ save_flags(flags);
+ cli();
+ val = CMOS_READ(RTC_CONTROL);
+ if (bit) {
+ val |= RTC_PIE;
+ } else {
+ val &= ~RTC_PIE;
+ }
+ CMOS_WRITE(val, RTC_CONTROL);
+ CMOS_READ(RTC_INTR_FLAGS);
+ restore_flags(flags);
+ return 0;
+}
+#endif
+
+/*
+==============================================================================
+ Free any resources that we have claimed
+*/
+static void free_resources(struct comedi_device * dev)
+{
+ //rt_printk("free_resource()\n");
+ if (dev->private) {
+ pcl816_ai_cancel(dev, devpriv->sub_ai);
+ pcl816_reset(dev);
+ if (devpriv->dma)
+ free_dma(devpriv->dma);
+ if (devpriv->dmabuf[0])
+ free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
+ if (devpriv->dmabuf[1])
+ free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
+#ifdef unused
+ if (devpriv->rtc_irq)
+ comedi_free_irq(devpriv->rtc_irq, dev);
+ if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
+ if (devpriv->rtc_iobase)
+ release_region(devpriv->rtc_iobase,
+ devpriv->rtc_iosize);
+ }
+#endif
+ }
+
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ if (dev->iobase)
+ release_region(dev->iobase, this_board->io_range);
+ //rt_printk("free_resource() end\n");
+}
+
+/*
+==============================================================================
+
+ Initialization
+
+*/
+static int pcl816_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int ret;
+ unsigned long iobase;
+ unsigned int irq, dma;
+ unsigned long pages;
+ //int i;
+ struct comedi_subdevice *s;
+
+ /* claim our I/O space */
+ iobase = it->options[0];
+ printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
+ this_board->name, iobase);
+
+ if (!request_region(iobase, this_board->io_range, "pcl816")) {
+ rt_printk("I/O port conflict\n");
+ return -EIO;
+ }
+
+ dev->iobase = iobase;
+
+ if (pcl816_check(iobase)) {
+ rt_printk(", I cann't detect board. FAIL!\n");
+ return -EIO;
+ }
+
+ if ((ret = alloc_private(dev, sizeof(struct pcl816_private))) < 0)
+ return ret; /* Can't alloc mem */
+
+ /* set up some name stuff */
+ dev->board_name = this_board->name;
+
+ /* grab our IRQ */
+ irq = 0;
+ if (this_board->IRQbits != 0) { /* board support IRQ */
+ irq = it->options[1];
+ if (irq) { /* we want to use IRQ */
+ if (((1 << irq) & this_board->IRQbits) == 0) {
+ rt_printk
+ (", IRQ %u is out of allowed range, DISABLING IT",
+ irq);
+ irq = 0; /* Bad IRQ */
+ } else {
+ if (comedi_request_irq(irq, interrupt_pcl816, 0,
+ "pcl816", dev)) {
+ rt_printk
+ (", unable to allocate IRQ %u, DISABLING IT",
+ irq);
+ irq = 0; /* Can't use IRQ */
+ } else {
+ rt_printk(", irq=%u", irq);
+ }
+ }
+ }
+ }
+
+ dev->irq = irq;
+ if (irq) {
+ devpriv->irq_free = 1;
+ } /* 1=we have allocated irq */
+ else {
+ devpriv->irq_free = 0;
+ }
+ devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
+ devpriv->int816_mode = 0; /* mode of irq */
+
+#ifdef unused
+ /* grab RTC for DMA operations */
+ devpriv->dma_rtc = 0;
+ if (it->options[2] > 0) { // we want to use DMA
+ if (RTC_lock == 0) {
+ if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
+ "pcl816 (RTC)"))
+ goto no_rtc;
+ }
+ devpriv->rtc_iobase = RTC_PORT(0);
+ devpriv->rtc_iosize = RTC_IO_EXTENT;
+ RTC_lock++;
+#ifdef UNTESTED_CODE
+ if (!comedi_request_irq(RTC_IRQ,
+ interrupt_pcl816_ai_mode13_dma_rtc, 0,
+ "pcl816 DMA (RTC)", dev)) {
+ devpriv->dma_rtc = 1;
+ devpriv->rtc_irq = RTC_IRQ;
+ rt_printk(", dma_irq=%u", devpriv->rtc_irq);
+ } else {
+ RTC_lock--;
+ if (RTC_lock == 0) {
+ if (devpriv->rtc_iobase)
+ release_region(devpriv->rtc_iobase,
+ devpriv->rtc_iosize);
+ }
+ devpriv->rtc_iobase = 0;
+ devpriv->rtc_iosize = 0;
+ }
+#else
+ printk("pcl816: RTC code missing");
+#endif
+
+ }
+
+ no_rtc:
+#endif
+ /* grab our DMA */
+ dma = 0;
+ devpriv->dma = dma;
+ if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
+ goto no_dma; /* if we haven't IRQ, we can't use DMA */
+
+ if (this_board->DMAbits != 0) { /* board support DMA */
+ dma = it->options[2];
+ if (dma < 1)
+ goto no_dma; /* DMA disabled */
+
+ if (((1 << dma) & this_board->DMAbits) == 0) {
+ rt_printk(", DMA is out of allowed range, FAIL!\n");
+ return -EINVAL; /* Bad DMA */
+ }
+ ret = request_dma(dma, "pcl816");
+ if (ret) {
+ rt_printk(", unable to allocate DMA %u, FAIL!\n", dma);
+ return -EBUSY; /* DMA isn't free */
+ }
+
+ devpriv->dma = dma;
+ rt_printk(", dma=%u", dma);
+ pages = 2; /* we need 16KB */
+ devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
+
+ if (!devpriv->dmabuf[0]) {
+ rt_printk(", unable to allocate DMA buffer, FAIL!\n");
+ /* maybe experiment with try_to_free_pages() will help .... */
+ return -EBUSY; /* no buffer :-( */
+ }
+ devpriv->dmapages[0] = pages;
+ devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
+ devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
+ //rt_printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE);
+
+ if (devpriv->dma_rtc == 0) { // we must do duble buff :-(
+ devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
+ if (!devpriv->dmabuf[1]) {
+ rt_printk
+ (", unable to allocate DMA buffer, FAIL!\n");
+ return -EBUSY;
+ }
+ devpriv->dmapages[1] = pages;
+ devpriv->hwdmaptr[1] =
+ virt_to_bus((void *)devpriv->dmabuf[1]);
+ devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
+ }
+ }
+
+ no_dma:
+
+/* if (this_board->n_aochan > 0)
+ subdevs[1] = COMEDI_SUBD_AO;
+ if (this_board->n_dichan > 0)
+ subdevs[2] = COMEDI_SUBD_DI;
+ if (this_board->n_dochan > 0)
+ subdevs[3] = COMEDI_SUBD_DO;
+*/
+ if ((ret = alloc_subdevices(dev, 1)) < 0)
+ return ret;
+
+ s = dev->subdevices + 0;
+ if (this_board->n_aichan > 0) {
+ s->type = COMEDI_SUBD_AI;
+ devpriv->sub_ai = s;
+ dev->read_subdev = s;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+ s->n_chan = this_board->n_aichan;
+ s->subdev_flags |= SDF_DIFF;
+ //printk (", %dchans DIFF DAC - %d", s->n_chan, i);
+ s->maxdata = this_board->ai_maxdata;
+ s->len_chanlist = this_board->ai_chanlist;
+ s->range_table = this_board->ai_range_type;
+ s->cancel = pcl816_ai_cancel;
+ s->do_cmdtest = pcl816_ai_cmdtest;
+ s->do_cmd = pcl816_ai_cmd;
+ s->poll = pcl816_ai_poll;
+ s->insn_read = pcl816_ai_insn_read;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+#if 0
+case COMEDI_SUBD_AO:
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = this_board->n_aochan;
+ s->maxdata = this_board->ao_maxdata;
+ s->len_chanlist = this_board->ao_chanlist;
+ s->range_table = this_board->ao_range_type;
+ break;
+
+case COMEDI_SUBD_DI:
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = this_board->n_dichan;
+ s->maxdata = 1;
+ s->len_chanlist = this_board->n_dichan;
+ s->range_table = &range_digital;
+ break;
+
+case COMEDI_SUBD_DO:
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = this_board->n_dochan;
+ s->maxdata = 1;
+ s->len_chanlist = this_board->n_dochan;
+ s->range_table = &range_digital;
+ break;
+#endif
+
+ pcl816_reset(dev);
+
+ rt_printk("\n");
+
+ return 0;
+}
+
+/*
+==============================================================================
+ Removes device
+ */
+static int pcl816_detach(struct comedi_device * dev)
+{
+ DEBUG(rt_printk("comedi%d: pcl816: remove\n", dev->minor);
+ )
+ free_resources(dev);
+#ifdef unused
+ if (devpriv->dma_rtc)
+ RTC_lock--;
+#endif
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/pcl818.c b/drivers/staging/comedi/drivers/pcl818.c
new file mode 100644
index 000000000000..43a9d56c6a43
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcl818.c
@@ -0,0 +1,1988 @@
+/*
+ comedi/drivers/pcl818.c
+
+ Author: Michal Dobes <dobes@tesnet.cz>
+
+ hardware driver for Advantech cards:
+ card: PCL-818L, PCL-818H, PCL-818HD, PCL-818HG, PCL-818, PCL-718
+ driver: pcl818l, pcl818h, pcl818hd, pcl818hg, pcl818, pcl718
+*/
+/*
+Driver: pcl818
+Description: Advantech PCL-818 cards, PCL-718
+Author: Michal Dobes <dobes@tesnet.cz>
+Devices: [Advantech] PCL-818L (pcl818l), PCL-818H (pcl818h),
+ PCL-818HD (pcl818hd), PCL-818HG (pcl818hg), PCL-818 (pcl818),
+ PCL-718 (pcl718)
+Status: works
+
+All cards have 16 SE/8 DIFF ADCs, one or two DACs, 16 DI and 16 DO.
+Differences are only at maximal sample speed, range list and FIFO
+support.
+The driver support AI mode 0, 1, 3 other subdevices (AO, DI, DO) support
+only mode 0. If DMA/FIFO/INT are disabled then AI support only mode 0.
+PCL-818HD and PCL-818HG support 1kword FIFO. Driver support this FIFO
+but this code is untested.
+A word or two about DMA. Driver support DMA operations at two ways:
+1) DMA uses two buffers and after one is filled then is generated
+ INT and DMA restart with second buffer. With this mode I'm unable run
+ more that 80Ksamples/secs without data dropouts on K6/233.
+2) DMA uses one buffer and run in autoinit mode and the data are
+ from DMA buffer moved on the fly with 2kHz interrupts from RTC.
+ This mode is used if the interrupt 8 is available for allocation.
+ If not, then first DMA mode is used. With this I can run at
+ full speed one card (100ksamples/secs) or two cards with
+ 60ksamples/secs each (more is problem on account of ISA limitations).
+ To use this mode you must have compiled kernel with disabled
+ "Enhanced Real Time Clock Support".
+ Maybe you can have problems if you use xntpd or similar.
+ If you've data dropouts with DMA mode 2 then:
+ a) disable IDE DMA
+ b) switch text mode console to fb.
+
+ Options for PCL-818L:
+ [0] - IO Base
+ [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
+ [2] - DMA (0=disable, 1, 3)
+ [3] - 0, 10=10MHz clock for 8254
+ 1= 1MHz clock for 8254
+ [4] - 0, 5=A/D input -5V.. +5V
+ 1, 10=A/D input -10V..+10V
+ [5] - 0, 5=D/A output 0-5V (internal reference -5V)
+ 1, 10=D/A output 0-10V (internal reference -10V)
+ 2 =D/A output unknow (external reference)
+
+ Options for PCL-818, PCL-818H:
+ [0] - IO Base
+ [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
+ [2] - DMA (0=disable, 1, 3)
+ [3] - 0, 10=10MHz clock for 8254
+ 1= 1MHz clock for 8254
+ [4] - 0, 5=D/A output 0-5V (internal reference -5V)
+ 1, 10=D/A output 0-10V (internal reference -10V)
+ 2 =D/A output unknow (external reference)
+
+ Options for PCL-818HD, PCL-818HG:
+ [0] - IO Base
+ [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
+ [2] - DMA/FIFO (-1=use FIFO, 0=disable both FIFO and DMA,
+ 1=use DMA ch 1, 3=use DMA ch 3)
+ [3] - 0, 10=10MHz clock for 8254
+ 1= 1MHz clock for 8254
+ [4] - 0, 5=D/A output 0-5V (internal reference -5V)
+ 1, 10=D/A output 0-10V (internal reference -10V)
+ 2 =D/A output unknow (external reference)
+
+ Options for PCL-718:
+ [0] - IO Base
+ [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
+ [2] - DMA (0=disable, 1, 3)
+ [3] - 0, 10=10MHz clock for 8254
+ 1= 1MHz clock for 8254
+ [4] - 0=A/D Range is +/-10V
+ 1= +/-5V
+ 2= +/-2.5V
+ 3= +/-1V
+ 4= +/-0.5V
+ 5= user defined bipolar
+ 6= 0-10V
+ 7= 0-5V
+ 8= 0-2V
+ 9= 0-1V
+ 10= user defined unipolar
+ [5] - 0, 5=D/A outputs 0-5V (internal reference -5V)
+ 1, 10=D/A outputs 0-10V (internal reference -10V)
+ 2=D/A outputs unknow (external reference)
+ [6] - 0, 60=max 60kHz A/D sampling
+ 1,100=max 100kHz A/D sampling (PCL-718 with Option 001 installed)
+
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+#include <linux/mc146818rtc.h>
+#include <linux/delay.h>
+#include <asm/dma.h>
+
+#include "8253.h"
+
+// #define PCL818_MODE13_AO 1
+
+// boards constants
+
+#define boardPCL818L 0
+#define boardPCL818H 1
+#define boardPCL818HD 2
+#define boardPCL818HG 3
+#define boardPCL818 4
+#define boardPCL718 5
+
+// IO space len
+#define PCLx1x_RANGE 16
+// IO space len if we use FIFO
+#define PCLx1xFIFO_RANGE 32
+
+// W: clear INT request
+#define PCL818_CLRINT 8
+// R: return status byte
+#define PCL818_STATUS 8
+// R: A/D high byte W: A/D range control
+#define PCL818_RANGE 1
+// R: next mux scan channel W: mux scan channel & range control pointer
+#define PCL818_MUX 2
+// R/W: operation control register
+#define PCL818_CONTROL 9
+// W: counter enable
+#define PCL818_CNTENABLE 10
+
+// R: low byte of A/D W: soft A/D trigger
+#define PCL818_AD_LO 0
+// R: high byte of A/D W: A/D range control
+#define PCL818_AD_HI 1
+// W: D/A low&high byte
+#define PCL818_DA_LO 4
+#define PCL818_DA_HI 5
+// R: low&high byte of DI
+#define PCL818_DI_LO 3
+#define PCL818_DI_HI 11
+// W: low&high byte of DO
+#define PCL818_DO_LO 3
+#define PCL818_DO_HI 11
+// W: PCL718 second D/A
+#define PCL718_DA2_LO 6
+#define PCL718_DA2_HI 7
+// counters
+#define PCL818_CTR0 12
+#define PCL818_CTR1 13
+#define PCL818_CTR2 14
+// W: counter control
+#define PCL818_CTRCTL 15
+
+// W: fifo enable/disable
+#define PCL818_FI_ENABLE 6
+// W: fifo interrupt clear
+#define PCL818_FI_INTCLR 20
+// W: fifo interrupt clear
+#define PCL818_FI_FLUSH 25
+// R: fifo status
+#define PCL818_FI_STATUS 25
+// R: one record from FIFO
+#define PCL818_FI_DATALO 23
+#define PCL818_FI_DATAHI 23
+
+// type of interrupt handler
+#define INT_TYPE_AI1_INT 1
+#define INT_TYPE_AI1_DMA 2
+#define INT_TYPE_AI1_FIFO 3
+#define INT_TYPE_AI3_INT 4
+#define INT_TYPE_AI3_DMA 5
+#define INT_TYPE_AI3_FIFO 6
+#ifdef PCL818_MODE13_AO
+#define INT_TYPE_AO1_INT 7
+#define INT_TYPE_AO3_INT 8
+#endif
+
+#ifdef unused
+// RTC stuff...
+#define INT_TYPE_AI1_DMA_RTC 9
+#define INT_TYPE_AI3_DMA_RTC 10
+
+#define RTC_IRQ 8
+#define RTC_IO_EXTENT 0x10
+#endif
+
+#define MAGIC_DMA_WORD 0x5a5a
+
+static const struct comedi_lrange range_pcl818h_ai = { 9, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ BIP_RANGE(10),
+ }
+};
+
+static const struct comedi_lrange range_pcl818hg_ai = { 10, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.005),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01),
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.01),
+ }
+};
+
+static const struct comedi_lrange range_pcl818l_l_ai = { 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ }
+};
+
+static const struct comedi_lrange range_pcl818l_h_ai = { 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ }
+};
+
+static const struct comedi_lrange range718_bipolar1 = { 1, {BIP_RANGE(1),} };
+static const struct comedi_lrange range718_bipolar0_5 = { 1, {BIP_RANGE(0.5),} };
+static const struct comedi_lrange range718_unipolar2 = { 1, {UNI_RANGE(2),} };
+static const struct comedi_lrange range718_unipolar1 = { 1, {BIP_RANGE(1),} };
+
+static int pcl818_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcl818_detach(struct comedi_device * dev);
+
+#ifdef unused
+static int RTC_lock = 0; /* RTC lock */
+static int RTC_timer_lock = 0; /* RTC int lock */
+#endif
+
+struct pcl818_board {
+
+ const char *name; // driver name
+ int n_ranges; // len of range list
+ int n_aichan_se; // num of A/D chans in single ended mode
+ int n_aichan_diff; // num of A/D chans in diferencial mode
+ unsigned int ns_min; // minimal alllowed delay between samples (in ns)
+ int n_aochan; // num of D/A chans
+ int n_dichan; // num of DI chans
+ int n_dochan; // num of DO chans
+ const struct comedi_lrange *ai_range_type; // default A/D rangelist
+ const struct comedi_lrange *ao_range_type; // default D/A rangelist
+ unsigned int io_range; // len of IO space
+ unsigned int IRQbits; // allowed interrupts
+ unsigned int DMAbits; // allowed DMA chans
+ int ai_maxdata; // maxdata for A/D
+ int ao_maxdata; // maxdata for D/A
+ unsigned char fifo; // 1=board has FIFO
+ int is_818;
+};
+
+
+static const struct pcl818_board boardtypes[] = {
+ {"pcl818l", 4, 16, 8, 25000, 1, 16, 16, &range_pcl818l_l_ai,
+ &range_unipolar5, PCLx1x_RANGE, 0x00fc,
+ 0x0a, 0xfff, 0xfff, 0, 1},
+ {"pcl818h", 9, 16, 8, 10000, 1, 16, 16, &range_pcl818h_ai,
+ &range_unipolar5, PCLx1x_RANGE, 0x00fc,
+ 0x0a, 0xfff, 0xfff, 0, 1},
+ {"pcl818hd", 9, 16, 8, 10000, 1, 16, 16, &range_pcl818h_ai,
+ &range_unipolar5, PCLx1x_RANGE, 0x00fc,
+ 0x0a, 0xfff, 0xfff, 1, 1},
+ {"pcl818hg", 12, 16, 8, 10000, 1, 16, 16, &range_pcl818hg_ai,
+ &range_unipolar5, PCLx1x_RANGE, 0x00fc,
+ 0x0a, 0xfff, 0xfff, 1, 1},
+ {"pcl818", 9, 16, 8, 10000, 2, 16, 16, &range_pcl818h_ai,
+ &range_unipolar5, PCLx1x_RANGE, 0x00fc,
+ 0x0a, 0xfff, 0xfff, 0, 1},
+ {"pcl718", 1, 16, 8, 16000, 2, 16, 16, &range_unipolar5,
+ &range_unipolar5, PCLx1x_RANGE, 0x00fc,
+ 0x0a, 0xfff, 0xfff, 0, 0},
+ /* pcm3718 */
+ {"pcm3718", 9, 16, 8, 10000, 0, 16, 16, &range_pcl818h_ai,
+ &range_unipolar5, PCLx1x_RANGE, 0x00fc,
+ 0x0a, 0xfff, 0xfff, 0, 1 /* XXX ? */ },
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl818_board))
+
+static struct comedi_driver driver_pcl818 = {
+ driver_name:"pcl818",
+ module:THIS_MODULE,
+ attach:pcl818_attach,
+ detach:pcl818_detach,
+ board_name:&boardtypes[0].name,
+ num_names:n_boardtypes,
+ offset:sizeof(struct pcl818_board),
+};
+
+COMEDI_INITCLEANUP(driver_pcl818);
+
+struct pcl818_private {
+
+ unsigned int dma; // used DMA, 0=don't use DMA
+ int dma_rtc; // 1=RTC used with DMA, 0=no RTC alloc
+ unsigned int io_range;
+#ifdef unused
+ unsigned long rtc_iobase; // RTC port region
+ unsigned int rtc_iosize;
+ unsigned int rtc_irq;
+ struct timer_list rtc_irq_timer; // timer for RTC sanity check
+ unsigned long rtc_freq; // RTC int freq
+ int rtc_irq_blocked; // 1=we now do AI with DMA&RTC
+#endif
+ unsigned long dmabuf[2]; // pointers to begin of DMA buffers
+ unsigned int dmapages[2]; // len of DMA buffers in PAGE_SIZEs
+ unsigned int hwdmaptr[2]; // hardware address of DMA buffers
+ unsigned int hwdmasize[2]; // len of DMA buffers in Bytes
+ unsigned int dmasamplsize; // size in samples hwdmasize[0]/2
+ unsigned int last_top_dma; // DMA pointer in last RTC int
+ int next_dma_buf; // which DMA buffer will be used next round
+ long dma_runs_to_end; // how many we must permorm DMA transfer to end of record
+ unsigned long last_dma_run; // how many bytes we must transfer on last DMA page
+ unsigned char neverending_ai; // if=1, then we do neverending record (you must use cancel())
+ unsigned int ns_min; // manimal alllowed delay between samples (in us) for actual card
+ int i8253_osc_base; // 1/frequency of on board oscilator in ns
+ int irq_free; // 1=have allocated IRQ
+ int irq_blocked; // 1=IRQ now uses any subdev
+ int irq_was_now_closed; // when IRQ finish, there's stored int818_mode for last interrupt
+ int ai_mode; // who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma
+ struct comedi_subdevice *last_int_sub; // ptr to subdevice which now finish
+ int ai_act_scan; // how many scans we finished
+ int ai_act_chan; // actual position in actual scan
+ unsigned int act_chanlist[16]; // MUX setting for actual AI operations
+ unsigned int act_chanlist_len; // how long is actual MUX list
+ unsigned int act_chanlist_pos; // actual position in MUX list
+ unsigned int ai_scans; // len of scanlist
+ unsigned int ai_n_chan; // how many channels is measured
+ unsigned int *ai_chanlist; // actaul chanlist
+ unsigned int ai_flags; // flaglist
+ unsigned int ai_data_len; // len of data buffer
+ short *ai_data; // data buffer
+ unsigned int ai_timer1; // timers
+ unsigned int ai_timer2;
+ struct comedi_subdevice *sub_ai; // ptr to AI subdevice
+ unsigned char usefifo; // 1=use fifo
+ unsigned int ao_readback[2];
+};
+
+
+static const unsigned int muxonechan[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // used for gain list programming
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+};
+
+#define devpriv ((struct pcl818_private *)dev->private)
+#define this_board ((const struct pcl818_board *)dev->board_ptr)
+
+/*
+==============================================================================
+*/
+static void setup_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int *chanlist, unsigned int n_chan, unsigned int seglen);
+static int check_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int *chanlist, unsigned int n_chan);
+
+static int pcl818_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
+ unsigned int divisor2);
+
+#ifdef unused
+static int set_rtc_irq_bit(unsigned char bit);
+static void rtc_dropped_irq(unsigned long data);
+static int rtc_setfreq_irq(int freq);
+#endif
+
+/*
+==============================================================================
+ ANALOG INPUT MODE0, 818 cards, slow version
+*/
+static int pcl818_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int timeout;
+
+ /* software trigger, DMA and INT off */
+ outb(0, dev->iobase + PCL818_CONTROL);
+
+ /* select channel */
+ outb(muxonechan[CR_CHAN(insn->chanspec)], dev->iobase + PCL818_MUX);
+
+ /* select gain */
+ outb(CR_RANGE(insn->chanspec), dev->iobase + PCL818_RANGE);
+
+ for (n = 0; n < insn->n; n++) {
+
+ /* clear INT (conversion end) flag */
+ outb(0, dev->iobase + PCL818_CLRINT);
+
+ /* start conversion */
+ outb(0, dev->iobase + PCL818_AD_LO);
+
+ timeout = 100;
+ while (timeout--) {
+ if (inb(dev->iobase + PCL818_STATUS) & 0x10)
+ goto conv_finish;
+ comedi_udelay(1);
+ }
+ comedi_error(dev, "A/D insn timeout");
+ /* clear INT (conversion end) flag */
+ outb(0, dev->iobase + PCL818_CLRINT);
+ return -EIO;
+
+ conv_finish:
+ data[n] = ((inb(dev->iobase + PCL818_AD_HI) << 4) |
+ (inb(dev->iobase + PCL818_AD_LO) >> 4));
+ }
+
+ return n;
+}
+
+/*
+==============================================================================
+ ANALOG OUTPUT MODE0, 818 cards
+ only one sample per call is supported
+*/
+static int pcl818_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (n = 0; n < insn->n; n++) {
+ data[n] = devpriv->ao_readback[chan];
+ }
+
+ return n;
+}
+
+static int pcl818_ao_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (n = 0; n < insn->n; n++) {
+ devpriv->ao_readback[chan] = data[n];
+ outb((data[n] & 0x000f) << 4, dev->iobase +
+ (chan) ? PCL718_DA2_LO : PCL818_DA_LO);
+ outb((data[n] & 0x0ff0) >> 4, dev->iobase +
+ (chan) ? PCL718_DA2_HI : PCL818_DA_HI);
+ }
+
+ return n;
+}
+
+/*
+==============================================================================
+ DIGITAL INPUT MODE0, 818 cards
+
+ only one sample per call is supported
+*/
+static int pcl818_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ data[1] = inb(dev->iobase + PCL818_DI_LO) |
+ (inb(dev->iobase + PCL818_DI_HI) << 8);
+
+ return 2;
+}
+
+/*
+==============================================================================
+ DIGITAL OUTPUT MODE0, 818 cards
+
+ only one sample per call is supported
+*/
+static int pcl818_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+
+ outb(s->state & 0xff, dev->iobase + PCL818_DO_LO);
+ outb((s->state >> 8), dev->iobase + PCL818_DO_HI);
+
+ data[1] = s->state;
+
+ return 2;
+}
+
+/*
+==============================================================================
+ analog input interrupt mode 1 & 3, 818 cards
+ one sample per interrupt version
+*/
+static irqreturn_t interrupt_pcl818_ai_mode13_int(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+ int low;
+ int timeout = 50; /* wait max 50us */
+
+ while (timeout--) {
+ if (inb(dev->iobase + PCL818_STATUS) & 0x10)
+ goto conv_finish;
+ comedi_udelay(1);
+ }
+ outb(0, dev->iobase + PCL818_STATUS); /* clear INT request */
+ comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
+ pcl818_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+
+ conv_finish:
+ low = inb(dev->iobase + PCL818_AD_LO);
+ comedi_buf_put(s->async, ((inb(dev->iobase + PCL818_AD_HI) << 4) | (low >> 4))); // get one sample
+ outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
+
+ if ((low & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { // dropout!
+ rt_printk
+ ("comedi: A/D mode1/3 IRQ - channel dropout %x!=%x !\n",
+ (low & 0xf),
+ devpriv->act_chanlist[devpriv->act_chanlist_pos]);
+ pcl818_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+ }
+ if (s->async->cur_chan == 0) {
+ // rt_printk("E");
+ devpriv->ai_act_scan--;
+ }
+
+ if (!devpriv->neverending_ai) {
+ if (devpriv->ai_act_scan == 0) { /* all data sampled */
+ pcl818_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ }
+ }
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+}
+
+/*
+==============================================================================
+ analog input dma mode 1 & 3, 818 cards
+*/
+static irqreturn_t interrupt_pcl818_ai_mode13_dma(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+ int i, len, bufptr;
+ unsigned long flags;
+ short *ptr;
+
+ disable_dma(devpriv->dma);
+ devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
+ if ((devpriv->dma_runs_to_end) > -1 || devpriv->neverending_ai) { // switch dma bufs
+ set_dma_mode(devpriv->dma, DMA_MODE_READ);
+ flags = claim_dma_lock();
+ set_dma_addr(devpriv->dma,
+ devpriv->hwdmaptr[devpriv->next_dma_buf]);
+ if (devpriv->dma_runs_to_end || devpriv->neverending_ai) {
+ set_dma_count(devpriv->dma,
+ devpriv->hwdmasize[devpriv->next_dma_buf]);
+ } else {
+ set_dma_count(devpriv->dma, devpriv->last_dma_run);
+ }
+ release_dma_lock(flags);
+ enable_dma(devpriv->dma);
+ }
+ rt_printk("comedi: A/D mode1/3 IRQ \n");
+
+ devpriv->dma_runs_to_end--;
+ outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
+ ptr = (short *) devpriv->dmabuf[1 - devpriv->next_dma_buf];
+
+ len = devpriv->hwdmasize[0] >> 1;
+ bufptr = 0;
+
+ for (i = 0; i < len; i++) {
+ if ((ptr[bufptr] & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { // dropout!
+ rt_printk
+ ("comedi: A/D mode1/3 DMA - channel dropout %d(card)!=%d(chanlist) at %d !\n",
+ (ptr[bufptr] & 0xf),
+ devpriv->act_chanlist[devpriv->
+ act_chanlist_pos],
+ devpriv->act_chanlist_pos);
+ pcl818_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+ }
+
+ comedi_buf_put(s->async, ptr[bufptr++] >> 4); // get one sample
+
+ devpriv->act_chanlist_pos++;
+ if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len) {
+ devpriv->ai_act_scan--;
+ devpriv->act_chanlist_pos = 0;
+ }
+
+ if (!devpriv->neverending_ai)
+ if (devpriv->ai_act_scan == 0) { /* all data sampled */
+ pcl818_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ // printk("done int ai13 dma\n");
+ return IRQ_HANDLED;
+ }
+ }
+
+ if (len > 0)
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+}
+
+#ifdef unused
+/*
+==============================================================================
+ analog input dma mode 1 & 3 over RTC, 818 cards
+*/
+static irqreturn_t interrupt_pcl818_ai_mode13_dma_rtc(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+ unsigned long tmp;
+ unsigned int top1, top2, i, bufptr;
+ long ofs_dats;
+ short *dmabuf = (short *) devpriv->dmabuf[0];
+
+ //outb(2,0x378);
+ switch (devpriv->ai_mode) {
+ case INT_TYPE_AI1_DMA_RTC:
+ case INT_TYPE_AI3_DMA_RTC:
+ tmp = (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
+ mod_timer(&devpriv->rtc_irq_timer,
+ jiffies + HZ / devpriv->rtc_freq + 2 * HZ / 100);
+
+ for (i = 0; i < 10; i++) {
+ top1 = get_dma_residue(devpriv->dma);
+ top2 = get_dma_residue(devpriv->dma);
+ if (top1 == top2)
+ break;
+ }
+
+ if (top1 != top2)
+ return IRQ_HANDLED;
+ top1 = devpriv->hwdmasize[0] - top1; // where is now DMA in buffer
+ top1 >>= 1;
+ ofs_dats = top1 - devpriv->last_top_dma; // new samples from last call
+ if (ofs_dats < 0)
+ ofs_dats = (devpriv->dmasamplsize) + ofs_dats;
+ if (!ofs_dats)
+ return IRQ_HANDLED; // exit=no new samples from last call
+ // obsluz data
+ i = devpriv->last_top_dma - 1;
+ i &= (devpriv->dmasamplsize - 1);
+
+ if (dmabuf[i] != MAGIC_DMA_WORD) { // DMA overflow!
+ comedi_error(dev, "A/D mode1/3 DMA buffer overflow!");
+ //rt_printk("I %d dmabuf[i] %d %d\n",i,dmabuf[i],devpriv->dmasamplsize);
+ pcl818_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+ }
+ //rt_printk("r %ld ",ofs_dats);
+
+ bufptr = devpriv->last_top_dma;
+
+ for (i = 0; i < ofs_dats; i++) {
+ if ((dmabuf[bufptr] & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { // dropout!
+ rt_printk
+ ("comedi: A/D mode1/3 DMA - channel dropout %d!=%d !\n",
+ (dmabuf[bufptr] & 0xf),
+ devpriv->act_chanlist[devpriv->
+ act_chanlist_pos]);
+ pcl818_ai_cancel(dev, s);
+ s->async->events |=
+ COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+ }
+
+ comedi_buf_put(s->async, dmabuf[bufptr++] >> 4); // get one sample
+ bufptr &= (devpriv->dmasamplsize - 1);
+
+ if (s->async->cur_chan == 0) {
+ devpriv->ai_act_scan--;
+ }
+
+ if (!devpriv->neverending_ai)
+ if (devpriv->ai_act_scan == 0) { /* all data sampled */
+ pcl818_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ //printk("done int ai13 dma\n");
+ return IRQ_HANDLED;
+ }
+ }
+
+ devpriv->last_top_dma = bufptr;
+ bufptr--;
+ bufptr &= (devpriv->dmasamplsize - 1);
+ dmabuf[bufptr] = MAGIC_DMA_WORD;
+ comedi_event(dev, s);
+ //outb(0,0x378);
+ return IRQ_HANDLED;
+ }
+
+ //outb(0,0x378);
+ return IRQ_HANDLED;
+}
+#endif
+
+/*
+==============================================================================
+ analog input interrupt mode 1 & 3, 818HD/HG cards
+*/
+static irqreturn_t interrupt_pcl818_ai_mode13_fifo(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->subdevices + 0;
+ int i, len, lo;
+
+ outb(0, dev->iobase + PCL818_FI_INTCLR); // clear fifo int request
+
+ lo = inb(dev->iobase + PCL818_FI_STATUS);
+
+ if (lo & 4) {
+ comedi_error(dev, "A/D mode1/3 FIFO overflow!");
+ pcl818_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+ }
+
+ if (lo & 1) {
+ comedi_error(dev, "A/D mode1/3 FIFO interrupt without data!");
+ pcl818_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+ }
+
+ if (lo & 2) {
+ len = 512;
+ } else {
+ len = 0;
+ }
+
+ for (i = 0; i < len; i++) {
+ lo = inb(dev->iobase + PCL818_FI_DATALO);
+ if ((lo & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { // dropout!
+ rt_printk
+ ("comedi: A/D mode1/3 FIFO - channel dropout %d!=%d !\n",
+ (lo & 0xf),
+ devpriv->act_chanlist[devpriv->
+ act_chanlist_pos]);
+ pcl818_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+ }
+
+ comedi_buf_put(s->async, (lo >> 4) | (inb(dev->iobase + PCL818_FI_DATAHI) << 4)); // get one sample
+
+ if (s->async->cur_chan == 0) {
+ devpriv->ai_act_scan--;
+ }
+
+ if (!devpriv->neverending_ai)
+ if (devpriv->ai_act_scan == 0) { /* all data sampled */
+ pcl818_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+ }
+ }
+
+ if (len > 0)
+ comedi_event(dev, s);
+ return IRQ_HANDLED;
+}
+
+/*
+==============================================================================
+ INT procedure
+*/
+static irqreturn_t interrupt_pcl818(int irq, void *d PT_REGS_ARG)
+{
+ struct comedi_device *dev = d;
+
+ if (!dev->attached) {
+ comedi_error(dev, "premature interrupt");
+ return IRQ_HANDLED;
+ }
+ //rt_printk("I\n");
+
+ switch (devpriv->ai_mode) {
+ case INT_TYPE_AI1_DMA:
+ case INT_TYPE_AI3_DMA:
+ return interrupt_pcl818_ai_mode13_dma(irq, d);
+ case INT_TYPE_AI1_INT:
+ case INT_TYPE_AI3_INT:
+ return interrupt_pcl818_ai_mode13_int(irq, d);
+ case INT_TYPE_AI1_FIFO:
+ case INT_TYPE_AI3_FIFO:
+ return interrupt_pcl818_ai_mode13_fifo(irq, d);
+#ifdef PCL818_MODE13_AO
+ case INT_TYPE_AO1_INT:
+ case INT_TYPE_AO3_INT:
+ return interrupt_pcl818_ao_mode13_int(irq, d);
+#endif
+ default:
+ break;
+ }
+
+ outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
+
+ if ((!dev->irq) || (!devpriv->irq_free) || (!devpriv->irq_blocked)
+ || (!devpriv->ai_mode)) {
+ if (devpriv->irq_was_now_closed) {
+ if (devpriv->neverending_ai &&
+ (devpriv->ai_mode == INT_TYPE_AI1_DMA
+ || devpriv->ai_mode ==
+ INT_TYPE_AI3_DMA)) {
+ /* we had neverending ai but ai_cancel() has been called
+ the cleanup from ai_cancel() has been delayed until know
+ because the card doesn't seem to like being reprogrammed
+ while a DMA transfer is in progress
+ */
+ struct comedi_subdevice *s = dev->subdevices + 0;
+ devpriv->ai_mode = devpriv->irq_was_now_closed;
+ devpriv->irq_was_now_closed = 0;
+ devpriv->neverending_ai = 0;
+ pcl818_ai_cancel(dev, s);
+ }
+ devpriv->irq_was_now_closed = 0;
+ return IRQ_HANDLED;
+ }
+ comedi_error(dev, "bad IRQ!");
+ return IRQ_NONE;
+ }
+
+ comedi_error(dev, "IRQ from unknow source!");
+ return IRQ_NONE;
+}
+
+/*
+==============================================================================
+ ANALOG INPUT MODE 1 or 3 DMA , 818 cards
+*/
+static void pcl818_ai_mode13dma_int(int mode, struct comedi_device * dev,
+ struct comedi_subdevice * s)
+{
+ unsigned int flags;
+ unsigned int bytes;
+
+ rt_printk("mode13dma_int, mode: %d\n", mode);
+ disable_dma(devpriv->dma); // disable dma
+ bytes = devpriv->hwdmasize[0];
+ if (!devpriv->neverending_ai) {
+ bytes = devpriv->ai_n_chan * devpriv->ai_scans * sizeof(short); // how many
+ devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; // how many DMA pages we must fiil
+ devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; //on last dma transfer must be moved
+ devpriv->dma_runs_to_end--;
+ if (devpriv->dma_runs_to_end >= 0)
+ bytes = devpriv->hwdmasize[0];
+ }
+
+ devpriv->next_dma_buf = 0;
+ set_dma_mode(devpriv->dma, DMA_MODE_READ);
+ flags = claim_dma_lock();
+ clear_dma_ff(devpriv->dma);
+ set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
+ set_dma_count(devpriv->dma, bytes);
+ release_dma_lock(flags);
+ enable_dma(devpriv->dma);
+
+ if (mode == 1) {
+ devpriv->ai_mode = INT_TYPE_AI1_DMA;
+ outb(0x87 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Pacer+IRQ+DMA */
+ } else {
+ devpriv->ai_mode = INT_TYPE_AI3_DMA;
+ outb(0x86 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Ext trig+IRQ+DMA */
+ };
+}
+
+#ifdef unused
+/*
+==============================================================================
+ ANALOG INPUT MODE 1 or 3 DMA rtc, 818 cards
+*/
+static void pcl818_ai_mode13dma_rtc(int mode, struct comedi_device * dev,
+ struct comedi_subdevice * s)
+{
+ unsigned int flags;
+ short *pole;
+
+ set_dma_mode(devpriv->dma, DMA_MODE_READ | DMA_AUTOINIT);
+ flags = claim_dma_lock();
+ clear_dma_ff(devpriv->dma);
+ set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
+ set_dma_count(devpriv->dma, devpriv->hwdmasize[0]);
+ release_dma_lock(flags);
+ enable_dma(devpriv->dma);
+ devpriv->last_top_dma = 0; //devpriv->hwdmasize[0];
+ pole = (short *) devpriv->dmabuf[0];
+ devpriv->dmasamplsize = devpriv->hwdmasize[0] / 2;
+ pole[devpriv->dmasamplsize - 1] = MAGIC_DMA_WORD;
+#ifdef unused
+ devpriv->rtc_freq = rtc_setfreq_irq(2048);
+ devpriv->rtc_irq_timer.expires =
+ jiffies + HZ / devpriv->rtc_freq + 2 * HZ / 100;
+ devpriv->rtc_irq_timer.data = (unsigned long)dev;
+ devpriv->rtc_irq_timer.function = rtc_dropped_irq;
+
+ add_timer(&devpriv->rtc_irq_timer);
+#endif
+
+ if (mode == 1) {
+ devpriv->int818_mode = INT_TYPE_AI1_DMA_RTC;
+ outb(0x07 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Pacer+DMA */
+ } else {
+ devpriv->int818_mode = INT_TYPE_AI3_DMA_RTC;
+ outb(0x06 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Ext trig+DMA */
+ };
+}
+#endif
+
+/*
+==============================================================================
+ ANALOG INPUT MODE 1 or 3, 818 cards
+*/
+static int pcl818_ai_cmd_mode(int mode, struct comedi_device * dev,
+ struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int divisor1, divisor2;
+ unsigned int seglen;
+
+ rt_printk("pcl818_ai_cmd_mode()\n");
+ if ((!dev->irq) && (!devpriv->dma_rtc)) {
+ comedi_error(dev, "IRQ not defined!");
+ return -EINVAL;
+ }
+
+ if (devpriv->irq_blocked)
+ return -EBUSY;
+
+ start_pacer(dev, -1, 0, 0); // stop pacer
+
+ seglen = check_channel_list(dev, s, devpriv->ai_chanlist,
+ devpriv->ai_n_chan);
+ if (seglen < 1)
+ return -EINVAL;
+ setup_channel_list(dev, s, devpriv->ai_chanlist,
+ devpriv->ai_n_chan, seglen);
+
+ comedi_udelay(1);
+
+ devpriv->ai_act_scan = devpriv->ai_scans;
+ devpriv->ai_act_chan = 0;
+ devpriv->irq_blocked = 1;
+ devpriv->irq_was_now_closed = 0;
+ devpriv->neverending_ai = 0;
+ devpriv->act_chanlist_pos = 0;
+ devpriv->dma_runs_to_end = 0;
+
+ if ((devpriv->ai_scans == 0) || (devpriv->ai_scans == -1))
+ devpriv->neverending_ai = 1; //well, user want neverending
+
+ if (mode == 1) {
+ i8253_cascade_ns_to_timer(devpriv->i8253_osc_base, &divisor1,
+ &divisor2, &cmd->convert_arg, TRIG_ROUND_NEAREST);
+ if (divisor1 == 1) { /* PCL718/818 crash if any divisor is set to 1 */
+ divisor1 = 2;
+ divisor2 /= 2;
+ }
+ if (divisor2 == 1) {
+ divisor2 = 2;
+ divisor1 /= 2;
+ }
+ }
+
+ outb(0, dev->iobase + PCL818_CNTENABLE); /* enable pacer */
+
+ switch (devpriv->dma) {
+ case 1: // DMA
+ case 3:
+ if (devpriv->dma_rtc == 0) {
+ pcl818_ai_mode13dma_int(mode, dev, s);
+ }
+#ifdef unused
+ else {
+ pcl818_ai_mode13dma_rtc(mode, dev, s);
+ }
+#else
+ else {
+ return -EINVAL;
+ }
+#endif
+ break;
+ case 0: // IRQ
+ // rt_printk("IRQ\n");
+ if (mode == 1) {
+ devpriv->ai_mode = INT_TYPE_AI1_INT;
+ outb(0x83 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Pacer+IRQ */
+ } else {
+ devpriv->ai_mode = INT_TYPE_AI3_INT;
+ outb(0x82 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Ext trig+IRQ */
+ };
+ break;
+ case -1: // FIFO
+ outb(1, dev->iobase + PCL818_FI_ENABLE); // enable FIFO
+ if (mode == 1) {
+ devpriv->ai_mode = INT_TYPE_AI1_FIFO;
+ outb(0x03, dev->iobase + PCL818_CONTROL); /* Pacer */
+ } else {
+ devpriv->ai_mode = INT_TYPE_AI3_FIFO;
+ outb(0x02, dev->iobase + PCL818_CONTROL);
+ }; /* Ext trig */
+ break;
+ }
+
+ start_pacer(dev, mode, divisor1, divisor2);
+
+#ifdef unused
+ switch (devpriv->ai_mode) {
+ case INT_TYPE_AI1_DMA_RTC:
+ case INT_TYPE_AI3_DMA_RTC:
+ set_rtc_irq_bit(1); /* start RTC */
+ break;
+ }
+#endif
+ rt_printk("pcl818_ai_cmd_mode() end\n");
+ return 0;
+}
+
+#ifdef unused
+/*
+==============================================================================
+ ANALOG OUTPUT MODE 1 or 3, 818 cards
+*/
+#ifdef PCL818_MODE13_AO
+static int pcl818_ao_mode13(int mode, struct comedi_device * dev, struct comedi_subdevice * s,
+ comedi_trig * it)
+{
+ int divisor1, divisor2;
+
+ if (!dev->irq) {
+ comedi_error(dev, "IRQ not defined!");
+ return -EINVAL;
+ }
+
+ if (devpriv->irq_blocked)
+ return -EBUSY;
+
+ start_pacer(dev, -1, 0, 0); // stop pacer
+
+ devpriv->int13_act_scan = it->n;
+ devpriv->int13_act_chan = 0;
+ devpriv->irq_blocked = 1;
+ devpriv->irq_was_now_closed = 0;
+ devpriv->neverending_ai = 0;
+ devpriv->act_chanlist_pos = 0;
+
+ if (mode == 1) {
+ i8253_cascade_ns_to_timer(devpriv->i8253_osc_base, &divisor1,
+ &divisor2, &it->trigvar, TRIG_ROUND_NEAREST);
+ if (divisor1 == 1) { /* PCL818 crash if any divisor is set to 1 */
+ divisor1 = 2;
+ divisor2 /= 2;
+ }
+ if (divisor2 == 1) {
+ divisor2 = 2;
+ divisor1 /= 2;
+ }
+ }
+
+ outb(0, dev->iobase + PCL818_CNTENABLE); /* enable pacer */
+ if (mode == 1) {
+ devpriv->int818_mode = INT_TYPE_AO1_INT;
+ outb(0x83 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Pacer+IRQ */
+ } else {
+ devpriv->int818_mode = INT_TYPE_AO3_INT;
+ outb(0x82 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Ext trig+IRQ */
+ };
+
+ start_pacer(dev, mode, divisor1, divisor2);
+
+ return 0;
+}
+
+/*
+==============================================================================
+ ANALOG OUTPUT MODE 1, 818 cards
+*/
+static int pcl818_ao_mode1(struct comedi_device * dev, struct comedi_subdevice * s,
+ comedi_trig * it)
+{
+ return pcl818_ao_mode13(1, dev, s, it);
+}
+
+/*
+==============================================================================
+ ANALOG OUTPUT MODE 3, 818 cards
+*/
+static int pcl818_ao_mode3(struct comedi_device * dev, struct comedi_subdevice * s,
+ comedi_trig * it)
+{
+ return pcl818_ao_mode13(3, dev, s, it);
+}
+#endif
+#endif
+
+/*
+==============================================================================
+ Start/stop pacer onboard pacer
+*/
+static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
+ unsigned int divisor2)
+{
+ outb(0xb4, dev->iobase + PCL818_CTRCTL);
+ outb(0x74, dev->iobase + PCL818_CTRCTL);
+ comedi_udelay(1);
+
+ if (mode == 1) {
+ outb(divisor2 & 0xff, dev->iobase + PCL818_CTR2);
+ outb((divisor2 >> 8) & 0xff, dev->iobase + PCL818_CTR2);
+ outb(divisor1 & 0xff, dev->iobase + PCL818_CTR1);
+ outb((divisor1 >> 8) & 0xff, dev->iobase + PCL818_CTR1);
+ }
+}
+
+/*
+==============================================================================
+ Check if channel list from user is builded correctly
+ If it's ok, then program scan/gain logic
+*/
+static int check_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int *chanlist, unsigned int n_chan)
+{
+ unsigned int chansegment[16];
+ unsigned int i, nowmustbechan, seglen, segpos;
+
+ /* correct channel and range number check itself comedi/range.c */
+ if (n_chan < 1) {
+ comedi_error(dev, "range/channel list is empty!");
+ return 0;
+ }
+
+ if (n_chan > 1) {
+ // first channel is everytime ok
+ chansegment[0] = chanlist[0];
+ // build part of chanlist
+ for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
+ // rt_printk("%d. %d %d\n",i,CR_CHAN(it->chanlist[i]),CR_RANGE(it->chanlist[i]));
+ // we detect loop, this must by finish
+ if (chanlist[0] == chanlist[i])
+ break;
+ nowmustbechan =
+ (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
+ if (nowmustbechan != CR_CHAN(chanlist[i])) { // channel list isn't continous :-(
+ rt_printk
+ ("comedi%d: pcl818: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
+ dev->minor, i, CR_CHAN(chanlist[i]),
+ nowmustbechan, CR_CHAN(chanlist[0]));
+ return 0;
+ }
+ // well, this is next correct channel in list
+ chansegment[i] = chanlist[i];
+ }
+
+ // check whole chanlist
+ for (i = 0, segpos = 0; i < n_chan; i++) {
+ //rt_printk("%d %d=%d %d\n",CR_CHAN(chansegment[i%seglen]),CR_RANGE(chansegment[i%seglen]),CR_CHAN(it->chanlist[i]),CR_RANGE(it->chanlist[i]));
+ if (chanlist[i] != chansegment[i % seglen]) {
+ rt_printk
+ ("comedi%d: pcl818: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
+ dev->minor, i, CR_CHAN(chansegment[i]),
+ CR_RANGE(chansegment[i]),
+ CR_AREF(chansegment[i]),
+ CR_CHAN(chanlist[i % seglen]),
+ CR_RANGE(chanlist[i % seglen]),
+ CR_AREF(chansegment[i % seglen]));
+ return 0; // chan/gain list is strange
+ }
+ }
+ } else {
+ seglen = 1;
+ }
+ rt_printk("check_channel_list: seglen %d\n", seglen);
+ return seglen;
+}
+
+static void setup_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int *chanlist, unsigned int n_chan, unsigned int seglen)
+{
+ int i;
+
+ devpriv->act_chanlist_len = seglen;
+ devpriv->act_chanlist_pos = 0;
+
+ for (i = 0; i < seglen; i++) { // store range list to card
+ devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
+ outb(muxonechan[CR_CHAN(chanlist[i])], dev->iobase + PCL818_MUX); /* select channel */
+ outb(CR_RANGE(chanlist[i]), dev->iobase + PCL818_RANGE); /* select gain */
+ }
+
+ comedi_udelay(1);
+
+ /* select channel interval to scan */
+ outb(devpriv->act_chanlist[0] | (devpriv->act_chanlist[seglen -
+ 1] << 4), dev->iobase + PCL818_MUX);
+}
+
+/*
+==============================================================================
+ Check if board is switched to SE (1) or DIFF(0) mode
+*/
+static int check_single_ended(unsigned int port)
+{
+ if (inb(port + PCL818_STATUS) & 0x20) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/*
+==============================================================================
+*/
+static int ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp, divisor1, divisor2;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_FOLLOW;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err) {
+ return 1;
+ }
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if (cmd->start_src != TRIG_NOW) {
+ cmd->start_src = TRIG_NOW;
+ err++;
+ }
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ cmd->scan_begin_src = TRIG_FOLLOW;
+ err++;
+ }
+ if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
+ err++;
+
+ if (cmd->scan_end_src != TRIG_COUNT) {
+ cmd->scan_end_src = TRIG_COUNT;
+ err++;
+ }
+
+ if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
+ err++;
+
+ if (err) {
+ return 2;
+ }
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < this_board->ns_min) {
+ cmd->convert_arg = this_board->ns_min;
+ err++;
+ }
+ } else { /* TRIG_EXT */
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+ }
+
+ if (!cmd->chanlist_len) {
+ cmd->chanlist_len = 1;
+ err++;
+ }
+ if (cmd->chanlist_len > s->n_chan) {
+ cmd->chanlist_len = s->n_chan;
+ err++;
+ }
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (!cmd->stop_arg) {
+ cmd->stop_arg = 1;
+ err++;
+ }
+ } else { /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err) {
+ return 3;
+ }
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ i8253_cascade_ns_to_timer(devpriv->i8253_osc_base, &divisor1,
+ &divisor2, &cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (cmd->convert_arg < this_board->ns_min)
+ cmd->convert_arg = this_board->ns_min;
+ if (tmp != cmd->convert_arg)
+ err++;
+ }
+
+ if (err) {
+ return 4;
+ }
+
+ /* step 5: complain about special chanlist considerations */
+
+ if (cmd->chanlist) {
+ if (!check_channel_list(dev, s, cmd->chanlist,
+ cmd->chanlist_len))
+ return 5; // incorrect channels list
+ }
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int retval;
+
+ rt_printk("pcl818_ai_cmd()\n");
+ devpriv->ai_n_chan = cmd->chanlist_len;
+ devpriv->ai_chanlist = cmd->chanlist;
+ devpriv->ai_flags = cmd->flags;
+ devpriv->ai_data_len = s->async->prealloc_bufsz;
+ devpriv->ai_data = s->async->prealloc_buf;
+ devpriv->ai_timer1 = 0;
+ devpriv->ai_timer2 = 0;
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ devpriv->ai_scans = cmd->stop_arg;
+ } else {
+ devpriv->ai_scans = 0;
+ }
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) { // mode 1, 3
+ if (cmd->convert_src == TRIG_TIMER) { // mode 1
+ devpriv->ai_timer1 = cmd->convert_arg;
+ retval = pcl818_ai_cmd_mode(1, dev, s);
+ rt_printk("pcl818_ai_cmd() end\n");
+ return retval;
+ }
+ if (cmd->convert_src == TRIG_EXT) { // mode 3
+ return pcl818_ai_cmd_mode(3, dev, s);
+ }
+ }
+
+ return -1;
+}
+
+/*
+==============================================================================
+ cancel any mode 1-4 AI
+*/
+static int pcl818_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ if (devpriv->irq_blocked > 0) {
+ rt_printk("pcl818_ai_cancel()\n");
+ devpriv->irq_was_now_closed = devpriv->ai_mode;
+ devpriv->ai_mode = 0;
+
+ switch (devpriv->irq_was_now_closed) {
+#ifdef unused
+ case INT_TYPE_AI1_DMA_RTC:
+ case INT_TYPE_AI3_DMA_RTC:
+ set_rtc_irq_bit(0); // stop RTC
+ del_timer(&devpriv->rtc_irq_timer);
+#endif
+ case INT_TYPE_AI1_DMA:
+ case INT_TYPE_AI3_DMA:
+ if (devpriv->neverending_ai) {
+ /* wait for running dma transfer to end, do cleanup in interrupt */
+ goto end;
+ }
+ disable_dma(devpriv->dma);
+ case INT_TYPE_AI1_INT:
+ case INT_TYPE_AI3_INT:
+ case INT_TYPE_AI1_FIFO:
+ case INT_TYPE_AI3_FIFO:
+#ifdef PCL818_MODE13_AO
+ case INT_TYPE_AO1_INT:
+ case INT_TYPE_AO3_INT:
+#endif
+ outb(inb(dev->iobase + PCL818_CONTROL) & 0x73, dev->iobase + PCL818_CONTROL); /* Stop A/D */
+ comedi_udelay(1);
+ start_pacer(dev, -1, 0, 0);
+ outb(0, dev->iobase + PCL818_AD_LO);
+ inb(dev->iobase + PCL818_AD_LO);
+ inb(dev->iobase + PCL818_AD_HI);
+ outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
+ outb(0, dev->iobase + PCL818_CONTROL); /* Stop A/D */
+ if (devpriv->usefifo) { // FIFO shutdown
+ outb(0, dev->iobase + PCL818_FI_INTCLR);
+ outb(0, dev->iobase + PCL818_FI_FLUSH);
+ outb(0, dev->iobase + PCL818_FI_ENABLE);
+ }
+ devpriv->irq_blocked = 0;
+ devpriv->last_int_sub = s;
+ devpriv->neverending_ai = 0;
+ break;
+ }
+ }
+
+ end:
+ rt_printk("pcl818_ai_cancel() end\n");
+ return 0;
+}
+
+/*
+==============================================================================
+ chech for PCL818
+*/
+static int pcl818_check(unsigned long iobase)
+{
+ outb(0x00, iobase + PCL818_MUX);
+ comedi_udelay(1);
+ if (inb(iobase + PCL818_MUX) != 0x00)
+ return 1; //there isn't card
+ outb(0x55, iobase + PCL818_MUX);
+ comedi_udelay(1);
+ if (inb(iobase + PCL818_MUX) != 0x55)
+ return 1; //there isn't card
+ outb(0x00, iobase + PCL818_MUX);
+ comedi_udelay(1);
+ outb(0x18, iobase + PCL818_CONTROL);
+ comedi_udelay(1);
+ if (inb(iobase + PCL818_CONTROL) != 0x18)
+ return 1; //there isn't card
+ return 0; // ok, card exist
+}
+
+/*
+==============================================================================
+ reset whole PCL-818 cards
+*/
+static void pcl818_reset(struct comedi_device * dev)
+{
+ if (devpriv->usefifo) { // FIFO shutdown
+ outb(0, dev->iobase + PCL818_FI_INTCLR);
+ outb(0, dev->iobase + PCL818_FI_FLUSH);
+ outb(0, dev->iobase + PCL818_FI_ENABLE);
+ }
+ outb(0, dev->iobase + PCL818_DA_LO); // DAC=0V
+ outb(0, dev->iobase + PCL818_DA_HI);
+ comedi_udelay(1);
+ outb(0, dev->iobase + PCL818_DO_HI); // DO=$0000
+ outb(0, dev->iobase + PCL818_DO_LO);
+ comedi_udelay(1);
+ outb(0, dev->iobase + PCL818_CONTROL);
+ outb(0, dev->iobase + PCL818_CNTENABLE);
+ outb(0, dev->iobase + PCL818_MUX);
+ outb(0, dev->iobase + PCL818_CLRINT);
+ outb(0xb0, dev->iobase + PCL818_CTRCTL); /* Stop pacer */
+ outb(0x70, dev->iobase + PCL818_CTRCTL);
+ outb(0x30, dev->iobase + PCL818_CTRCTL);
+ if (this_board->is_818) {
+ outb(0, dev->iobase + PCL818_RANGE);
+ } else {
+ outb(0, dev->iobase + PCL718_DA2_LO);
+ outb(0, dev->iobase + PCL718_DA2_HI);
+ }
+}
+
+#ifdef unused
+/*
+==============================================================================
+ Enable(1)/disable(0) periodic interrupts from RTC
+*/
+static int set_rtc_irq_bit(unsigned char bit)
+{
+ unsigned char val;
+ unsigned long flags;
+
+ if (bit == 1) {
+ RTC_timer_lock++;
+ if (RTC_timer_lock > 1)
+ return 0;
+ } else {
+ RTC_timer_lock--;
+ if (RTC_timer_lock < 0)
+ RTC_timer_lock = 0;
+ if (RTC_timer_lock > 0)
+ return 0;
+ }
+
+ save_flags(flags);
+ cli();
+ val = CMOS_READ(RTC_CONTROL);
+ if (bit) {
+ val |= RTC_PIE;
+ } else {
+ val &= ~RTC_PIE;
+ }
+ CMOS_WRITE(val, RTC_CONTROL);
+ CMOS_READ(RTC_INTR_FLAGS);
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+==============================================================================
+ Restart RTC if something stop it (xntpd every 11 mins or large IDE transfers)
+*/
+static void rtc_dropped_irq(unsigned long data)
+{
+ struct comedi_device *dev = (void *)data;
+ unsigned long flags, tmp;
+
+ switch (devpriv->int818_mode) {
+ case INT_TYPE_AI1_DMA_RTC:
+ case INT_TYPE_AI3_DMA_RTC:
+ mod_timer(&devpriv->rtc_irq_timer,
+ jiffies + HZ / devpriv->rtc_freq + 2 * HZ / 100);
+ save_flags(flags);
+ cli();
+ tmp = (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); /* restart */
+ restore_flags(flags);
+ break;
+ };
+}
+
+/*
+==============================================================================
+ Set frequency of interrupts from RTC
+*/
+static int rtc_setfreq_irq(int freq)
+{
+ int tmp = 0;
+ int rtc_freq;
+ unsigned char val;
+ unsigned long flags;
+
+ if (freq < 2)
+ freq = 2;
+ if (freq > 8192)
+ freq = 8192;
+
+ while (freq > (1 << tmp))
+ tmp++;
+
+ rtc_freq = 1 << tmp;
+
+ save_flags(flags);
+ cli();
+ val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
+ val |= (16 - tmp);
+ CMOS_WRITE(val, RTC_FREQ_SELECT);
+ restore_flags(flags);
+ return rtc_freq;
+}
+#endif
+
+/*
+==============================================================================
+ Free any resources that we have claimed
+*/
+static void free_resources(struct comedi_device * dev)
+{
+ //rt_printk("free_resource()\n");
+ if (dev->private) {
+ pcl818_ai_cancel(dev, devpriv->sub_ai);
+ pcl818_reset(dev);
+ if (devpriv->dma)
+ free_dma(devpriv->dma);
+ if (devpriv->dmabuf[0])
+ free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
+ if (devpriv->dmabuf[1])
+ free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
+#ifdef unused
+ if (devpriv->rtc_irq)
+ comedi_free_irq(devpriv->rtc_irq, dev);
+ if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
+ if (devpriv->rtc_iobase)
+ release_region(devpriv->rtc_iobase,
+ devpriv->rtc_iosize);
+ }
+ if (devpriv->dma_rtc)
+ RTC_lock--;
+#endif
+ }
+
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ if (dev->iobase)
+ release_region(dev->iobase, devpriv->io_range);
+ //rt_printk("free_resource() end\n");
+}
+
+/*
+==============================================================================
+
+ Initialization
+
+*/
+static int pcl818_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int ret;
+ unsigned long iobase;
+ unsigned int irq, dma;
+ unsigned long pages;
+ struct comedi_subdevice *s;
+
+ if ((ret = alloc_private(dev, sizeof(struct pcl818_private))) < 0)
+ return ret; /* Can't alloc mem */
+
+ /* claim our I/O space */
+ iobase = it->options[0];
+ printk("comedi%d: pcl818: board=%s, ioport=0x%03lx",
+ dev->minor, this_board->name, iobase);
+ devpriv->io_range = this_board->io_range;
+ if ((this_board->fifo) && (it->options[2] == -1)) { // we've board with FIFO and we want to use FIFO
+ devpriv->io_range = PCLx1xFIFO_RANGE;
+ devpriv->usefifo = 1;
+ }
+ if (!request_region(iobase, devpriv->io_range, "pcl818")) {
+ rt_printk("I/O port conflict\n");
+ return -EIO;
+ }
+
+ dev->iobase = iobase;
+
+ if (pcl818_check(iobase)) {
+ rt_printk(", I can't detect board. FAIL!\n");
+ return -EIO;
+ }
+
+ /* set up some name stuff */
+ dev->board_name = this_board->name;
+ /* grab our IRQ */
+ irq = 0;
+ if (this_board->IRQbits != 0) { /* board support IRQ */
+ irq = it->options[1];
+ if (irq) { /* we want to use IRQ */
+ if (((1 << irq) & this_board->IRQbits) == 0) {
+ rt_printk
+ (", IRQ %u is out of allowed range, DISABLING IT",
+ irq);
+ irq = 0; /* Bad IRQ */
+ } else {
+ if (comedi_request_irq(irq, interrupt_pcl818, 0,
+ "pcl818", dev)) {
+ rt_printk
+ (", unable to allocate IRQ %u, DISABLING IT",
+ irq);
+ irq = 0; /* Can't use IRQ */
+ } else {
+ rt_printk(", irq=%u", irq);
+ }
+ }
+ }
+ }
+
+ dev->irq = irq;
+ if (irq) {
+ devpriv->irq_free = 1;
+ } /* 1=we have allocated irq */
+ else {
+ devpriv->irq_free = 0;
+ }
+ devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
+ devpriv->ai_mode = 0; /* mode of irq */
+
+#ifdef unused
+ /* grab RTC for DMA operations */
+ devpriv->dma_rtc = 0;
+ if (it->options[2] > 0) { // we want to use DMA
+ if (RTC_lock == 0) {
+ if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
+ "pcl818 (RTC)"))
+ goto no_rtc;
+ }
+ devpriv->rtc_iobase = RTC_PORT(0);
+ devpriv->rtc_iosize = RTC_IO_EXTENT;
+ RTC_lock++;
+ if (!comedi_request_irq(RTC_IRQ,
+ interrupt_pcl818_ai_mode13_dma_rtc, 0,
+ "pcl818 DMA (RTC)", dev)) {
+ devpriv->dma_rtc = 1;
+ devpriv->rtc_irq = RTC_IRQ;
+ rt_printk(", dma_irq=%u", devpriv->rtc_irq);
+ } else {
+ RTC_lock--;
+ if (RTC_lock == 0) {
+ if (devpriv->rtc_iobase)
+ release_region(devpriv->rtc_iobase,
+ devpriv->rtc_iosize);
+ }
+ devpriv->rtc_iobase = 0;
+ devpriv->rtc_iosize = 0;
+ }
+ }
+
+ no_rtc:
+#endif
+ /* grab our DMA */
+ dma = 0;
+ devpriv->dma = dma;
+ if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
+ goto no_dma; /* if we haven't IRQ, we can't use DMA */
+ if (this_board->DMAbits != 0) { /* board support DMA */
+ dma = it->options[2];
+ if (dma < 1)
+ goto no_dma; /* DMA disabled */
+ if (((1 << dma) & this_board->DMAbits) == 0) {
+ rt_printk(", DMA is out of allowed range, FAIL!\n");
+ return -EINVAL; /* Bad DMA */
+ }
+ ret = request_dma(dma, "pcl818");
+ if (ret) {
+ rt_printk(", unable to allocate DMA %u, FAIL!\n", dma);
+ return -EBUSY; /* DMA isn't free */
+ }
+ devpriv->dma = dma;
+ rt_printk(", dma=%u", dma);
+ pages = 2; /* we need 16KB */
+ devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
+ if (!devpriv->dmabuf[0]) {
+ rt_printk(", unable to allocate DMA buffer, FAIL!\n");
+ /* maybe experiment with try_to_free_pages() will help .... */
+ return -EBUSY; /* no buffer :-( */
+ }
+ devpriv->dmapages[0] = pages;
+ devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
+ devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
+ //rt_printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE);
+ if (devpriv->dma_rtc == 0) { // we must do duble buff :-(
+ devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
+ if (!devpriv->dmabuf[1]) {
+ rt_printk
+ (", unable to allocate DMA buffer, FAIL!\n");
+ return -EBUSY;
+ }
+ devpriv->dmapages[1] = pages;
+ devpriv->hwdmaptr[1] =
+ virt_to_bus((void *)devpriv->dmabuf[1]);
+ devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
+ }
+ }
+
+ no_dma:
+
+ if ((ret = alloc_subdevices(dev, 4)) < 0)
+ return ret;
+
+ s = dev->subdevices + 0;
+ if (!this_board->n_aichan_se) {
+ s->type = COMEDI_SUBD_UNUSED;
+ } else {
+ s->type = COMEDI_SUBD_AI;
+ devpriv->sub_ai = s;
+ s->subdev_flags = SDF_READABLE;
+ if (check_single_ended(dev->iobase)) {
+ s->n_chan = this_board->n_aichan_se;
+ s->subdev_flags |= SDF_COMMON | SDF_GROUND;
+ printk(", %dchans S.E. DAC", s->n_chan);
+ } else {
+ s->n_chan = this_board->n_aichan_diff;
+ s->subdev_flags |= SDF_DIFF;
+ printk(", %dchans DIFF DAC", s->n_chan);
+ }
+ s->maxdata = this_board->ai_maxdata;
+ s->len_chanlist = s->n_chan;
+ s->range_table = this_board->ai_range_type;
+ s->cancel = pcl818_ai_cancel;
+ s->insn_read = pcl818_ai_insn_read;
+ if ((irq) || (devpriv->dma_rtc)) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->do_cmdtest = ai_cmdtest;
+ s->do_cmd = ai_cmd;
+ }
+ if (this_board->is_818) {
+ if ((it->options[4] == 1) || (it->options[4] == 10))
+ s->range_table = &range_pcl818l_h_ai; // secondary range list jumper selectable
+ } else {
+ switch (it->options[4]) {
+ case 0:
+ s->range_table = &range_bipolar10;
+ break;
+ case 1:
+ s->range_table = &range_bipolar5;
+ break;
+ case 2:
+ s->range_table = &range_bipolar2_5;
+ break;
+ case 3:
+ s->range_table = &range718_bipolar1;
+ break;
+ case 4:
+ s->range_table = &range718_bipolar0_5;
+ break;
+ case 6:
+ s->range_table = &range_unipolar10;
+ break;
+ case 7:
+ s->range_table = &range_unipolar5;
+ break;
+ case 8:
+ s->range_table = &range718_unipolar2;
+ break;
+ case 9:
+ s->range_table = &range718_unipolar1;
+ break;
+ default:
+ s->range_table = &range_unknown;
+ break;
+ }
+ }
+ }
+
+ s = dev->subdevices + 1;
+ if (!this_board->n_aochan) {
+ s->type = COMEDI_SUBD_UNUSED;
+ } else {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = this_board->n_aochan;
+ s->maxdata = this_board->ao_maxdata;
+ s->len_chanlist = this_board->n_aochan;
+ s->range_table = this_board->ao_range_type;
+ s->insn_read = pcl818_ao_insn_read;
+ s->insn_write = pcl818_ao_insn_write;
+#ifdef unused
+#ifdef PCL818_MODE13_AO
+ if (irq) {
+ s->trig[1] = pcl818_ao_mode1;
+ s->trig[3] = pcl818_ao_mode3;
+ }
+#endif
+#endif
+ if (this_board->is_818) {
+ if ((it->options[4] == 1) || (it->options[4] == 10))
+ s->range_table = &range_unipolar10;
+ if (it->options[4] == 2)
+ s->range_table = &range_unknown;
+ } else {
+ if ((it->options[5] == 1) || (it->options[5] == 10))
+ s->range_table = &range_unipolar10;
+ if (it->options[5] == 2)
+ s->range_table = &range_unknown;
+ }
+ }
+
+ s = dev->subdevices + 2;
+ if (!this_board->n_dichan) {
+ s->type = COMEDI_SUBD_UNUSED;
+ } else {
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = this_board->n_dichan;
+ s->maxdata = 1;
+ s->len_chanlist = this_board->n_dichan;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl818_di_insn_bits;
+ }
+
+ s = dev->subdevices + 3;
+ if (!this_board->n_dochan) {
+ s->type = COMEDI_SUBD_UNUSED;
+ } else {
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = this_board->n_dochan;
+ s->maxdata = 1;
+ s->len_chanlist = this_board->n_dochan;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl818_do_insn_bits;
+ }
+
+ /* select 1/10MHz oscilator */
+ if ((it->options[3] == 0) || (it->options[3] == 10)) {
+ devpriv->i8253_osc_base = 100;
+ } else {
+ devpriv->i8253_osc_base = 1000;
+ }
+
+ /* max sampling speed */
+ devpriv->ns_min = this_board->ns_min;
+
+ if (!this_board->is_818) {
+ if ((it->options[6] == 1) || (it->options[6] == 100))
+ devpriv->ns_min = 10000; /* extended PCL718 to 100kHz DAC */
+ }
+
+ pcl818_reset(dev);
+
+ rt_printk("\n");
+
+ return 0;
+}
+
+/*
+==============================================================================
+ Removes device
+ */
+static int pcl818_detach(struct comedi_device * dev)
+{
+ // rt_printk("comedi%d: pcl818: remove\n", dev->minor);
+ free_resources(dev);
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/pcm3724.c b/drivers/staging/comedi/drivers/pcm3724.c
new file mode 100644
index 000000000000..a3ed3a0e855a
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcm3724.c
@@ -0,0 +1,307 @@
+/*
+ comedi/drivers/pcm724.c
+
+ Drew Csillag <drew_csillag@yahoo.com>
+
+ hardware driver for Advantech card:
+ card: PCM-3724
+ driver: pcm3724
+
+ Options for PCM-3724
+ [0] - IO Base
+*/
+/*
+Driver: pcm3724
+Description: Advantech PCM-3724
+Author: Drew Csillag <drew_csillag@yahoo.com>
+Devices: [Advantech] PCM-3724 (pcm724)
+Status: tested
+
+This is driver for digital I/O boards PCM-3724 with 48 DIO.
+It needs 8255.o for operations and only immediate mode is supported.
+See the source for configuration details.
+
+Copy/pasted/hacked from pcm724.c
+*/
+/*
+ * check_driver overrides:
+ * struct comedi_insn
+ */
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+
+#include "8255.h"
+
+#define PCM3724_SIZE 16
+#define SIZE_8255 4
+
+#define BUF_C0 0x1
+#define BUF_B0 0x2
+#define BUF_A0 0x4
+#define BUF_C1 0x8
+#define BUF_B1 0x10
+#define BUF_A1 0x20
+
+#define GATE_A0 0x4
+#define GATE_B0 0x2
+#define GATE_C0 0x1
+#define GATE_A1 0x20
+#define GATE_B1 0x10
+#define GATE_C1 0x8
+
+/* from 8255.c */
+#define CR_CW 0x80
+#define _8255_CR 3
+#define CR_B_IO 0x02
+#define CR_B_MODE 0x04
+#define CR_C_IO 0x09
+#define CR_A_IO 0x10
+#define CR_A_MODE(a) ((a)<<5)
+#define CR_CW 0x80
+
+static int pcm3724_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcm3724_detach(struct comedi_device * dev);
+
+struct pcm3724_board {
+ const char *name; // driver name
+ int dio; // num of DIO
+ int numofports; // num of 8255 subdevices
+ unsigned int IRQbits; // allowed interrupts
+ unsigned int io_range; // len of IO space
+};
+
+//used to track configured dios
+struct priv_pcm3724 {
+ int dio_1;
+ int dio_2;
+};
+
+static const struct pcm3724_board boardtypes[] = {
+ {"pcm3724", 48, 2, 0x00fc, PCM3724_SIZE,},
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcm3724_board))
+#define this_board ((const struct pcm3724_board *)dev->board_ptr)
+
+static struct comedi_driver driver_pcm3724 = {
+ driver_name:"pcm3724",
+ module:THIS_MODULE,
+ attach:pcm3724_attach,
+ detach:pcm3724_detach,
+ board_name:&boardtypes[0].name,
+ num_names:n_boardtypes,
+ offset:sizeof(struct pcm3724_board),
+};
+
+COMEDI_INITCLEANUP(driver_pcm3724);
+
+// (setq c-basic-offset 8)
+
+static int subdev_8255_cb(int dir, int port, int data, unsigned long arg)
+{
+ unsigned long iobase = arg;
+ unsigned char inbres;
+ //printk("8255cb %d %d %d %lx\n", dir,port,data,arg);
+ if (dir) {
+ //printk("8255 cb outb(%x, %lx)\n", data, iobase+port);
+ outb(data, iobase + port);
+ return 0;
+ } else {
+ inbres = inb(iobase + port);
+ //printk("8255 cb inb(%lx) = %x\n", iobase+port, inbres);
+ return inbres;
+ }
+}
+
+static int compute_buffer(int config, int devno, struct comedi_subdevice * s)
+{
+ /* 1 in io_bits indicates output */
+ if (s->io_bits & 0x0000ff) {
+ if (devno == 0) {
+ config |= BUF_A0;
+ } else {
+ config |= BUF_A1;
+ }
+ }
+ if (s->io_bits & 0x00ff00) {
+ if (devno == 0) {
+ config |= BUF_B0;
+ } else {
+ config |= BUF_B1;
+ }
+ }
+ if (s->io_bits & 0xff0000) {
+ if (devno == 0) {
+ config |= BUF_C0;
+ } else {
+ config |= BUF_C1;
+ }
+ }
+ return config;
+}
+
+static void do_3724_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ int chanspec)
+{
+ int config;
+ int buffer_config;
+ unsigned long port_8255_cfg;
+
+ config = CR_CW;
+ buffer_config = 0;
+
+ /* 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 & 0xff0000)) {
+ config |= CR_C_IO;
+ }
+
+ buffer_config = compute_buffer(0, 0, dev->subdevices);
+ buffer_config = compute_buffer(buffer_config, 1, (dev->subdevices) + 1);
+
+ if (s == dev->subdevices) {
+ port_8255_cfg = dev->iobase + _8255_CR;
+ } else {
+ port_8255_cfg = dev->iobase + SIZE_8255 + _8255_CR;
+ }
+ outb(buffer_config, dev->iobase + 8); /* update buffer register */
+ //printk("pcm3724 buffer_config (%lx) %d, %x\n", dev->iobase + _8255_CR, chanspec, buffer_config);
+ outb(config, port_8255_cfg);
+}
+
+static void enable_chan(struct comedi_device * dev, struct comedi_subdevice * s, int chanspec)
+{
+ unsigned int mask;
+ int gatecfg;
+ struct priv_pcm3724 *priv;
+
+ gatecfg = 0;
+ priv = (struct priv_pcm3724 *) (dev->private);
+
+ mask = 1 << CR_CHAN(chanspec);
+ if (s == dev->subdevices) { // subdev 0
+ priv->dio_1 |= mask;
+ } else { //subdev 1
+ priv->dio_2 |= mask;
+ }
+ if (priv->dio_1 & 0xff0000) {
+ gatecfg |= GATE_C0;
+ }
+ if (priv->dio_1 & 0xff00) {
+ gatecfg |= GATE_B0;
+ }
+ if (priv->dio_1 & 0xff) {
+ gatecfg |= GATE_A0;
+ }
+ if (priv->dio_2 & 0xff0000) {
+ gatecfg |= GATE_C1;
+ }
+ if (priv->dio_2 & 0xff00) {
+ gatecfg |= GATE_B1;
+ }
+ if (priv->dio_2 & 0xff) {
+ gatecfg |= GATE_A1;
+ }
+ // printk("gate control %x\n", gatecfg);
+ outb(gatecfg, dev->iobase + 9);
+}
+
+/* overriding the 8255 insn config */
+static int subdev_3724_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ unsigned int mask;
+ unsigned int bits;
+
+ mask = 1 << CR_CHAN(insn->chanspec);
+ if (mask & 0x0000ff) {
+ bits = 0x0000ff;
+ } else if (mask & 0x00ff00) {
+ bits = 0x00ff00;
+ } else if (mask & 0x0f0000) {
+ bits = 0x0f0000;
+ } else {
+ bits = 0xf00000;
+ }
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~bits;
+ break;
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= bits;
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ do_3724_config(dev, s, insn->chanspec);
+ enable_chan(dev, s, insn->chanspec);
+ return 1;
+}
+
+static int pcm3724_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ unsigned long iobase;
+ unsigned int iorange;
+ int ret, i, n_subdevices;
+
+ iobase = it->options[0];
+ iorange = this_board->io_range;
+ if ((ret = alloc_private(dev, sizeof(struct priv_pcm3724))) < 0)
+ return -ENOMEM;
+
+ ((struct priv_pcm3724 *) (dev->private))->dio_1 = 0;
+ ((struct priv_pcm3724 *) (dev->private))->dio_2 = 0;
+
+ printk("comedi%d: pcm3724: board=%s, 0x%03lx ", dev->minor,
+ this_board->name, iobase);
+ if (!iobase || !request_region(iobase, iorange, "pcm3724")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+
+ dev->iobase = iobase;
+ dev->board_name = this_board->name;
+ printk("\n");
+
+ n_subdevices = this_board->numofports;
+
+ if ((ret = alloc_subdevices(dev, n_subdevices)) < 0)
+ return ret;
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ subdev_8255_init(dev, dev->subdevices + i, subdev_8255_cb,
+ (unsigned long)(dev->iobase + SIZE_8255 * i));
+ ((dev->subdevices) + i)->insn_config = subdev_3724_insn_config;
+ };
+ return 0;
+}
+
+static int pcm3724_detach(struct comedi_device * dev)
+{
+ int i;
+
+ if (dev->subdevices) {
+ for (i = 0; i < dev->n_subdevices; i++) {
+ subdev_8255_cleanup(dev, dev->subdevices + i);
+ }
+ }
+ if (dev->iobase) {
+ release_region(dev->iobase, this_board->io_range);
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/pcm3730.c b/drivers/staging/comedi/drivers/pcm3730.c
new file mode 100644
index 000000000000..1de555fe645c
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcm3730.c
@@ -0,0 +1,152 @@
+/*
+ * comedi/drivers/pcm3730.c
+ * Driver for PCM3730 and clones
+ * Blaine Lee
+ * from pcl725 by David S.
+ */
+/*
+Driver: pcm3730
+Description: PCM3730
+Author: Blaine Lee
+Devices: [Advantech] PCM-3730 (pcm3730)
+Status: unknown
+
+Configuration options:
+ [0] - I/O port base
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+#define PCM3730_SIZE 4 // consecutive io port addresses
+
+#define PCM3730_DOA 0 // offsets for each port
+#define PCM3730_DOB 2
+#define PCM3730_DOC 3
+#define PCM3730_DIA 0
+#define PCM3730_DIB 2
+#define PCM3730_DIC 3
+
+static int pcm3730_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcm3730_detach(struct comedi_device * dev);
+static struct comedi_driver driver_pcm3730 = {
+ driver_name:"pcm3730",
+ module:THIS_MODULE,
+ attach:pcm3730_attach,
+ detach:pcm3730_detach,
+};
+
+COMEDI_INITCLEANUP(driver_pcm3730);
+
+static int pcm3730_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ outb(s->state, dev->iobase + (unsigned long)(s->private));
+ }
+ data[1] = s->state;
+
+ return 2;
+}
+
+static int pcm3730_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+ data[1] = inb(dev->iobase + (unsigned long)(s->private));
+ return 2;
+}
+
+static int pcm3730_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+
+ iobase = it->options[0];
+ printk("comedi%d: pcm3730: 0x%04lx ", dev->minor, iobase);
+ if (!request_region(iobase, PCM3730_SIZE, "pcm3730")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+ dev->board_name = "pcm3730";
+ dev->iobase = dev->iobase;
+ dev->irq = 0;
+
+ if (alloc_subdevices(dev, 6) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->maxdata = 1;
+ s->n_chan = 8;
+ s->insn_bits = pcm3730_do_insn_bits;
+ s->range_table = &range_digital;
+ s->private = (void *)PCM3730_DOA;
+
+ s = dev->subdevices + 1;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->maxdata = 1;
+ s->n_chan = 8;
+ s->insn_bits = pcm3730_do_insn_bits;
+ s->range_table = &range_digital;
+ s->private = (void *)PCM3730_DOB;
+
+ s = dev->subdevices + 2;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->maxdata = 1;
+ s->n_chan = 8;
+ s->insn_bits = pcm3730_do_insn_bits;
+ s->range_table = &range_digital;
+ s->private = (void *)PCM3730_DOC;
+
+ s = dev->subdevices + 3;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->maxdata = 1;
+ s->n_chan = 8;
+ s->insn_bits = pcm3730_di_insn_bits;
+ s->range_table = &range_digital;
+ s->private = (void *)PCM3730_DIA;
+
+ s = dev->subdevices + 4;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->maxdata = 1;
+ s->n_chan = 8;
+ s->insn_bits = pcm3730_di_insn_bits;
+ s->range_table = &range_digital;
+ s->private = (void *)PCM3730_DIB;
+
+ s = dev->subdevices + 5;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->maxdata = 1;
+ s->n_chan = 8;
+ s->insn_bits = pcm3730_di_insn_bits;
+ s->range_table = &range_digital;
+ s->private = (void *)PCM3730_DIC;
+
+ printk("\n");
+
+ return 0;
+}
+
+static int pcm3730_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: pcm3730: remove\n", dev->minor);
+
+ if (dev->iobase)
+ release_region(dev->iobase, PCM3730_SIZE);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/pcmad.c b/drivers/staging/comedi/drivers/pcmad.c
new file mode 100644
index 000000000000..fc2a73d97c24
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcmad.c
@@ -0,0 +1,173 @@
+/*
+ comedi/drivers/pcmad.c
+ Hardware driver for Winsystems PCM-A/D12 and PCM-A/D16
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000,2001 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: pcmad
+Description: Winsystems PCM-A/D12, PCM-A/D16
+Author: ds
+Devices: [Winsystems] PCM-A/D12 (pcmad12), PCM-A/D16 (pcmad16)
+Status: untested
+
+This driver was written on a bet that I couldn't write a driver
+in less than 2 hours. I won the bet, but never got paid. =(
+
+Configuration options:
+ [0] - I/O port base
+ [1] - unused
+ [2] - Analog input reference
+ 0 = single ended
+ 1 = differential
+ [3] - Analog input encoding (must match jumpers)
+ 0 = straight binary
+ 1 = two's complement
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+#define PCMAD_SIZE 4
+
+#define PCMAD_STATUS 0
+#define PCMAD_LSB 1
+#define PCMAD_MSB 2
+#define PCMAD_CONVERT 1
+
+struct pcmad_board_struct {
+ const char *name;
+ int n_ai_bits;
+};
+static const struct pcmad_board_struct pcmad_boards[] = {
+ {
+ name: "pcmad12",
+ n_ai_bits:12,
+ },
+ {
+ name: "pcmad16",
+ n_ai_bits:16,
+ },
+};
+
+#define this_board ((const struct pcmad_board_struct *)(dev->board_ptr))
+#define n_pcmad_boards (sizeof(pcmad_boards)/sizeof(pcmad_boards[0]))
+
+struct pcmad_priv_struct {
+ int differential;
+ int twos_comp;
+};
+#define devpriv ((struct pcmad_priv_struct *)dev->private)
+
+static int pcmad_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcmad_detach(struct comedi_device * dev);
+static struct comedi_driver driver_pcmad = {
+ driver_name:"pcmad",
+ module:THIS_MODULE,
+ attach:pcmad_attach,
+ detach:pcmad_detach,
+ board_name:&pcmad_boards[0].name,
+ num_names:n_pcmad_boards,
+ offset:sizeof(pcmad_boards[0]),
+};
+
+COMEDI_INITCLEANUP(driver_pcmad);
+
+#define TIMEOUT 100
+
+static int pcmad_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan;
+ int n;
+
+ chan = CR_CHAN(insn->chanspec);
+
+ for (n = 0; n < insn->n; n++) {
+ outb(chan, dev->iobase + PCMAD_CONVERT);
+
+ for (i = 0; i < TIMEOUT; i++) {
+ if ((inb(dev->iobase + PCMAD_STATUS) & 0x3) == 0x3)
+ break;
+ }
+ data[n] = inb(dev->iobase + PCMAD_LSB);
+ data[n] |= (inb(dev->iobase + PCMAD_MSB) << 8);
+
+ if (devpriv->twos_comp) {
+ data[n] ^= (1 << (this_board->n_ai_bits - 1));
+ }
+ }
+
+ return n;
+}
+
+/*
+ * options:
+ * 0 i/o base
+ * 1 unused
+ * 2 0=single ended 1=differential
+ * 3 0=straight binary 1=two's comp
+ */
+static int pcmad_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int ret;
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+
+ iobase = it->options[0];
+ printk("comedi%d: pcmad: 0x%04lx ", dev->minor, iobase);
+ if (!request_region(iobase, PCMAD_SIZE, "pcmad")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ if ((ret = alloc_subdevices(dev, 1)) < 0)
+ return ret;
+ if ((ret = alloc_private(dev, sizeof(struct pcmad_priv_struct))) < 0)
+ return ret;
+
+ dev->board_name = this_board->name;
+
+ s = dev->subdevices + 0;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | AREF_GROUND;
+ s->n_chan = 16; /* XXX */
+ s->len_chanlist = 1;
+ s->insn_read = pcmad_ai_insn_read;
+ s->maxdata = (1 << this_board->n_ai_bits) - 1;
+ s->range_table = &range_unknown;
+
+ return 0;
+}
+
+static int pcmad_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: pcmad: remove\n", dev->minor);
+
+ if (dev->irq) {
+ free_irq(dev->irq, dev);
+ }
+ if (dev->iobase)
+ release_region(dev->iobase, PCMAD_SIZE);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/pcmda12.c b/drivers/staging/comedi/drivers/pcmda12.c
new file mode 100644
index 000000000000..2a1ff465602a
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcmda12.c
@@ -0,0 +1,306 @@
+/*
+ comedi/drivers/pcmda12.c
+ Driver for Winsystems PC-104 based PCM-D/A-12 8-channel AO board.
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2006 Calin A. Culianu <calin@ajvar.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+Driver: pcmda12
+Description: A driver for the Winsystems PCM-D/A-12
+Devices: [Winsystems] PCM-D/A-12 (pcmda12)
+Author: Calin Culianu <calin@ajvar.org>
+Updated: Fri, 13 Jan 2006 12:01:01 -0500
+Status: works
+
+A driver for the relatively straightforward-to-program PCM-D/A-12.
+This board doesn't support commands, and the only way to set its
+analog output range is to jumper the board. As such,
+comedi_data_write() ignores the range value specified.
+
+The board uses 16 consecutive I/O addresses starting at the I/O port
+base address. Each address corresponds to the LSB then MSB of a
+particular channel from 0-7.
+
+Note that the board is not ISA-PNP capable and thus
+needs the I/O port comedi_config parameter.
+
+Note that passing a nonzero value as the second config option will
+enable "simultaneous xfer" mode for this board, in which AO writes
+will not take effect until a subsequent read of any AO channel. This
+is so that one can speed up programming by preloading all AO registers
+with values before simultaneously setting them to take effect with one
+read command.
+
+Configuration Options:
+ [0] - I/O port base address
+ [1] - Do Simultaneous Xfer (see description)
+*/
+
+#include "../comedidev.h"
+
+#include <linux/pci.h> /* for PCI devices */
+
+#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) )
+#define SDEV_NO ((int)(s - dev->subdevices))
+#define CHANS 8
+#define IOSIZE 16
+#define LSB(x) ((unsigned char)((x) & 0xff))
+#define MSB(x) ((unsigned char)((((unsigned short)(x))>>8) & 0xff))
+#define LSB_PORT(chan) (dev->iobase + (chan)*2)
+#define MSB_PORT(chan) (LSB_PORT(chan)+1)
+#define BITS 12
+
+/*
+ * Bords
+ */
+struct pcmda12_board {
+ const char *name;
+};
+
+/* note these have no effect and are merely here for reference..
+ these are configured by jumpering the board! */
+static const struct comedi_lrange pcmda12_ranges = {
+ 3,
+ {
+ UNI_RANGE(5), UNI_RANGE(10), BIP_RANGE(5)
+ }
+};
+
+static const struct pcmda12_board pcmda12_boards[] = {
+ {
+ name: "pcmda12",
+ },
+};
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct pcmda12_board *)dev->board_ptr)
+
+struct pcmda12_private {
+
+ unsigned int ao_readback[CHANS];
+ int simultaneous_xfer_mode;
+};
+
+
+#define devpriv ((struct pcmda12_private *)(dev->private))
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int pcmda12_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcmda12_detach(struct comedi_device * dev);
+
+static void zero_chans(struct comedi_device * dev);
+
+static struct comedi_driver driver = {
+ driver_name:"pcmda12",
+ module:THIS_MODULE,
+ attach:pcmda12_attach,
+ detach:pcmda12_detach,
+/* It is not necessary to implement the following members if you are
+ * writing a driver for a ISA PnP or PCI card */
+ /* Most drivers will support multiple types of boards by
+ * having an array of board structures. These were defined
+ * in pcmda12_boards[] above. Note that the element 'name'
+ * was first in the structure -- Comedi uses this fact to
+ * extract the name of the board without knowing any details
+ * about the structure except for its length.
+ * When a device is attached (by comedi_config), the name
+ * of the device is given to Comedi, and Comedi tries to
+ * match it by going through the list of board names. If
+ * there is a match, the address of the pointer is put
+ * into dev->board_ptr and driver->attach() is called.
+ *
+ * Note that these are not necessary if you can determine
+ * the type of board in software. ISA PnP, PCI, and PCMCIA
+ * devices are such boards.
+ */
+ board_name:&pcmda12_boards[0].name,
+ offset:sizeof(struct pcmda12_board),
+ num_names:sizeof(pcmda12_boards) / sizeof(struct pcmda12_board),
+};
+
+static int ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int pcmda12_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+
+ iobase = it->options[0];
+ printk("comedi%d: %s: io: %lx %s ", dev->minor, driver.driver_name,
+ iobase, it->options[1] ? "simultaneous xfer mode enabled" : "");
+
+ if (!request_region(iobase, IOSIZE, driver.driver_name)) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+/*
+ * Initialize dev->board_name. Note that we can use the "thisboard"
+ * macro now, since we just initialized it in the last line.
+ */
+ dev->board_name = thisboard->name;
+
+/*
+ * Allocate the private structure area. alloc_private() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_private(dev, sizeof(struct pcmda12_private)) < 0) {
+ printk("cannot allocate private data structure\n");
+ return -ENOMEM;
+ }
+
+ devpriv->simultaneous_xfer_mode = it->options[1];
+
+ /*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ *
+ * Allocate 2 subdevs (32 + 16 DIO lines) or 3 32 DIO subdevs for the
+ * 96-channel version of the board.
+ */
+ if (alloc_subdevices(dev, 1) < 0) {
+ printk("cannot allocate subdevice data structures\n");
+ return -ENOMEM;
+ }
+
+ s = dev->subdevices;
+ s->private = NULL;
+ s->maxdata = (0x1 << BITS) - 1;
+ s->range_table = &pcmda12_ranges;
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = CHANS;
+ s->insn_write = &ao_winsn;
+ s->insn_read = &ao_rinsn;
+
+ zero_chans(dev); /* clear out all the registers, basically */
+
+ printk("attached\n");
+
+ return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int pcmda12_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
+ if (dev->iobase)
+ release_region(dev->iobase, IOSIZE);
+ return 0;
+}
+
+static void zero_chans(struct comedi_device * dev)
+{ /* sets up an
+ ASIC chip to defaults */
+ int i;
+ for (i = 0; i < CHANS; ++i) {
+/* /\* do this as one instruction?? *\/ */
+/* outw(0, LSB_PORT(chan)); */
+ outb(0, LSB_PORT(i));
+ outb(0, MSB_PORT(i));
+ }
+ inb(LSB_PORT(0)); /* update chans. */
+}
+
+static int ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ /* Writing a list of values to an AO channel is probably not
+ * very useful, but that's how the interface is defined. */
+ for (i = 0; i < insn->n; ++i) {
+
+/* /\* do this as one instruction?? *\/ */
+/* outw(data[i], LSB_PORT(chan)); */
+
+ /* Need to do this as two instructions due to 8-bit bus?? */
+ /* first, load the low byte */
+ outb(LSB(data[i]), LSB_PORT(chan));
+ /* next, write the high byte */
+ outb(MSB(data[i]), MSB_PORT(chan));
+
+ /* save shadow register */
+ devpriv->ao_readback[chan] = data[i];
+
+ if (!devpriv->simultaneous_xfer_mode)
+ inb(LSB_PORT(chan));
+ }
+
+ /* return the number of samples written */
+ return i;
+}
+
+/* AO subdevices should have a read insn as well as a write insn.
+
+ Usually this means copying a value stored in devpriv->ao_readback.
+ However, since this driver supports simultaneous xfer then sometimes
+ this function actually accomplishes work.
+
+ Simultaneaous xfer mode is accomplished by loading ALL the values
+ you want for AO in all the channels, then READing off one of the AO
+ registers to initiate the instantaneous simultaneous update of all
+ DAC outputs, which makes all AO channels update simultaneously.
+ This is useful for some control applications, I would imagine.
+*/
+static int ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++) {
+ if (devpriv->simultaneous_xfer_mode)
+ inb(LSB_PORT(chan));
+ /* read back shadow register */
+ data[i] = devpriv->ao_readback[chan];
+ }
+
+ return i;
+}
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_INITCLEANUP(driver);
diff --git a/drivers/staging/comedi/drivers/pcmmio.c b/drivers/staging/comedi/drivers/pcmmio.c
new file mode 100644
index 000000000000..01e40f1b5628
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcmmio.c
@@ -0,0 +1,1333 @@
+/*
+ comedi/drivers/pcmmio.c
+ Driver for Winsystems PC-104 based multifunction IO board.
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2007 Calin A. Culianu <calin@ajvar.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+Driver: pcmmio
+Description: A driver for the PCM-MIO multifunction board
+Devices: [Winsystems] PCM-MIO (pcmmio)
+Author: Calin Culianu <calin@ajvar.org>
+Updated: Wed, May 16 2007 16:21:10 -0500
+Status: works
+
+A driver for the relatively new PCM-MIO multifunction board from
+Winsystems. This board is a PC-104 based I/O board. It contains
+four subdevices:
+ subdevice 0 - 16 channels of 16-bit AI
+ subdevice 1 - 8 channels of 16-bit AO
+ subdevice 2 - first 24 channels of the 48 channel of DIO (with edge-triggered interrupt support)
+ subdevice 3 - last 24 channels of the 48 channel DIO (no interrupt support for this bank of channels)
+
+ Some notes:
+
+ Synchronous reads and writes are the only things implemented for AI and AO,
+ even though the hardware itself can do streaming acquisition, etc. Anyone
+ want to add asynchronous I/O for AI/AO as a feature? Be my guest...
+
+ Asynchronous I/O for the DIO subdevices *is* implemented, however! They are
+ basically edge-triggered interrupts for any configuration of the first
+ 24 DIO-lines.
+
+ Also note that this interrupt support is untested.
+
+ A few words about edge-detection IRQ support (commands on DIO):
+
+ * To use edge-detection IRQ support for the DIO subdevice, pass the IRQ
+ of the board to the comedi_config command. The board IRQ is not jumpered
+ but rather configured through software, so any IRQ from 1-15 is OK.
+
+ * Due to the genericity of the comedi API, you need to create a special
+ comedi_command in order to use edge-triggered interrupts for DIO.
+
+ * Use comedi_commands with TRIG_NOW. Your callback will be called each
+ time an edge is detected on the specified DIO line(s), and the data
+ values will be two sample_t's, which should be concatenated to form
+ one 32-bit unsigned int. This value is the mask of channels that had
+ edges detected from your channel list. Note that the bits positions
+ in the mask correspond to positions in your chanlist when you
+ specified the command and *not* channel id's!
+
+ * To set the polarity of the edge-detection interrupts pass a nonzero value
+ for either CR_RANGE or CR_AREF for edge-up polarity, or a zero
+ value for both CR_RANGE and CR_AREF if you want edge-down polarity.
+
+Configuration Options:
+ [0] - I/O port base address
+ [1] - IRQ (optional -- for edge-detect interrupt support only, leave out if you don't need this feature)
+*/
+
+#include "../comedidev.h"
+#include <linux/pci.h> /* for PCI devices */
+
+#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) )
+
+/* This stuff is all from pcmuio.c -- it refers to the DIO subdevices only */
+#define CHANS_PER_PORT 8
+#define PORTS_PER_ASIC 6
+#define INTR_PORTS_PER_ASIC 3
+#define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
+#define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
+#define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
+#define INTR_CHANS_PER_ASIC 24
+#define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
+#define MAX_DIO_CHANS (PORTS_PER_ASIC*1*CHANS_PER_PORT)
+#define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC)
+#define SDEV_NO ((int)(s - dev->subdevices))
+#define CALC_N_DIO_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
+/* IO Memory sizes */
+#define ASIC_IOSIZE (0x0B)
+#define PCMMIO48_IOSIZE ASIC_IOSIZE
+
+/* Some offsets - these are all in the 16byte IO memory offset from
+ the base address. Note that there is a paging scheme to swap out
+ offsets 0x8-0xA using the PAGELOCK register. See the table below.
+
+ Register(s) Pages R/W? Description
+ --------------------------------------------------------------
+ REG_PORTx All R/W Read/Write/Configure IO
+ REG_INT_PENDING All ReadOnly Quickly see which INT_IDx has int.
+ REG_PAGELOCK All WriteOnly Select a page
+ REG_POLx Pg. 1 only WriteOnly Select edge-detection polarity
+ REG_ENABx Pg. 2 only WriteOnly Enable/Disable edge-detect. int.
+ REG_INT_IDx Pg. 3 only R/W See which ports/bits have ints.
+ */
+#define REG_PORT0 0x0
+#define REG_PORT1 0x1
+#define REG_PORT2 0x2
+#define REG_PORT3 0x3
+#define REG_PORT4 0x4
+#define REG_PORT5 0x5
+#define REG_INT_PENDING 0x6
+#define REG_PAGELOCK 0x7 /* page selector register, upper 2 bits select a page
+ and bits 0-5 are used to 'lock down' a particular
+ port above to make it readonly. */
+#define REG_POL0 0x8
+#define REG_POL1 0x9
+#define REG_POL2 0xA
+#define REG_ENAB0 0x8
+#define REG_ENAB1 0x9
+#define REG_ENAB2 0xA
+#define REG_INT_ID0 0x8
+#define REG_INT_ID1 0x9
+#define REG_INT_ID2 0xA
+
+#define NUM_PAGED_REGS 3
+#define NUM_PAGES 4
+#define FIRST_PAGED_REG 0x8
+#define REG_PAGE_BITOFFSET 6
+#define REG_LOCK_BITOFFSET 0
+#define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
+#define REG_LOCK_MASK ~(REG_PAGE_MASK)
+#define PAGE_POL 1
+#define PAGE_ENAB 2
+#define PAGE_INT_ID 3
+
+typedef int (*comedi_insn_fn_t) (struct comedi_device *, struct comedi_subdevice *,
+ struct comedi_insn *, unsigned int *);
+
+static int ai_rinsn(struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *,
+ unsigned int *);
+static int ao_rinsn(struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *,
+ unsigned int *);
+static int ao_winsn(struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *,
+ unsigned int *);
+
+/*
+ * Board descriptions for two imaginary boards. Describing the
+ * boards in this way is optional, and completely driver-dependent.
+ * Some drivers use arrays such as this, other do not.
+ */
+struct pcmmio_board {
+ const char *name;
+ const int dio_num_asics;
+ const int dio_num_ports;
+ const int total_iosize;
+ const int ai_bits;
+ const int ao_bits;
+ const int n_ai_chans;
+ const int n_ao_chans;
+ const struct comedi_lrange *ai_range_table, *ao_range_table;
+ comedi_insn_fn_t ai_rinsn, ao_rinsn, ao_winsn;
+};
+
+static const struct comedi_lrange ranges_ai =
+ { 4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0.,
+ 10.)}
+};
+
+static const struct comedi_lrange ranges_ao =
+ { 6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
+ RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
+};
+
+static const struct pcmmio_board pcmmio_boards[] = {
+ {
+ name: "pcmmio",
+ dio_num_asics:1,
+ dio_num_ports:6,
+ total_iosize:32,
+ ai_bits: 16,
+ ao_bits: 16,
+ n_ai_chans:16,
+ n_ao_chans:8,
+ ai_range_table:&ranges_ai,
+ ao_range_table:&ranges_ao,
+ ai_rinsn:ai_rinsn,
+ ao_rinsn:ao_rinsn,
+ ao_winsn:ao_winsn},
+};
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct pcmmio_board *)dev->board_ptr)
+
+/* this structure is for data unique to this subdevice. */
+struct pcmmio_subdev_private {
+
+ union {
+ /* for DIO: mapping of halfwords (bytes) in port/chanarray to iobase */
+ unsigned long iobases[PORTS_PER_SUBDEV];
+
+ /* for AI/AO */
+ unsigned long iobase;
+ };
+ union {
+ struct {
+
+ /* The below is only used for intr subdevices */
+ struct {
+ int asic; /* if non-negative, this subdev has an interrupt asic */
+ int first_chan; /* if nonnegative, the first channel id for
+ interrupts. */
+ int num_asic_chans; /* the number of asic channels in this subdev
+ that have interrutps */
+ int asic_chan; /* if nonnegative, the first channel id with
+ respect to the asic that has interrupts */
+ int enabled_mask; /* subdev-relative channel mask for channels
+ we are interested in */
+ int active;
+ int stop_count;
+ int continuous;
+ spinlock_t spinlock;
+ } intr;
+ } dio;
+ struct {
+ unsigned int shadow_samples[8]; /* the last unsigned int data written */
+ } ao;
+ };
+};
+
+/* 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 struct comedi_device struct. */
+struct pcmmio_private {
+ /* stuff for DIO */
+ struct {
+ unsigned char pagelock; /* current page and lock */
+ unsigned char pol[NUM_PAGED_REGS]; /* shadow of POLx registers */
+ unsigned char enab[NUM_PAGED_REGS]; /* shadow of ENABx registers */
+ int num;
+ unsigned long iobase;
+ unsigned int irq;
+ spinlock_t spinlock;
+ } asics[MAX_ASICS];
+ struct pcmmio_subdev_private *sprivs;
+};
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((struct pcmmio_private *)dev->private)
+#define subpriv ((struct pcmmio_subdev_private *)s->private)
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int pcmmio_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcmmio_detach(struct comedi_device * dev);
+
+static struct comedi_driver driver = {
+ driver_name:"pcmmio",
+ module:THIS_MODULE,
+ attach:pcmmio_attach,
+ detach:pcmmio_detach,
+/* It is not necessary to implement the following members if you are
+ * writing a driver for a ISA PnP or PCI card */
+ /* Most drivers will support multiple types of boards by
+ * having an array of board structures. These were defined
+ * in pcmmio_boards[] above. Note that the element 'name'
+ * was first in the structure -- Comedi uses this fact to
+ * extract the name of the board without knowing any details
+ * about the structure except for its length.
+ * When a device is attached (by comedi_config), the name
+ * of the device is given to Comedi, and Comedi tries to
+ * match it by going through the list of board names. If
+ * there is a match, the address of the pointer is put
+ * into dev->board_ptr and driver->attach() is called.
+ *
+ * Note that these are not necessary if you can determine
+ * the type of board in software. ISA PnP, PCI, and PCMCIA
+ * devices are such boards.
+ */
+ board_name:&pcmmio_boards[0].name,
+ offset:sizeof(struct pcmmio_board),
+ num_names:sizeof(pcmmio_boards) / sizeof(struct pcmmio_board),
+};
+
+static int pcmmio_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int pcmmio_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static irqreturn_t interrupt_pcmmio(int irq, void *d PT_REGS_ARG);
+static void pcmmio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
+static int pcmmio_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static int pcmmio_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int pcmmio_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+
+/* some helper functions to deal with specifics of this device's registers */
+static void init_asics(struct comedi_device * dev); /* sets up/clears ASIC chips to defaults */
+static void switch_page(struct comedi_device * dev, int asic, int page);
+#ifdef notused
+static void lock_port(struct comedi_device * dev, int asic, int port);
+static void unlock_port(struct comedi_device * dev, int asic, int port);
+#endif
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int pcmmio_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
+ thisasic_chanct = 0;
+ unsigned long iobase;
+ unsigned int irq[MAX_ASICS];
+
+ iobase = it->options[0];
+ irq[0] = it->options[1];
+
+ printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
+ iobase);
+
+ dev->iobase = iobase;
+
+ if (!iobase || !request_region(iobase,
+ thisboard->total_iosize, driver.driver_name)) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+
+/*
+ * Initialize dev->board_name. Note that we can use the "thisboard"
+ * macro now, since we just initialized it in the last line.
+ */
+ dev->board_name = thisboard->name;
+
+/*
+ * Allocate the private structure area. alloc_private() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_private(dev, sizeof(struct pcmmio_private)) < 0) {
+ printk("cannot allocate private data structure\n");
+ return -ENOMEM;
+ }
+
+ for (asic = 0; asic < MAX_ASICS; ++asic) {
+ devpriv->asics[asic].num = asic;
+ devpriv->asics[asic].iobase =
+ dev->iobase + 16 + asic * ASIC_IOSIZE;
+ devpriv->asics[asic].irq = 0; /* this gets actually set at the end of
+ this function when we
+ comedi_request_irqs */
+ spin_lock_init(&devpriv->asics[asic].spinlock);
+ }
+
+ chans_left = CHANS_PER_ASIC * thisboard->dio_num_asics;
+ n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
+ n_subdevs = n_dio_subdevs + 2;
+ devpriv->sprivs =
+ kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private), GFP_KERNEL);
+ if (!devpriv->sprivs) {
+ printk("cannot allocate subdevice private data structures\n");
+ return -ENOMEM;
+ }
+ /*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ *
+ * Allocate 1 AI + 1 AO + 2 DIO subdevs (24 lines per DIO)
+ */
+ if (alloc_subdevices(dev, n_subdevs) < 0) {
+ printk("cannot allocate subdevice data structures\n");
+ return -ENOMEM;
+ }
+
+ /* First, AI */
+ sdev_no = 0;
+ s = dev->subdevices + sdev_no;
+ s->private = devpriv->sprivs + sdev_no;
+ s->maxdata = (1 << thisboard->ai_bits) - 1;
+ s->range_table = thisboard->ai_range_table;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
+ s->type = COMEDI_SUBD_AI;
+ s->n_chan = thisboard->n_ai_chans;
+ s->len_chanlist = s->n_chan;
+ s->insn_read = thisboard->ai_rinsn;
+ subpriv->iobase = dev->iobase + 0;
+ /* initialize the resource enable register by clearing it */
+ outb(0, subpriv->iobase + 3);
+ outb(0, subpriv->iobase + 4 + 3);
+
+ /* Next, AO */
+ ++sdev_no;
+ s = dev->subdevices + sdev_no;
+ s->private = devpriv->sprivs + sdev_no;
+ s->maxdata = (1 << thisboard->ao_bits) - 1;
+ s->range_table = thisboard->ao_range_table;
+ s->subdev_flags = SDF_READABLE;
+ s->type = COMEDI_SUBD_AO;
+ s->n_chan = thisboard->n_ao_chans;
+ s->len_chanlist = s->n_chan;
+ s->insn_read = thisboard->ao_rinsn;
+ s->insn_write = thisboard->ao_winsn;
+ subpriv->iobase = dev->iobase + 8;
+ /* initialize the resource enable register by clearing it */
+ outb(0, subpriv->iobase + 3);
+ outb(0, subpriv->iobase + 4 + 3);
+
+ ++sdev_no;
+ port = 0;
+ asic = 0;
+ for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
+ int byte_no;
+
+ s = dev->subdevices + sdev_no;
+ s->private = devpriv->sprivs + sdev_no;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->type = COMEDI_SUBD_DIO;
+ s->insn_bits = pcmmio_dio_insn_bits;
+ s->insn_config = pcmmio_dio_insn_config;
+ s->n_chan = MIN(chans_left, MAX_CHANS_PER_SUBDEV);
+ subpriv->dio.intr.asic = -1;
+ subpriv->dio.intr.first_chan = -1;
+ subpriv->dio.intr.asic_chan = -1;
+ subpriv->dio.intr.num_asic_chans = -1;
+ subpriv->dio.intr.active = 0;
+ s->len_chanlist = 1;
+
+ /* save the ioport address for each 'port' of 8 channels in the
+ subdevice */
+ for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
+ if (port >= PORTS_PER_ASIC) {
+ port = 0;
+ ++asic;
+ thisasic_chanct = 0;
+ }
+ subpriv->iobases[byte_no] =
+ devpriv->asics[asic].iobase + port;
+
+ if (thisasic_chanct <
+ CHANS_PER_PORT * INTR_PORTS_PER_ASIC
+ && subpriv->dio.intr.asic < 0) {
+ /* this is an interrupt subdevice, so setup the struct */
+ subpriv->dio.intr.asic = asic;
+ subpriv->dio.intr.active = 0;
+ subpriv->dio.intr.stop_count = 0;
+ subpriv->dio.intr.first_chan = byte_no * 8;
+ subpriv->dio.intr.asic_chan = thisasic_chanct;
+ subpriv->dio.intr.num_asic_chans =
+ s->n_chan -
+ subpriv->dio.intr.first_chan;
+ s->cancel = pcmmio_cancel;
+ s->do_cmd = pcmmio_cmd;
+ s->do_cmdtest = pcmmio_cmdtest;
+ s->len_chanlist =
+ subpriv->dio.intr.num_asic_chans;
+ }
+ thisasic_chanct += CHANS_PER_PORT;
+ }
+ spin_lock_init(&subpriv->dio.intr.spinlock);
+
+ chans_left -= s->n_chan;
+
+ if (!chans_left) {
+ asic = 0; /* reset the asic to our first asic, to do intr subdevs */
+ port = 0;
+ }
+
+ }
+
+ init_asics(dev); /* clear out all the registers, basically */
+
+ for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
+ if (irq[asic]
+ && comedi_request_irq(irq[asic], interrupt_pcmmio,
+ IRQF_SHARED, thisboard->name, dev)) {
+ int i;
+ /* unroll the allocated irqs.. */
+ for (i = asic - 1; i >= 0; --i) {
+ comedi_free_irq(irq[i], dev);
+ devpriv->asics[i].irq = irq[i] = 0;
+ }
+ irq[asic] = 0;
+ }
+ devpriv->asics[asic].irq = irq[asic];
+ }
+
+ dev->irq = irq[0]; /* grr.. wish comedi dev struct supported multiple
+ irqs.. */
+
+ if (irq[0]) {
+ printk("irq: %u ", irq[0]);
+ if (irq[1] && thisboard->dio_num_asics == 2)
+ printk("second ASIC irq: %u ", irq[1]);
+ } else {
+ printk("(IRQ mode disabled) ");
+ }
+
+ printk("attached\n");
+
+ return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int pcmmio_detach(struct comedi_device * dev)
+{
+ int i;
+
+ printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
+ if (dev->iobase)
+ release_region(dev->iobase, thisboard->total_iosize);
+
+ for (i = 0; i < MAX_ASICS; ++i) {
+ if (devpriv && devpriv->asics[i].irq)
+ comedi_free_irq(devpriv->asics[i].irq, dev);
+ }
+
+ if (devpriv && devpriv->sprivs)
+ kfree(devpriv->sprivs);
+
+ return 0;
+}
+
+/* DIO devices are slightly special. Although it is possible to
+ * implement the insn_read/insn_write interface, it is much more
+ * useful to applications if you implement the insn_bits interface.
+ * This allows packed reading/writing of the DIO channels. The
+ * comedi core can convert between insn_bits and insn_read/write */
+static int pcmmio_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int byte_no;
+ if (insn->n != 2)
+ return -EINVAL;
+
+ /* NOTE:
+ reading a 0 means this channel was high
+ writine a 0 sets the channel high
+ reading a 1 means this channel was low
+ writing a 1 means set this channel low
+
+ Therefore everything is always inverted. */
+
+ /* The insn data is a mask in data[0] and the new data
+ * in data[1], each channel cooresponding to a bit. */
+
+#ifdef DAMMIT_ITS_BROKEN
+ /* DEBUG */
+ printk("write mask: %08x data: %08x\n", data[0], data[1]);
+#endif
+
+ s->state = 0;
+
+ for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
+ /* address of 8-bit port */
+ unsigned long ioaddr = subpriv->iobases[byte_no],
+ /* bit offset of port in 32-bit doubleword */
+ offset = byte_no * 8;
+ /* this 8-bit port's data */
+ unsigned char byte = 0,
+ /* The write mask for this port (if any) */
+ write_mask_byte = (data[0] >> offset) & 0xff,
+ /* The data byte for this port */
+ data_byte = (data[1] >> offset) & 0xff;
+
+ byte = inb(ioaddr); /* read all 8-bits for this port */
+
+#ifdef DAMMIT_ITS_BROKEN
+ /* DEBUG */
+ printk("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ", byte_no, (unsigned)write_mask_byte, (unsigned)data_byte, offset, ioaddr, (unsigned)byte);
+#endif
+
+ if (write_mask_byte) {
+ /* this byte has some write_bits -- so set the output lines */
+ byte &= ~write_mask_byte; /* clear bits for write mask */
+ byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */
+ /* Write out the new digital output state */
+ outb(byte, ioaddr);
+ }
+#ifdef DAMMIT_ITS_BROKEN
+ /* DEBUG */
+ printk("data_out_byte %02x\n", (unsigned)byte);
+#endif
+ /* save the digital input lines for this byte.. */
+ s->state |= ((unsigned int)byte) << offset;
+ }
+
+ /* now return the DIO lines to data[1] - note they came inverted! */
+ data[1] = ~s->state;
+
+#ifdef DAMMIT_ITS_BROKEN
+ /* DEBUG */
+ printk("s->state %08x data_out %08x\n", s->state, data[1]);
+#endif
+
+ return 2;
+}
+
+/* The input or output configuration of each digital line is
+ * configured by a special insn_config instruction. chanspec
+ * contains the channel to be changed, and data[0] contains the
+ * value COMEDI_INPUT or COMEDI_OUTPUT. */
+static int pcmmio_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
+ chan % 8;
+ unsigned long ioaddr;
+ unsigned char byte;
+
+ /* Compute ioaddr for this channel */
+ ioaddr = subpriv->iobases[byte_no];
+
+ /* NOTE:
+ writing a 0 an IO channel's bit sets the channel to INPUT
+ and pulls the line high as well
+
+ writing a 1 to an IO channel's bit pulls the line low
+
+ All channels are implicitly always in OUTPUT mode -- but when
+ they are high they can be considered to be in INPUT mode..
+
+ Thus, we only force channels low if the config request was INPUT,
+ otherwise we do nothing to the hardware. */
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ /* save to io_bits -- don't actually do anything since
+ all input channels are also output channels... */
+ s->io_bits |= 1 << chan;
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ /* write a 0 to the actual register representing the channel
+ to set it to 'input'. 0 means "float high". */
+ byte = inb(ioaddr);
+ byte &= ~(1 << bit_no);
+ /**< set input channel to '0' */
+
+ /* write out byte -- this is the only time we actually affect the
+ hardware as all channels are implicitly output -- but input
+ channels are set to float-high */
+ outb(byte, ioaddr);
+
+ /* save to io_bits */
+ s->io_bits &= ~(1 << chan);
+ break;
+
+ case INSN_CONFIG_DIO_QUERY:
+ /* retreive from shadow register */
+ data[1] =
+ (s->
+ io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+ break;
+
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ return insn->n;
+}
+
+static void init_asics(struct comedi_device * dev)
+{ /* sets up an
+ ASIC chip to defaults */
+ int asic;
+
+ for (asic = 0; asic < thisboard->dio_num_asics; ++asic) {
+ int port, page;
+ unsigned long baseaddr = devpriv->asics[asic].iobase;
+
+ switch_page(dev, asic, 0); /* switch back to page 0 */
+
+ /* first, clear all the DIO port bits */
+ for (port = 0; port < PORTS_PER_ASIC; ++port)
+ outb(0, baseaddr + REG_PORT0 + port);
+
+ /* Next, clear all the paged registers for each page */
+ for (page = 1; page < NUM_PAGES; ++page) {
+ int reg;
+ /* now clear all the paged registers */
+ switch_page(dev, asic, page);
+ for (reg = FIRST_PAGED_REG;
+ reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
+ outb(0, baseaddr + reg);
+ }
+
+ /* DEBUG set rising edge interrupts on port0 of both asics */
+ /*switch_page(dev, asic, PAGE_POL);
+ outb(0xff, baseaddr + REG_POL0);
+ switch_page(dev, asic, PAGE_ENAB);
+ outb(0xff, baseaddr + REG_ENAB0); */
+ /* END DEBUG */
+
+ switch_page(dev, asic, 0); /* switch back to default page 0 */
+
+ }
+}
+
+static void switch_page(struct comedi_device * dev, int asic, int page)
+{
+ if (asic < 0 || asic >= thisboard->dio_num_asics)
+ return; /* paranoia */
+ if (page < 0 || page >= NUM_PAGES)
+ return; /* more paranoia */
+
+ devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
+ devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
+
+ /* now write out the shadow register */
+ outb(devpriv->asics[asic].pagelock,
+ devpriv->asics[asic].iobase + REG_PAGELOCK);
+}
+
+#ifdef notused
+static void lock_port(struct comedi_device * dev, int asic, int port)
+{
+ if (asic < 0 || asic >= thisboard->dio_num_asics)
+ return; /* paranoia */
+ if (port < 0 || port >= PORTS_PER_ASIC)
+ return; /* more paranoia */
+
+ devpriv->asics[asic].pagelock |= 0x1 << port;
+ /* now write out the shadow register */
+ outb(devpriv->asics[asic].pagelock,
+ devpriv->asics[asic].iobase + REG_PAGELOCK);
+ return;
+}
+
+static void unlock_port(struct comedi_device * dev, int asic, int port)
+{
+ if (asic < 0 || asic >= thisboard->dio_num_asics)
+ return; /* paranoia */
+ if (port < 0 || port >= PORTS_PER_ASIC)
+ return; /* more paranoia */
+ devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
+ /* now write out the shadow register */
+ outb(devpriv->asics[asic].pagelock,
+ devpriv->asics[asic].iobase + REG_PAGELOCK);
+}
+#endif /* notused */
+
+static irqreturn_t interrupt_pcmmio(int irq, void *d PT_REGS_ARG)
+{
+ int asic, got1 = 0;
+ struct comedi_device *dev = (struct comedi_device *) d;
+
+ for (asic = 0; asic < MAX_ASICS; ++asic) {
+ if (irq == devpriv->asics[asic].irq) {
+ unsigned long flags;
+ unsigned triggered = 0;
+ unsigned long iobase = devpriv->asics[asic].iobase;
+ /* it is an interrupt for ASIC #asic */
+ unsigned char int_pend;
+
+ comedi_spin_lock_irqsave(&devpriv->asics[asic].spinlock,
+ flags);
+
+ int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
+
+ if (int_pend) {
+ int port;
+ for (port = 0; port < INTR_PORTS_PER_ASIC;
+ ++port) {
+ if (int_pend & (0x1 << port)) {
+ unsigned char
+ io_lines_with_edges = 0;
+ switch_page(dev, asic,
+ PAGE_INT_ID);
+ io_lines_with_edges =
+ inb(iobase +
+ REG_INT_ID0 + port);
+
+ if (io_lines_with_edges)
+ /* clear pending interrupt */
+ outb(0, iobase +
+ REG_INT_ID0 +
+ port);
+
+ triggered |=
+ io_lines_with_edges <<
+ port * 8;
+ }
+ }
+
+ ++got1;
+ }
+
+ comedi_spin_unlock_irqrestore(&devpriv->asics[asic].
+ spinlock, flags);
+
+ if (triggered) {
+ struct comedi_subdevice *s;
+ /* TODO here: dispatch io lines to subdevs with commands.. */
+ printk("PCMMIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n", irq, asic, triggered);
+ for (s = dev->subdevices + 2;
+ s < dev->subdevices + dev->n_subdevices;
+ ++s) {
+ if (subpriv->dio.intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */
+ unsigned long flags;
+ unsigned oldevents;
+
+ comedi_spin_lock_irqsave
+ (&subpriv->dio.intr.
+ spinlock, flags);
+
+ oldevents = s->async->events;
+
+ if (subpriv->dio.intr.active) {
+ unsigned mytrig =
+ ((triggered >>
+ subpriv->
+ dio.
+ intr.
+ asic_chan)
+ & ((0x1 << subpriv->dio.intr.num_asic_chans) - 1)) << subpriv->dio.intr.first_chan;
+ if (mytrig & subpriv->
+ dio.intr.
+ enabled_mask) {
+ unsigned int val =
+ 0;
+ unsigned int n,
+ ch, len;
+
+ len = s->async->
+ cmd.
+ chanlist_len;
+ for (n = 0;
+ n < len;
+ n++) {
+ ch = CR_CHAN(s->async->cmd.chanlist[n]);
+ if (mytrig & (1U << ch)) {
+ val |= (1U << n);
+ }
+ }
+ /* Write the scan to the buffer. */
+ if (comedi_buf_put(s->async, ((short *) & val)[0])
+ &&
+ comedi_buf_put
+ (s->async, ((short *) & val)[1])) {
+ s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
+ } else {
+ /* Overflow! Stop acquisition!! */
+ /* TODO: STOP_ACQUISITION_CALL_HERE!! */
+ pcmmio_stop_intr
+ (dev,
+ s);
+ }
+
+ /* Check for end of acquisition. */
+ if (!subpriv->
+ dio.
+ intr.
+ continuous)
+ {
+ /* stop_src == TRIG_COUNT */
+ if (subpriv->dio.intr.stop_count > 0) {
+ subpriv->
+ dio.
+ intr.
+ stop_count--;
+ if (subpriv->dio.intr.stop_count == 0) {
+ s->async->events |= COMEDI_CB_EOA;
+ /* TODO: STOP_ACQUISITION_CALL_HERE!! */
+ pcmmio_stop_intr
+ (dev,
+ s);
+ }
+ }
+ }
+ }
+ }
+
+ comedi_spin_unlock_irqrestore
+ (&subpriv->dio.intr.
+ spinlock, flags);
+
+ if (oldevents !=
+ s->async->events) {
+ comedi_event(dev, s);
+ }
+
+ }
+
+ }
+ }
+
+ }
+ }
+ if (!got1)
+ return IRQ_NONE; /* interrupt from other source */
+ return IRQ_HANDLED;
+}
+
+static void pcmmio_stop_intr(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int nports, firstport, asic, port;
+
+ if ((asic = subpriv->dio.intr.asic) < 0)
+ return; /* not an interrupt subdev */
+
+ subpriv->dio.intr.enabled_mask = 0;
+ subpriv->dio.intr.active = 0;
+ s->async->inttrig = 0;
+ nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
+ firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
+ switch_page(dev, asic, PAGE_ENAB);
+ for (port = firstport; port < firstport + nports; ++port) {
+ /* disable all intrs for this subdev.. */
+ outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
+ }
+}
+
+static int pcmmio_start_intr(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ if (!subpriv->dio.intr.continuous && subpriv->dio.intr.stop_count == 0) {
+ /* An empty acquisition! */
+ s->async->events |= COMEDI_CB_EOA;
+ subpriv->dio.intr.active = 0;
+ return 1;
+ } else {
+ unsigned bits = 0, pol_bits = 0, n;
+ int nports, firstport, asic, port;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if ((asic = subpriv->dio.intr.asic) < 0)
+ return 1; /* not an interrupt
+ subdev */
+ subpriv->dio.intr.enabled_mask = 0;
+ subpriv->dio.intr.active = 1;
+ nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
+ firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
+ if (cmd->chanlist) {
+ for (n = 0; n < cmd->chanlist_len; n++) {
+ bits |= (1U << CR_CHAN(cmd->chanlist[n]));
+ pol_bits |= (CR_AREF(cmd->chanlist[n])
+ || CR_RANGE(cmd->chanlist[n]) ? 1U : 0U)
+ << CR_CHAN(cmd->chanlist[n]);
+ }
+ }
+ bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
+ 1) << subpriv->dio.intr.first_chan;
+ subpriv->dio.intr.enabled_mask = bits;
+
+ { /* the below code configures the board to use a specific IRQ from 0-15. */
+ unsigned char b;
+ /* set resource enable register to enable IRQ operation */
+ outb(1 << 4, dev->iobase + 3);
+ /* set bits 0-3 of b to the irq number from 0-15 */
+ b = dev->irq & ((1 << 4) - 1);
+ outb(b, dev->iobase + 2);
+ /* done, we told the board what irq to use */
+ }
+
+ switch_page(dev, asic, PAGE_ENAB);
+ for (port = firstport; port < firstport + nports; ++port) {
+ unsigned enab =
+ bits >> (subpriv->dio.intr.first_chan + (port -
+ firstport) * 8) & 0xff, pol =
+ pol_bits >> (subpriv->dio.intr.first_chan +
+ (port - firstport) * 8) & 0xff;
+ /* set enab intrs for this subdev.. */
+ outb(enab,
+ devpriv->asics[asic].iobase + REG_ENAB0 + port);
+ switch_page(dev, asic, PAGE_POL);
+ outb(pol,
+ devpriv->asics[asic].iobase + REG_ENAB0 + port);
+ }
+ }
+ return 0;
+}
+
+static int pcmmio_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
+ if (subpriv->dio.intr.active)
+ pcmmio_stop_intr(dev, s);
+ comedi_spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
+
+ return 0;
+}
+
+/*
+ * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
+ */
+static int
+pcmmio_inttrig_start_intr(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trignum)
+{
+ unsigned long flags;
+ int event = 0;
+
+ if (trignum != 0)
+ return -EINVAL;
+
+ comedi_spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
+ s->async->inttrig = 0;
+ if (subpriv->dio.intr.active) {
+ event = pcmmio_start_intr(dev, s);
+ }
+ comedi_spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
+
+ if (event) {
+ comedi_event(dev, s);
+ }
+
+ return 1;
+}
+
+/*
+ * 'do_cmd' function for an 'INTERRUPT' subdevice.
+ */
+static int pcmmio_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned long flags;
+ int event = 0;
+
+ comedi_spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
+ subpriv->dio.intr.active = 1;
+
+ /* Set up end of acquisition. */
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ subpriv->dio.intr.continuous = 0;
+ subpriv->dio.intr.stop_count = cmd->stop_arg;
+ break;
+ default:
+ /* TRIG_NONE */
+ subpriv->dio.intr.continuous = 1;
+ subpriv->dio.intr.stop_count = 0;
+ break;
+ }
+
+ /* Set up start of acquisition. */
+ switch (cmd->start_src) {
+ case TRIG_INT:
+ s->async->inttrig = pcmmio_inttrig_start_intr;
+ break;
+ default:
+ /* TRIG_NOW */
+ event = pcmmio_start_intr(dev, s);
+ break;
+ }
+ comedi_spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
+
+ if (event) {
+ comedi_event(dev, s);
+ }
+
+ return 0;
+}
+
+/*
+ * 'do_cmdtest' function for an 'INTERRUPT' subdevice.
+ */
+static int
+pcmmio_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s, struct comedi_cmd * cmd)
+{
+ int err = 0;
+ unsigned int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= (TRIG_NOW | TRIG_INT);
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= (TRIG_COUNT | TRIG_NONE);
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ /* these tests are true if more than one _src bit is set */
+ if ((cmd->start_src & (cmd->start_src - 1)) != 0)
+ err++;
+ if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
+ err++;
+ if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
+ err++;
+ if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
+ err++;
+ if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ /* cmd->start_src == TRIG_NOW || cmd->start_src == TRIG_INT */
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ /* cmd->scan_begin_src == TRIG_EXT */
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+
+ /* cmd->convert_src == TRIG_NOW */
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+
+ /* cmd->scan_end_src == TRIG_COUNT */
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ /* any count allowed */
+ break;
+ case TRIG_NONE:
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ /* if (err) return 4; */
+
+ return 0;
+}
+
+static int adc_wait_ready(unsigned long iobase)
+{
+ unsigned long retry = 100000;
+ while (retry--)
+ if (inb(iobase + 3) & 0x80)
+ return 0;
+ return 1;
+}
+
+/* All this is for AI and AO */
+static int ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ unsigned long iobase = subpriv->iobase;
+
+ /*
+ 1. write the CMD byte (to BASE+2)
+ 2. read junk lo byte (BASE+0)
+ 3. read junk hi byte (BASE+1)
+ 4. (mux settled so) write CMD byte again (BASE+2)
+ 5. read valid lo byte(BASE+0)
+ 6. read valid hi byte(BASE+1)
+
+ Additionally note that the BASE += 4 if the channel >= 8
+ */
+
+ /* convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ unsigned chan = CR_CHAN(insn->chanspec), range =
+ CR_RANGE(insn->chanspec), aref =
+ CR_AREF(insn->chanspec);
+ unsigned char command_byte = 0;
+ unsigned iooffset = 0;
+ short sample, adc_adjust = 0;
+
+ if (chan > 7)
+ chan -= 8, iooffset = 4; /* use the second dword for channels > 7 */
+
+ if (aref != AREF_DIFF) {
+ aref = AREF_GROUND;
+ command_byte |= 1 << 7; /* set bit 7 to indicate single-ended */
+ }
+ if (range < 2)
+ adc_adjust = 0x8000; /* bipolar ranges (-5,5 .. -10,10 need to be adjusted -- that is.. they need to wrap around by adding 0x8000 */
+
+ if (chan % 2) {
+ command_byte |= 1 << 6; /* odd-numbered channels have bit 6 set */
+ }
+
+ /* select the channel, bits 4-5 == chan/2 */
+ command_byte |= ((chan / 2) & 0x3) << 4;
+
+ /* set the range, bits 2-3 */
+ command_byte |= (range & 0x3) << 2;
+
+ /* need to do this twice to make sure mux settled */
+ outb(command_byte, iobase + iooffset + 2); /* chan/range/aref select */
+
+ adc_wait_ready(iobase + iooffset); /* wait for the adc to say it finised the conversion */
+
+ outb(command_byte, iobase + iooffset + 2); /* select the chan/range/aref AGAIN */
+
+ adc_wait_ready(iobase + iooffset);
+
+ sample = inb(iobase + iooffset + 0); /* read data lo byte */
+ sample |= inb(iobase + iooffset + 1) << 8; /* read data hi byte */
+ sample += adc_adjust; /* adjustment .. munge data */
+ data[n] = sample;
+ }
+ /* return the number of samples read/written */
+ return n;
+}
+
+static int ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ for (n = 0; n < insn->n; n++) {
+ unsigned chan = CR_CHAN(insn->chanspec);
+ if (chan < s->n_chan)
+ data[n] = subpriv->ao.shadow_samples[chan];
+ }
+ return n;
+}
+
+static int wait_dac_ready(unsigned long iobase)
+{
+ unsigned long retry = 100000L;
+
+ /* This may seem like an absurd way to handle waiting and violates the
+ "no busy waiting" policy. The fact is that the hardware is
+ normally so fast that we usually only need one time through the loop
+ anyway. The longer timeout is for rare occasions and for detecting
+ non-existant hardware. */
+
+ while (retry--) {
+ if (inb(iobase + 3) & 0x80)
+ return 0;
+
+ }
+ return 1;
+}
+
+static int ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ unsigned iobase = subpriv->iobase, iooffset = 0;
+
+ for (n = 0; n < insn->n; n++) {
+ unsigned chan = CR_CHAN(insn->chanspec), range =
+ CR_RANGE(insn->chanspec);
+ if (chan < s->n_chan) {
+ unsigned char command_byte = 0, range_byte =
+ range & ((1 << 4) - 1);
+ if (chan >= 4)
+ chan -= 4, iooffset += 4;
+ /* set the range.. */
+ outb(range_byte, iobase + iooffset + 0);
+ outb(0, iobase + iooffset + 1);
+
+ /* tell it to begin */
+ command_byte = (chan << 1) | 0x60;
+ outb(command_byte, iobase + iooffset + 2);
+
+ wait_dac_ready(iobase + iooffset);
+
+ outb(data[n] & 0xff, iobase + iooffset + 0); /* low order byte */
+ outb((data[n] >> 8) & 0xff, iobase + iooffset + 1); /* high order byte */
+ command_byte = 0x70 | (chan << 1); /* set bit 4 of command byte to indicate data is loaded and trigger conversion */
+ /* trigger converion */
+ outb(command_byte, iobase + iooffset + 2);
+
+ wait_dac_ready(iobase + iooffset);
+
+ subpriv->ao.shadow_samples[chan] = data[n]; /* save to shadow register for ao_rinsn */
+ }
+ }
+ return n;
+}
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_INITCLEANUP(driver);
diff --git a/drivers/staging/comedi/drivers/pcmuio.c b/drivers/staging/comedi/drivers/pcmuio.c
new file mode 100644
index 000000000000..4e7d8b6327fd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcmuio.c
@@ -0,0 +1,1101 @@
+/*
+ comedi/drivers/pcmuio.c
+ Driver for Winsystems PC-104 based 48-channel and 96-channel DIO boards.
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2006 Calin A. Culianu <calin@ajvar.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+Driver: pcmuio
+Description: A driver for the PCM-UIO48A and PCM-UIO96A boards from Winsystems.
+Devices: [Winsystems] PCM-UIO48A (pcmuio48), PCM-UIO96A (pcmuio96)
+Author: Calin Culianu <calin@ajvar.org>
+Updated: Fri, 13 Jan 2006 12:01:01 -0500
+Status: works
+
+A driver for the relatively straightforward-to-program PCM-UIO48A and
+PCM-UIO96A boards from Winsystems. These boards use either one or two
+(in the 96-DIO version) WS16C48 ASIC HighDensity I/O Chips (HDIO).
+This chip is interesting in that each I/O line is individually
+programmable for INPUT or OUTPUT (thus comedi_dio_config can be done
+on a per-channel basis). Also, each chip supports edge-triggered
+interrupts for the first 24 I/O lines. Of course, since the
+96-channel version of the board has two ASICs, it can detect polarity
+changes on up to 48 I/O lines. Since this is essentially an (non-PnP)
+ISA board, I/O Address and IRQ selection are done through jumpers on
+the board. You need to pass that information to this driver as the
+first and second comedi_config option, respectively. Note that the
+48-channel version uses 16 bytes of IO memory and the 96-channel
+version uses 32-bytes (in case you are worried about conflicts). The
+48-channel board is split into two 24-channel comedi subdevices.
+The 96-channel board is split into 4 24-channel DIO subdevices.
+
+Note that IRQ support has been added, but it is untested.
+
+To use edge-detection IRQ support, pass the IRQs of both ASICS
+(for the 96 channel version) or just 1 ASIC (for 48-channel version).
+Then, use use comedi_commands with TRIG_NOW.
+Your callback will be called each time an edge is triggered, and the data
+values will be two sample_t's, which should be concatenated to form one
+32-bit unsigned int. This value is the mask of channels that had
+edges detected from your channel list. Note that the bits positions
+in the mask correspond to positions in your chanlist when you specified
+the command and *not* channel id's!
+
+To set the polarity of the edge-detection interrupts pass a nonzero value for
+either CR_RANGE or CR_AREF for edge-up polarity, or a zero value for both
+CR_RANGE and CR_AREF if you want edge-down polarity.
+
+In the 48-channel version:
+
+On subdev 0, the first 24 channels channels are edge-detect channels.
+
+In the 96-channel board you have the collowing channels that can do edge detection:
+
+subdev 0, channels 0-24 (first 24 channels of 1st ASIC)
+subdev 2, channels 0-24 (first 24 channels of 2nd ASIC)
+
+Configuration Options:
+ [0] - I/O port base address
+ [1] - IRQ (for first ASIC, or first 24 channels)
+ [2] - IRQ for second ASIC (pcmuio96 only - IRQ for chans 48-72 .. can be the same as first irq!)
+*/
+
+#include "../comedidev.h"
+
+#include <linux/pci.h> /* for PCI devices */
+
+#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) )
+#define CHANS_PER_PORT 8
+#define PORTS_PER_ASIC 6
+#define INTR_PORTS_PER_ASIC 3
+#define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
+#define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
+#define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
+#define INTR_CHANS_PER_ASIC 24
+#define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
+#define MAX_DIO_CHANS (PORTS_PER_ASIC*2*CHANS_PER_PORT)
+#define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC)
+#define SDEV_NO ((int)(s - dev->subdevices))
+#define CALC_N_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
+/* IO Memory sizes */
+#define ASIC_IOSIZE (0x10)
+#define PCMUIO48_IOSIZE ASIC_IOSIZE
+#define PCMUIO96_IOSIZE (ASIC_IOSIZE*2)
+
+/* Some offsets - these are all in the 16byte IO memory offset from
+ the base address. Note that there is a paging scheme to swap out
+ offsets 0x8-0xA using the PAGELOCK register. See the table below.
+
+ Register(s) Pages R/W? Description
+ --------------------------------------------------------------
+ REG_PORTx All R/W Read/Write/Configure IO
+ REG_INT_PENDING All ReadOnly Quickly see which INT_IDx has int.
+ REG_PAGELOCK All WriteOnly Select a page
+ REG_POLx Pg. 1 only WriteOnly Select edge-detection polarity
+ REG_ENABx Pg. 2 only WriteOnly Enable/Disable edge-detect. int.
+ REG_INT_IDx Pg. 3 only R/W See which ports/bits have ints.
+ */
+#define REG_PORT0 0x0
+#define REG_PORT1 0x1
+#define REG_PORT2 0x2
+#define REG_PORT3 0x3
+#define REG_PORT4 0x4
+#define REG_PORT5 0x5
+#define REG_INT_PENDING 0x6
+#define REG_PAGELOCK 0x7 /* page selector register, upper 2 bits select a page
+ and bits 0-5 are used to 'lock down' a particular
+ port above to make it readonly. */
+#define REG_POL0 0x8
+#define REG_POL1 0x9
+#define REG_POL2 0xA
+#define REG_ENAB0 0x8
+#define REG_ENAB1 0x9
+#define REG_ENAB2 0xA
+#define REG_INT_ID0 0x8
+#define REG_INT_ID1 0x9
+#define REG_INT_ID2 0xA
+
+#define NUM_PAGED_REGS 3
+#define NUM_PAGES 4
+#define FIRST_PAGED_REG 0x8
+#define REG_PAGE_BITOFFSET 6
+#define REG_LOCK_BITOFFSET 0
+#define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
+#define REG_LOCK_MASK ~(REG_PAGE_MASK)
+#define PAGE_POL 1
+#define PAGE_ENAB 2
+#define PAGE_INT_ID 3
+
+/*
+ * Board descriptions for two imaginary boards. Describing the
+ * boards in this way is optional, and completely driver-dependent.
+ * Some drivers use arrays such as this, other do not.
+ */
+struct pcmuio_board {
+ const char *name;
+ const int num_asics;
+ const int num_channels_per_port;
+ const int num_ports;
+};
+
+static const struct pcmuio_board pcmuio_boards[] = {
+ {
+ name: "pcmuio48",
+ num_asics:1,
+ num_ports:6,
+ },
+ {
+ name: "pcmuio96",
+ num_asics:2,
+ num_ports:12,
+ },
+};
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct pcmuio_board *)dev->board_ptr)
+
+/* this structure is for data unique to this subdevice. */
+struct pcmuio_subdev_private {
+ /* mapping of halfwords (bytes) in port/chanarray to iobase */
+ unsigned long iobases[PORTS_PER_SUBDEV];
+
+ /* The below is only used for intr subdevices */
+ struct {
+ int asic; /* if non-negative, this subdev has an interrupt asic */
+ int first_chan; /* if nonnegative, the first channel id for
+ interrupts. */
+ int num_asic_chans; /* the number of asic channels in this subdev
+ that have interrutps */
+ int asic_chan; /* if nonnegative, the first channel id with
+ respect to the asic that has interrupts */
+ int enabled_mask; /* subdev-relative channel mask for channels
+ we are interested in */
+ int active;
+ int stop_count;
+ int continuous;
+ spinlock_t spinlock;
+ } intr;
+};
+
+/* 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 struct comedi_device struct. */
+struct pcmuio_private {
+ struct {
+ unsigned char pagelock; /* current page and lock */
+ unsigned char pol[NUM_PAGED_REGS]; /* shadow of POLx registers */
+ unsigned char enab[NUM_PAGED_REGS]; /* shadow of ENABx registers */
+ int num;
+ unsigned long iobase;
+ unsigned int irq;
+ spinlock_t spinlock;
+ } asics[MAX_ASICS];
+ struct pcmuio_subdev_private *sprivs;
+};
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((struct pcmuio_private *)dev->private)
+#define subpriv ((struct pcmuio_subdev_private *)s->private)
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int pcmuio_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int pcmuio_detach(struct comedi_device * dev);
+
+static struct comedi_driver driver = {
+ driver_name:"pcmuio",
+ module:THIS_MODULE,
+ attach:pcmuio_attach,
+ detach:pcmuio_detach,
+/* It is not necessary to implement the following members if you are
+ * writing a driver for a ISA PnP or PCI card */
+ /* Most drivers will support multiple types of boards by
+ * having an array of board structures. These were defined
+ * in pcmuio_boards[] above. Note that the element 'name'
+ * was first in the structure -- Comedi uses this fact to
+ * extract the name of the board without knowing any details
+ * about the structure except for its length.
+ * When a device is attached (by comedi_config), the name
+ * of the device is given to Comedi, and Comedi tries to
+ * match it by going through the list of board names. If
+ * there is a match, the address of the pointer is put
+ * into dev->board_ptr and driver->attach() is called.
+ *
+ * Note that these are not necessary if you can determine
+ * the type of board in software. ISA PnP, PCI, and PCMCIA
+ * devices are such boards.
+ */
+ board_name:&pcmuio_boards[0].name,
+ offset:sizeof(struct pcmuio_board),
+ num_names:sizeof(pcmuio_boards) / sizeof(struct pcmuio_board),
+};
+
+static int pcmuio_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int pcmuio_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static irqreturn_t interrupt_pcmuio(int irq, void *d PT_REGS_ARG);
+static void pcmuio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
+static int pcmuio_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
+static int pcmuio_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
+static int pcmuio_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+
+/* some helper functions to deal with specifics of this device's registers */
+static void init_asics(struct comedi_device * dev); /* sets up/clears ASIC chips to defaults */
+static void switch_page(struct comedi_device * dev, int asic, int page);
+#ifdef notused
+static void lock_port(struct comedi_device * dev, int asic, int port);
+static void unlock_port(struct comedi_device * dev, int asic, int port);
+#endif
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int pcmuio_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0;
+ unsigned long iobase;
+ unsigned int irq[MAX_ASICS];
+
+ iobase = it->options[0];
+ irq[0] = it->options[1];
+ irq[1] = it->options[2];
+
+ printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
+ iobase);
+
+ dev->iobase = iobase;
+
+ if (!iobase || !request_region(iobase,
+ thisboard->num_asics * ASIC_IOSIZE,
+ driver.driver_name)) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+
+/*
+ * Initialize dev->board_name. Note that we can use the "thisboard"
+ * macro now, since we just initialized it in the last line.
+ */
+ dev->board_name = thisboard->name;
+
+/*
+ * Allocate the private structure area. alloc_private() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_private(dev, sizeof(struct pcmuio_private)) < 0) {
+ printk("cannot allocate private data structure\n");
+ return -ENOMEM;
+ }
+
+ for (asic = 0; asic < MAX_ASICS; ++asic) {
+ devpriv->asics[asic].num = asic;
+ devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE;
+ devpriv->asics[asic].irq = 0; /* this gets actually set at the end of
+ this function when we
+ comedi_request_irqs */
+ spin_lock_init(&devpriv->asics[asic].spinlock);
+ }
+
+ chans_left = CHANS_PER_ASIC * thisboard->num_asics;
+ n_subdevs = CALC_N_SUBDEVS(chans_left);
+ devpriv->sprivs =
+ kcalloc(n_subdevs, sizeof(struct pcmuio_subdev_private), GFP_KERNEL);
+ if (!devpriv->sprivs) {
+ printk("cannot allocate subdevice private data structures\n");
+ return -ENOMEM;
+ }
+ /*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ *
+ * Allocate 2 subdevs (32 + 16 DIO lines) or 3 32 DIO subdevs for the
+ * 96-channel version of the board.
+ */
+ if (alloc_subdevices(dev, n_subdevs) < 0) {
+ printk("cannot allocate subdevice data structures\n");
+ return -ENOMEM;
+ }
+
+ port = 0;
+ asic = 0;
+ for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
+ int byte_no;
+
+ s = dev->subdevices + sdev_no;
+ s->private = devpriv->sprivs + sdev_no;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->type = COMEDI_SUBD_DIO;
+ s->insn_bits = pcmuio_dio_insn_bits;
+ s->insn_config = pcmuio_dio_insn_config;
+ s->n_chan = MIN(chans_left, MAX_CHANS_PER_SUBDEV);
+ subpriv->intr.asic = -1;
+ subpriv->intr.first_chan = -1;
+ subpriv->intr.asic_chan = -1;
+ subpriv->intr.num_asic_chans = -1;
+ subpriv->intr.active = 0;
+ s->len_chanlist = 1;
+
+ /* save the ioport address for each 'port' of 8 channels in the
+ subdevice */
+ for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
+ if (port >= PORTS_PER_ASIC) {
+ port = 0;
+ ++asic;
+ thisasic_chanct = 0;
+ }
+ subpriv->iobases[byte_no] =
+ devpriv->asics[asic].iobase + port;
+
+ if (thisasic_chanct <
+ CHANS_PER_PORT * INTR_PORTS_PER_ASIC
+ && subpriv->intr.asic < 0) {
+ /* this is an interrupt subdevice, so setup the struct */
+ subpriv->intr.asic = asic;
+ subpriv->intr.active = 0;
+ subpriv->intr.stop_count = 0;
+ subpriv->intr.first_chan = byte_no * 8;
+ subpriv->intr.asic_chan = thisasic_chanct;
+ subpriv->intr.num_asic_chans =
+ s->n_chan - subpriv->intr.first_chan;
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->cancel = pcmuio_cancel;
+ s->do_cmd = pcmuio_cmd;
+ s->do_cmdtest = pcmuio_cmdtest;
+ s->len_chanlist = subpriv->intr.num_asic_chans;
+ }
+ thisasic_chanct += CHANS_PER_PORT;
+ }
+ spin_lock_init(&subpriv->intr.spinlock);
+
+ chans_left -= s->n_chan;
+
+ if (!chans_left) {
+ asic = 0; /* reset the asic to our first asic, to do intr subdevs */
+ port = 0;
+ }
+
+ }
+
+ init_asics(dev); /* clear out all the registers, basically */
+
+ for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
+ if (irq[asic]
+ && comedi_request_irq(irq[asic], interrupt_pcmuio,
+ IRQF_SHARED, thisboard->name, dev)) {
+ int i;
+ /* unroll the allocated irqs.. */
+ for (i = asic - 1; i >= 0; --i) {
+ comedi_free_irq(irq[i], dev);
+ devpriv->asics[i].irq = irq[i] = 0;
+ }
+ irq[asic] = 0;
+ }
+ devpriv->asics[asic].irq = irq[asic];
+ }
+
+ dev->irq = irq[0]; /* grr.. wish comedi dev struct supported multiple
+ irqs.. */
+
+ if (irq[0]) {
+ printk("irq: %u ", irq[0]);
+ if (irq[1] && thisboard->num_asics == 2)
+ printk("second ASIC irq: %u ", irq[1]);
+ } else {
+ printk("(IRQ mode disabled) ");
+ }
+
+ printk("attached\n");
+
+ return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int pcmuio_detach(struct comedi_device * dev)
+{
+ int i;
+
+ printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
+ if (dev->iobase)
+ release_region(dev->iobase, ASIC_IOSIZE * thisboard->num_asics);
+
+ for (i = 0; i < MAX_ASICS; ++i) {
+ if (devpriv->asics[i].irq)
+ comedi_free_irq(devpriv->asics[i].irq, dev);
+ }
+
+ if (devpriv && devpriv->sprivs)
+ kfree(devpriv->sprivs);
+
+ return 0;
+}
+
+/* DIO devices are slightly special. Although it is possible to
+ * implement the insn_read/insn_write interface, it is much more
+ * useful to applications if you implement the insn_bits interface.
+ * This allows packed reading/writing of the DIO channels. The
+ * comedi core can convert between insn_bits and insn_read/write */
+static int pcmuio_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int byte_no;
+ if (insn->n != 2)
+ return -EINVAL;
+
+ /* NOTE:
+ reading a 0 means this channel was high
+ writine a 0 sets the channel high
+ reading a 1 means this channel was low
+ writing a 1 means set this channel low
+
+ Therefore everything is always inverted. */
+
+ /* The insn data is a mask in data[0] and the new data
+ * in data[1], each channel cooresponding to a bit. */
+
+#ifdef DAMMIT_ITS_BROKEN
+ /* DEBUG */
+ printk("write mask: %08x data: %08x\n", data[0], data[1]);
+#endif
+
+ s->state = 0;
+
+ for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
+ /* address of 8-bit port */
+ unsigned long ioaddr = subpriv->iobases[byte_no],
+ /* bit offset of port in 32-bit doubleword */
+ offset = byte_no * 8;
+ /* this 8-bit port's data */
+ unsigned char byte = 0,
+ /* The write mask for this port (if any) */
+ write_mask_byte = (data[0] >> offset) & 0xff,
+ /* The data byte for this port */
+ data_byte = (data[1] >> offset) & 0xff;
+
+ byte = inb(ioaddr); /* read all 8-bits for this port */
+
+#ifdef DAMMIT_ITS_BROKEN
+ /* DEBUG */
+ printk("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ", byte_no, (unsigned)write_mask_byte, (unsigned)data_byte, offset, ioaddr, (unsigned)byte);
+#endif
+
+ if (write_mask_byte) {
+ /* this byte has some write_bits -- so set the output lines */
+ byte &= ~write_mask_byte; /* clear bits for write mask */
+ byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */
+ /* Write out the new digital output state */
+ outb(byte, ioaddr);
+ }
+#ifdef DAMMIT_ITS_BROKEN
+ /* DEBUG */
+ printk("data_out_byte %02x\n", (unsigned)byte);
+#endif
+ /* save the digital input lines for this byte.. */
+ s->state |= ((unsigned int)byte) << offset;
+ }
+
+ /* now return the DIO lines to data[1] - note they came inverted! */
+ data[1] = ~s->state;
+
+#ifdef DAMMIT_ITS_BROKEN
+ /* DEBUG */
+ printk("s->state %08x data_out %08x\n", s->state, data[1]);
+#endif
+
+ return 2;
+}
+
+/* The input or output configuration of each digital line is
+ * configured by a special insn_config instruction. chanspec
+ * contains the channel to be changed, and data[0] contains the
+ * value COMEDI_INPUT or COMEDI_OUTPUT. */
+static int pcmuio_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
+ chan % 8;
+ unsigned long ioaddr;
+ unsigned char byte;
+
+ /* Compute ioaddr for this channel */
+ ioaddr = subpriv->iobases[byte_no];
+
+ /* NOTE:
+ writing a 0 an IO channel's bit sets the channel to INPUT
+ and pulls the line high as well
+
+ writing a 1 to an IO channel's bit pulls the line low
+
+ All channels are implicitly always in OUTPUT mode -- but when
+ they are high they can be considered to be in INPUT mode..
+
+ Thus, we only force channels low if the config request was INPUT,
+ otherwise we do nothing to the hardware. */
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ /* save to io_bits -- don't actually do anything since
+ all input channels are also output channels... */
+ s->io_bits |= 1 << chan;
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ /* write a 0 to the actual register representing the channel
+ to set it to 'input'. 0 means "float high". */
+ byte = inb(ioaddr);
+ byte &= ~(1 << bit_no);
+ /**< set input channel to '0' */
+
+ /* write out byte -- this is the only time we actually affect the
+ hardware as all channels are implicitly output -- but input
+ channels are set to float-high */
+ outb(byte, ioaddr);
+
+ /* save to io_bits */
+ s->io_bits &= ~(1 << chan);
+ break;
+
+ case INSN_CONFIG_DIO_QUERY:
+ /* retreive from shadow register */
+ data[1] =
+ (s->
+ io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+ break;
+
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ return insn->n;
+}
+
+static void init_asics(struct comedi_device * dev)
+{ /* sets up an
+ ASIC chip to defaults */
+ int asic;
+
+ for (asic = 0; asic < thisboard->num_asics; ++asic) {
+ int port, page;
+ unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE;
+
+ switch_page(dev, asic, 0); /* switch back to page 0 */
+
+ /* first, clear all the DIO port bits */
+ for (port = 0; port < PORTS_PER_ASIC; ++port)
+ outb(0, baseaddr + REG_PORT0 + port);
+
+ /* Next, clear all the paged registers for each page */
+ for (page = 1; page < NUM_PAGES; ++page) {
+ int reg;
+ /* now clear all the paged registers */
+ switch_page(dev, asic, page);
+ for (reg = FIRST_PAGED_REG;
+ reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
+ outb(0, baseaddr + reg);
+ }
+
+ /* DEBUG set rising edge interrupts on port0 of both asics */
+ /*switch_page(dev, asic, PAGE_POL);
+ outb(0xff, baseaddr + REG_POL0);
+ switch_page(dev, asic, PAGE_ENAB);
+ outb(0xff, baseaddr + REG_ENAB0); */
+ /* END DEBUG */
+
+ switch_page(dev, asic, 0); /* switch back to default page 0 */
+
+ }
+}
+
+static void switch_page(struct comedi_device * dev, int asic, int page)
+{
+ if (asic < 0 || asic >= thisboard->num_asics)
+ return; /* paranoia */
+ if (page < 0 || page >= NUM_PAGES)
+ return; /* more paranoia */
+
+ devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
+ devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
+
+ /* now write out the shadow register */
+ outb(devpriv->asics[asic].pagelock,
+ dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
+}
+
+#ifdef notused
+static void lock_port(struct comedi_device * dev, int asic, int port)
+{
+ if (asic < 0 || asic >= thisboard->num_asics)
+ return; /* paranoia */
+ if (port < 0 || port >= PORTS_PER_ASIC)
+ return; /* more paranoia */
+
+ devpriv->asics[asic].pagelock |= 0x1 << port;
+ /* now write out the shadow register */
+ outb(devpriv->asics[asic].pagelock,
+ dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
+}
+
+static void unlock_port(struct comedi_device * dev, int asic, int port)
+{
+ if (asic < 0 || asic >= thisboard->num_asics)
+ return; /* paranoia */
+ if (port < 0 || port >= PORTS_PER_ASIC)
+ return; /* more paranoia */
+ devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
+ /* now write out the shadow register */
+ outb(devpriv->asics[asic].pagelock,
+ dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
+}
+#endif /* notused */
+
+static irqreturn_t interrupt_pcmuio(int irq, void *d PT_REGS_ARG)
+{
+ int asic, got1 = 0;
+ struct comedi_device *dev = (struct comedi_device *) d;
+
+ for (asic = 0; asic < MAX_ASICS; ++asic) {
+ if (irq == devpriv->asics[asic].irq) {
+ unsigned long flags;
+ unsigned triggered = 0;
+ unsigned long iobase = devpriv->asics[asic].iobase;
+ /* it is an interrupt for ASIC #asic */
+ unsigned char int_pend;
+
+ comedi_spin_lock_irqsave(&devpriv->asics[asic].spinlock,
+ flags);
+
+ int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
+
+ if (int_pend) {
+ int port;
+ for (port = 0; port < INTR_PORTS_PER_ASIC;
+ ++port) {
+ if (int_pend & (0x1 << port)) {
+ unsigned char
+ io_lines_with_edges = 0;
+ switch_page(dev, asic,
+ PAGE_INT_ID);
+ io_lines_with_edges =
+ inb(iobase +
+ REG_INT_ID0 + port);
+
+ if (io_lines_with_edges)
+ /* clear pending interrupt */
+ outb(0, iobase +
+ REG_INT_ID0 +
+ port);
+
+ triggered |=
+ io_lines_with_edges <<
+ port * 8;
+ }
+ }
+
+ ++got1;
+ }
+
+ comedi_spin_unlock_irqrestore(&devpriv->asics[asic].
+ spinlock, flags);
+
+ if (triggered) {
+ struct comedi_subdevice *s;
+ /* TODO here: dispatch io lines to subdevs with commands.. */
+ printk("PCMUIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n", irq, asic, triggered);
+ for (s = dev->subdevices;
+ s < dev->subdevices + dev->n_subdevices;
+ ++s) {
+ if (subpriv->intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */
+ unsigned long flags;
+ unsigned oldevents;
+
+ comedi_spin_lock_irqsave
+ (&subpriv->intr.
+ spinlock, flags);
+
+ oldevents = s->async->events;
+
+ if (subpriv->intr.active) {
+ unsigned mytrig =
+ ((triggered >>
+ subpriv->
+ intr.
+ asic_chan)
+ & ((0x1 << subpriv->intr.num_asic_chans) - 1)) << subpriv->intr.first_chan;
+ if (mytrig & subpriv->
+ intr.
+ enabled_mask) {
+ unsigned int val =
+ 0;
+ unsigned int n,
+ ch, len;
+
+ len = s->async->
+ cmd.
+ chanlist_len;
+ for (n = 0;
+ n < len;
+ n++) {
+ ch = CR_CHAN(s->async->cmd.chanlist[n]);
+ if (mytrig & (1U << ch)) {
+ val |= (1U << n);
+ }
+ }
+ /* Write the scan to the buffer. */
+ if (comedi_buf_put(s->async, ((short *) & val)[0])
+ &&
+ comedi_buf_put
+ (s->async, ((short *) & val)[1])) {
+ s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
+ } else {
+ /* Overflow! Stop acquisition!! */
+ /* TODO: STOP_ACQUISITION_CALL_HERE!! */
+ pcmuio_stop_intr
+ (dev,
+ s);
+ }
+
+ /* Check for end of acquisition. */
+ if (!subpriv->
+ intr.
+ continuous)
+ {
+ /* stop_src == TRIG_COUNT */
+ if (subpriv->intr.stop_count > 0) {
+ subpriv->
+ intr.
+ stop_count--;
+ if (subpriv->intr.stop_count == 0) {
+ s->async->events |= COMEDI_CB_EOA;
+ /* TODO: STOP_ACQUISITION_CALL_HERE!! */
+ pcmuio_stop_intr
+ (dev,
+ s);
+ }
+ }
+ }
+ }
+ }
+
+ comedi_spin_unlock_irqrestore
+ (&subpriv->intr.
+ spinlock, flags);
+
+ if (oldevents !=
+ s->async->events) {
+ comedi_event(dev, s);
+ }
+
+ }
+
+ }
+ }
+
+ }
+ }
+ if (!got1)
+ return IRQ_NONE; /* interrupt from other source */
+ return IRQ_HANDLED;
+}
+
+static void pcmuio_stop_intr(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ int nports, firstport, asic, port;
+
+ if ((asic = subpriv->intr.asic) < 0)
+ return; /* not an interrupt subdev */
+
+ subpriv->intr.enabled_mask = 0;
+ subpriv->intr.active = 0;
+ s->async->inttrig = 0;
+ nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
+ firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
+ switch_page(dev, asic, PAGE_ENAB);
+ for (port = firstport; port < firstport + nports; ++port) {
+ /* disable all intrs for this subdev.. */
+ outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
+ }
+}
+
+static int pcmuio_start_intr(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) {
+ /* An empty acquisition! */
+ s->async->events |= COMEDI_CB_EOA;
+ subpriv->intr.active = 0;
+ return 1;
+ } else {
+ unsigned bits = 0, pol_bits = 0, n;
+ int nports, firstport, asic, port;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if ((asic = subpriv->intr.asic) < 0)
+ return 1; /* not an interrupt
+ subdev */
+ subpriv->intr.enabled_mask = 0;
+ subpriv->intr.active = 1;
+ nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
+ firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
+ if (cmd->chanlist) {
+ for (n = 0; n < cmd->chanlist_len; n++) {
+ bits |= (1U << CR_CHAN(cmd->chanlist[n]));
+ pol_bits |= (CR_AREF(cmd->chanlist[n])
+ || CR_RANGE(cmd->chanlist[n]) ? 1U : 0U)
+ << CR_CHAN(cmd->chanlist[n]);
+ }
+ }
+ bits &= ((0x1 << subpriv->intr.num_asic_chans) -
+ 1) << subpriv->intr.first_chan;
+ subpriv->intr.enabled_mask = bits;
+
+ switch_page(dev, asic, PAGE_ENAB);
+ for (port = firstport; port < firstport + nports; ++port) {
+ unsigned enab =
+ bits >> (subpriv->intr.first_chan + (port -
+ firstport) * 8) & 0xff, pol =
+ pol_bits >> (subpriv->intr.first_chan + (port -
+ firstport) * 8) & 0xff;
+ /* set enab intrs for this subdev.. */
+ outb(enab,
+ devpriv->asics[asic].iobase + REG_ENAB0 + port);
+ switch_page(dev, asic, PAGE_POL);
+ outb(pol,
+ devpriv->asics[asic].iobase + REG_ENAB0 + port);
+ }
+ }
+ return 0;
+}
+
+static int pcmuio_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&subpriv->intr.spinlock, flags);
+ if (subpriv->intr.active)
+ pcmuio_stop_intr(dev, s);
+ comedi_spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
+
+ return 0;
+}
+
+/*
+ * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
+ */
+static int
+pcmuio_inttrig_start_intr(struct comedi_device * dev, struct comedi_subdevice * s,
+ unsigned int trignum)
+{
+ unsigned long flags;
+ int event = 0;
+
+ if (trignum != 0)
+ return -EINVAL;
+
+ comedi_spin_lock_irqsave(&subpriv->intr.spinlock, flags);
+ s->async->inttrig = 0;
+ if (subpriv->intr.active) {
+ event = pcmuio_start_intr(dev, s);
+ }
+ comedi_spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
+
+ if (event) {
+ comedi_event(dev, s);
+ }
+
+ return 1;
+}
+
+/*
+ * 'do_cmd' function for an 'INTERRUPT' subdevice.
+ */
+static int pcmuio_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned long flags;
+ int event = 0;
+
+ comedi_spin_lock_irqsave(&subpriv->intr.spinlock, flags);
+ subpriv->intr.active = 1;
+
+ /* Set up end of acquisition. */
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ subpriv->intr.continuous = 0;
+ subpriv->intr.stop_count = cmd->stop_arg;
+ break;
+ default:
+ /* TRIG_NONE */
+ subpriv->intr.continuous = 1;
+ subpriv->intr.stop_count = 0;
+ break;
+ }
+
+ /* Set up start of acquisition. */
+ switch (cmd->start_src) {
+ case TRIG_INT:
+ s->async->inttrig = pcmuio_inttrig_start_intr;
+ break;
+ default:
+ /* TRIG_NOW */
+ event = pcmuio_start_intr(dev, s);
+ break;
+ }
+ comedi_spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
+
+ if (event) {
+ comedi_event(dev, s);
+ }
+
+ return 0;
+}
+
+/*
+ * 'do_cmdtest' function for an 'INTERRUPT' subdevice.
+ */
+static int
+pcmuio_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s, struct comedi_cmd * cmd)
+{
+ int err = 0;
+ unsigned int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= (TRIG_NOW | TRIG_INT);
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= (TRIG_COUNT | TRIG_NONE);
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ /* these tests are true if more than one _src bit is set */
+ if ((cmd->start_src & (cmd->start_src - 1)) != 0)
+ err++;
+ if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
+ err++;
+ if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
+ err++;
+ if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
+ err++;
+ if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ /* cmd->start_src == TRIG_NOW || cmd->start_src == TRIG_INT */
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+
+ /* cmd->scan_begin_src == TRIG_EXT */
+ if (cmd->scan_begin_arg != 0) {
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+
+ /* cmd->convert_src == TRIG_NOW */
+ if (cmd->convert_arg != 0) {
+ cmd->convert_arg = 0;
+ err++;
+ }
+
+ /* cmd->scan_end_src == TRIG_COUNT */
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ /* any count allowed */
+ break;
+ case TRIG_NONE:
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ /* if (err) return 4; */
+
+ return 0;
+}
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_INITCLEANUP(driver);
diff --git a/drivers/staging/comedi/drivers/plx9052.h b/drivers/staging/comedi/drivers/plx9052.h
new file mode 100644
index 000000000000..5894739ff426
--- /dev/null
+++ b/drivers/staging/comedi/drivers/plx9052.h
@@ -0,0 +1,86 @@
+/*
+ comedi/drivers/plx9052.h
+ Definitions for the PLX-9052 PCI interface chip
+
+ Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef _PLX9052_H_
+#define _PLX9052_H_
+
+/*
+ * PLX PCI9052 INTCSR register.
+ */
+#define PLX9052_INTCSR 0x4C /* Offset in Local Configuration Registers */
+/* Local Interrupt 1 Enable */
+#define PLX9052_INTCSR_LI1ENAB_MASK 0x0001
+#define PLX9052_INTCSR_LI1ENAB_DISABLED 0x0000
+#define PLX9052_INTCSR_LI1ENAB_ENABLED 0x0001
+/* Local Interrupt 1 Polarity */
+#define PLX9052_INTCSR_LI1POL_MASK 0x0002
+#define PLX9052_INTCSR_LI1POL_LOW 0x0000
+#define PLX9052_INTCSR_LI1POL_HIGH 0x0002
+/* Local Interrupt 1 Status (read-only) */
+#define PLX9052_INTCSR_LI1STAT_MASK 0x0004
+#define PLX9052_INTCSR_LI1STAT_INACTIVE 0x0000
+#define PLX9052_INTCSR_LI1STAT_ACTIVE 0x0004
+/* Local Interrupt 2 Enable */
+#define PLX9052_INTCSR_LI2ENAB_MASK 0x0008
+#define PLX9052_INTCSR_LI2ENAB_DISABLED 0x0000
+#define PLX9052_INTCSR_LI2ENAB_ENABLED 0x0008
+/* Local Interrupt 2 Polarity */
+#define PLX9052_INTCSR_LI2POL_MASK 0x0010
+#define PLX9052_INTCSR_LI2POL_LOW 0x0000
+#define PLX9052_INTCSR_LI2POL_HIGH 0x0010
+/* Local Interrupt 2 Status (read-only) */
+#define PLX9052_INTCSR_LI2STAT_MASK 0x0020
+#define PLX9052_INTCSR_LI2STAT_INACTIVE 0x0000
+#define PLX9052_INTCSR_LI2STAT_ACTIVE 0x0020
+/* PCI Interrupt Enable */
+#define PLX9052_INTCSR_PCIENAB_MASK 0x0040
+#define PLX9052_INTCSR_PCIENAB_DISABLED 0x0000
+#define PLX9052_INTCSR_PCIENAB_ENABLED 0x0040
+/* Software Interrupt */
+#define PLX9052_INTCSR_SOFTINT_MASK 0x0080
+#define PLX9052_INTCSR_SOFTINT_UNASSERTED 0x0000
+#define PLX9052_INTCSR_SOFTINT_ASSERTED 0x0080
+/* Local Interrupt 1 Select Enable */
+#define PLX9052_INTCSR_LI1SEL_MASK 0x0100
+#define PLX9052_INTCSR_LI1SEL_LEVEL 0x0000
+#define PLX9052_INTCSR_LI1SEL_EDGE 0x0100
+/* Local Interrupt 2 Select Enable */
+#define PLX9052_INTCSR_LI2SEL_MASK 0x0200
+#define PLX9052_INTCSR_LI2SEL_LEVEL 0x0000
+#define PLX9052_INTCSR_LI2SEL_EDGE 0x0200
+/* Local Edge Triggerable Interrupt 1 Clear Bit */
+#define PLX9052_INTCSR_LI1CLRINT_MASK 0x0400
+#define PLX9052_INTCSR_LI1CLRINT_UNASSERTED 0x0000
+#define PLX9052_INTCSR_LI1CLRINT_ASSERTED 0x0400
+/* Local Edge Triggerable Interrupt 2 Clear Bit */
+#define PLX9052_INTCSR_LI2CLRINT_MASK 0x0800
+#define PLX9052_INTCSR_LI2CLRINT_UNASSERTED 0x0000
+#define PLX9052_INTCSR_LI2CLRINT_ASSERTED 0x0800
+/* ISA Interface Mode Enable (read-only over PCI bus) */
+#define PLX9052_INTCSR_ISAMODE_MASK 0x1000
+#define PLX9052_INTCSR_ISAMODE_DISABLED 0x0000
+#define PLX9052_INTCSR_ISAMODE_ENABLED 0x1000
+
+#endif /* _PLX9052_H_ */
diff --git a/drivers/staging/comedi/drivers/plx9080.h b/drivers/staging/comedi/drivers/plx9080.h
index a5a1a6808c5d..9231ba802030 100644
--- a/drivers/staging/comedi/drivers/plx9080.h
+++ b/drivers/staging/comedi/drivers/plx9080.h
@@ -27,7 +27,7 @@
#ifndef __COMEDI_PLX9080_H
#define __COMEDI_PLX9080_H
-// descriptor block used for chained dma transfers
+/* descriptor block used for chained dma transfers */
struct plx_dma_desc {
volatile uint32_t pci_start_addr;
volatile uint32_t local_start_addr;
@@ -52,14 +52,14 @@ struct plx_dma_desc {
#define LRNG_ANY32 0x00000000 /* Locate anywhere in 32 bit */
#define LRNG_LT1MB 0x00000002 /* Locate in 1st meg */
#define LRNG_ANY64 0x00000004 /* Locate anywhere in 64 bit */
-#define LRNG_MEM_MASK 0xfffffff0 // bits that specify range for memory io
-#define LRNG_IO_MASK 0xfffffffa // bits that specify range for normal io
+#define LRNG_MEM_MASK 0xfffffff0 /* bits that specify range for memory io */
+#define LRNG_IO_MASK 0xfffffffa /* bits that specify range for normal io */
#define PLX_LAS0MAP_REG 0x0004 /* L, Local Addr Space 0 Remap Register */
#define PLX_LAS1MAP_REG 0x00f4 /* L, Local Addr Space 1 Remap Register */
#define LMAP_EN 0x00000001 /* Enable slave decode */
-#define LMAP_MEM_MASK 0xfffffff0 // bits that specify decode for memory io
-#define LMAP_IO_MASK 0xfffffffa // bits that specify decode bits for normal io
+#define LMAP_MEM_MASK 0xfffffff0 /* bits that specify decode for memory io */
+#define LMAP_IO_MASK 0xfffffffa /* bits that specify decode bits for normal io */
/* Mode/Arbitration Register.
*/
@@ -169,7 +169,7 @@ enum bigend_bits {
#define ICS_AERR 0x00000001 /* Assert LSERR on ABORT */
#define ICS_PERR 0x00000002 /* Assert LSERR on Parity Error */
#define ICS_SERR 0x00000004 /* Generate PCI SERR# */
-#define ICS_MBIE 0x00000008 // mailbox interrupt enable
+#define ICS_MBIE 0x00000008 /* mailbox interrupt enable */
#define ICS_PIE 0x00000100 /* PCI Interrupt Enable */
#define ICS_PDIE 0x00000200 /* PCI Doorbell Interrupt Enable */
#define ICS_PAIE 0x00000400 /* PCI Abort Interrupt Enable */
@@ -190,7 +190,7 @@ enum bigend_bits {
#define ICS_TA_DMA0 0x02000000 /* Target Abort - DMA #0 */
#define ICS_TA_DMA1 0x04000000 /* Target Abort - DMA #1 */
#define ICS_TA_RA 0x08000000 /* Target Abort - Retry Timeout */
-#define ICS_MBIA(x) (0x10000000 << ((x) & 0x3)) // mailbox x is active
+#define ICS_MBIA(x) (0x10000000 << ((x) & 0x3)) /* mailbox x is active */
#define PLX_CONTROL_REG 0x006C /* L, EEPROM Cntl & PCI Cmd Codes */
#define CTL_RDMA 0x0000000E /* DMA Read Command */
@@ -208,51 +208,51 @@ enum bigend_bits {
#define CTL_RESET 0x40000000 /* !! Adapter Reset !! */
#define CTL_READY 0x80000000 /* Local Init Done */
-#define PLX_ID_REG 0x70 // hard-coded plx vendor and device ids
+#define PLX_ID_REG 0x70 /* hard-coded plx vendor and device ids */
-#define PLX_REVISION_REG 0x74 // silicon revision
+#define PLX_REVISION_REG 0x74 /* silicon revision */
-#define PLX_DMA0_MODE_REG 0x80 // dma channel 0 mode register
-#define PLX_DMA1_MODE_REG 0x94 // dma channel 0 mode register
+#define PLX_DMA0_MODE_REG 0x80 /* dma channel 0 mode register */
+#define PLX_DMA1_MODE_REG 0x94 /* dma channel 0 mode register */
#define PLX_LOCAL_BUS_16_WIDE_BITS 0x1
#define PLX_LOCAL_BUS_32_WIDE_BITS 0x3
#define PLX_LOCAL_BUS_WIDTH_MASK 0x3
-#define PLX_DMA_EN_READYIN_BIT 0x40 // enable ready in input
-#define PLX_EN_BTERM_BIT 0x80 // enable BTERM# input
-#define PLX_DMA_LOCAL_BURST_EN_BIT 0x100 // enable local burst mode
-#define PLX_EN_CHAIN_BIT 0x200 // enables chaining
-#define PLX_EN_DMA_DONE_INTR_BIT 0x400 // enables interrupt on dma done
-#define PLX_LOCAL_ADDR_CONST_BIT 0x800 // hold local address constant (don't increment)
-#define PLX_DEMAND_MODE_BIT 0x1000 // enables demand-mode for dma transfer
+#define PLX_DMA_EN_READYIN_BIT 0x40 /* enable ready in input */
+#define PLX_EN_BTERM_BIT 0x80 /* enable BTERM# input */
+#define PLX_DMA_LOCAL_BURST_EN_BIT 0x100 /* enable local burst mode */
+#define PLX_EN_CHAIN_BIT 0x200 /* enables chaining */
+#define PLX_EN_DMA_DONE_INTR_BIT 0x400 /* enables interrupt on dma done */
+#define PLX_LOCAL_ADDR_CONST_BIT 0x800 /* hold local address constant (don't increment) */
+#define PLX_DEMAND_MODE_BIT 0x1000 /* enables demand-mode for dma transfer */
#define PLX_EOT_ENABLE_BIT 0x4000
#define PLX_STOP_MODE_BIT 0x8000
-#define PLX_DMA_INTR_PCI_BIT 0x20000 // routes dma interrupt to pci bus (instead of local bus)
+#define PLX_DMA_INTR_PCI_BIT 0x20000 /* routes dma interrupt to pci bus (instead of local bus) */
-#define PLX_DMA0_PCI_ADDRESS_REG 0x84 // pci address that dma transfers start at
+#define PLX_DMA0_PCI_ADDRESS_REG 0x84 /* pci address that dma transfers start at */
#define PLX_DMA1_PCI_ADDRESS_REG 0x98
-#define PLX_DMA0_LOCAL_ADDRESS_REG 0x88 // local address that dma transfers start at
+#define PLX_DMA0_LOCAL_ADDRESS_REG 0x88 /* local address that dma transfers start at */
#define PLX_DMA1_LOCAL_ADDRESS_REG 0x9c
-#define PLX_DMA0_TRANSFER_SIZE_REG 0x8c // number of bytes to transfer (first 23 bits)
+#define PLX_DMA0_TRANSFER_SIZE_REG 0x8c /* number of bytes to transfer (first 23 bits) */
#define PLX_DMA1_TRANSFER_SIZE_REG 0xa0
-#define PLX_DMA0_DESCRIPTOR_REG 0x90 // descriptor pointer register
+#define PLX_DMA0_DESCRIPTOR_REG 0x90 /* descriptor pointer register */
#define PLX_DMA1_DESCRIPTOR_REG 0xa4
-#define PLX_DESC_IN_PCI_BIT 0x1 // descriptor is located in pci space (not local space)
-#define PLX_END_OF_CHAIN_BIT 0x2 // end of chain bit
-#define PLX_INTR_TERM_COUNT 0x4 // interrupt when this descriptor's transfer is finished
-#define PLX_XFER_LOCAL_TO_PCI 0x8 // transfer from local to pci bus (not pci to local)
+#define PLX_DESC_IN_PCI_BIT 0x1 /* descriptor is located in pci space (not local space) */
+#define PLX_END_OF_CHAIN_BIT 0x2 /* end of chain bit */
+#define PLX_INTR_TERM_COUNT 0x4 /* interrupt when this descriptor's transfer is finished */
+#define PLX_XFER_LOCAL_TO_PCI 0x8 /* transfer from local to pci bus (not pci to local) */
-#define PLX_DMA0_CS_REG 0xa8 // command status register
+#define PLX_DMA0_CS_REG 0xa8 /* command status register */
#define PLX_DMA1_CS_REG 0xa9
-#define PLX_DMA_EN_BIT 0x1 // enable dma channel
-#define PLX_DMA_START_BIT 0x2 // start dma transfer
-#define PLX_DMA_ABORT_BIT 0x4 // abort dma transfer
-#define PLX_CLEAR_DMA_INTR_BIT 0x8 // clear dma interrupt
-#define PLX_DMA_DONE_BIT 0x10 // transfer done status bit
+#define PLX_DMA_EN_BIT 0x1 /* enable dma channel */
+#define PLX_DMA_START_BIT 0x2 /* start dma transfer */
+#define PLX_DMA_ABORT_BIT 0x4 /* abort dma transfer */
+#define PLX_CLEAR_DMA_INTR_BIT 0x8 /* clear dma interrupt */
+#define PLX_DMA_DONE_BIT 0x10 /* transfer done status bit */
-#define PLX_DMA0_THRESHOLD_REG 0xb0 // command status register
+#define PLX_DMA0_THRESHOLD_REG 0xb0 /* command status register */
/*
* Accesses near the end of memory can cause the PLX chip
@@ -392,12 +392,12 @@ static inline int plx9080_abort_dma(void *iobase, unsigned int channel)
else
dma_cs_addr = iobase + PLX_DMA0_CS_REG;
- // abort dma transfer if necessary
+ /* abort dma transfer if necessary */
dma_status = readb(dma_cs_addr);
- if ((dma_status & PLX_DMA_EN_BIT) == 0) {
+ if ((dma_status & PLX_DMA_EN_BIT) == 0)
return 0;
- }
- // wait to make sure done bit is zero
+
+ /* wait to make sure done bit is zero */
for (i = 0; (dma_status & PLX_DMA_DONE_BIT) && i < timeout; i++) {
comedi_udelay(1);
dma_status = readb(dma_cs_addr);
@@ -408,9 +408,9 @@ static inline int plx9080_abort_dma(void *iobase, unsigned int channel)
channel);
return -ETIMEDOUT;
}
- // disable and abort channel
+ /* disable and abort channel */
writeb(PLX_DMA_ABORT_BIT, dma_cs_addr);
- // wait for dma done bit
+ /* wait for dma done bit */
dma_status = readb(dma_cs_addr);
for (i = 0; (dma_status & PLX_DMA_DONE_BIT) == 0 && i < timeout; i++) {
comedi_udelay(1);
diff --git a/drivers/staging/comedi/drivers/poc.c b/drivers/staging/comedi/drivers/poc.c
new file mode 100644
index 000000000000..8a0bf6854939
--- /dev/null
+++ b/drivers/staging/comedi/drivers/poc.c
@@ -0,0 +1,247 @@
+/*
+ comedi/drivers/poc.c
+ Mini-drivers for POC (Piece of Crap) boards
+ Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
+ Copyright (C) 2001 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+Driver: poc
+Description: Generic driver for very simple devices
+Author: ds
+Devices: [Keithley Metrabyte] DAC-02 (dac02), [Advantech] PCL-733 (pcl733),
+ PCL-734 (pcl734)
+Updated: Sat, 16 Mar 2002 17:34:48 -0800
+Status: unknown
+
+This driver is indended to support very simple ISA-based devices,
+including:
+ dac02 - Keithley DAC-02 analog output board
+ pcl733 - Advantech PCL-733
+ pcl734 - Advantech PCL-734
+
+Configuration options:
+ [0] - I/O port base
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+static int poc_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int poc_detach(struct comedi_device * dev);
+static int readback_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+static int dac02_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int pcl733_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int pcl734_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+struct boarddef_struct {
+ const char *name;
+ unsigned int iosize;
+ int (*setup) (struct comedi_device *);
+ int type;
+ int n_chan;
+ int n_bits;
+ int (*winsn) (struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *,
+ unsigned int *);
+ int (*rinsn) (struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *,
+ unsigned int *);
+ int (*insnbits) (struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *,
+ unsigned int *);
+ const struct comedi_lrange *range;
+};
+static const struct boarddef_struct boards[] = {
+ {
+ name: "dac02",
+ iosize: 8,
+ //setup: dac02_setup,
+ type: COMEDI_SUBD_AO,
+ n_chan: 2,
+ n_bits: 12,
+ winsn: dac02_ao_winsn,
+ rinsn: readback_insn,
+ range: &range_unknown,
+ },
+ {
+ name: "pcl733",
+ iosize: 4,
+ type: COMEDI_SUBD_DI,
+ n_chan: 32,
+ n_bits: 1,
+ insnbits:pcl733_insn_bits,
+ range: &range_digital,
+ },
+ {
+ name: "pcl734",
+ iosize: 4,
+ type: COMEDI_SUBD_DO,
+ n_chan: 32,
+ n_bits: 1,
+ insnbits:pcl734_insn_bits,
+ range: &range_digital,
+ },
+};
+
+#define n_boards (sizeof(boards)/sizeof(boards[0]))
+#define this_board ((const struct boarddef_struct *)dev->board_ptr)
+
+static struct comedi_driver driver_poc = {
+ driver_name:"poc",
+ module:THIS_MODULE,
+ attach:poc_attach,
+ detach:poc_detach,
+ board_name:&boards[0].name,
+ num_names:n_boards,
+ offset:sizeof(boards[0]),
+};
+
+static int poc_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+ unsigned int iosize;
+
+ iobase = it->options[0];
+ printk("comedi%d: poc: using %s iobase 0x%lx\n", dev->minor,
+ this_board->name, iobase);
+
+ dev->board_name = this_board->name;
+
+ if (iobase == 0) {
+ printk("io base address required\n");
+ return -EINVAL;
+ }
+
+ iosize = this_board->iosize;
+ /* check if io addresses are available */
+ if (!request_region(iobase, iosize, "dac02")) {
+ printk("I/O port conflict: failed to allocate ports 0x%lx to 0x%lx\n", iobase, iobase + iosize - 1);
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ if (alloc_subdevices(dev, 1) < 0)
+ return -ENOMEM;
+ if (alloc_private(dev, sizeof(unsigned int) * this_board->n_chan) < 0)
+ return -ENOMEM;
+
+ /* analog output subdevice */
+ s = dev->subdevices + 0;
+ s->type = this_board->type;
+ s->n_chan = this_board->n_chan;
+ s->maxdata = (1 << this_board->n_bits) - 1;
+ s->range_table = this_board->range;
+ s->insn_write = this_board->winsn;
+ s->insn_read = this_board->rinsn;
+ s->insn_bits = this_board->insnbits;
+ if (s->type == COMEDI_SUBD_AO || s->type == COMEDI_SUBD_DO) {
+ s->subdev_flags = SDF_WRITABLE;
+ }
+
+ return 0;
+}
+
+static int poc_detach(struct comedi_device * dev)
+{
+ /* only free stuff if it has been allocated by _attach */
+ if (dev->iobase)
+ release_region(dev->iobase, this_board->iosize);
+
+ printk("comedi%d: dac02: remove\n", dev->minor);
+
+ return 0;
+}
+
+static int readback_insn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan;
+
+ chan = CR_CHAN(insn->chanspec);
+ data[0] = ((unsigned int *) dev->private)[chan];
+
+ return 1;
+}
+
+/* DAC-02 registers */
+#define DAC02_LSB(a) (2 * a)
+#define DAC02_MSB(a) (2 * a + 1)
+
+static int dac02_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int temp;
+ int chan;
+ int output;
+
+ chan = CR_CHAN(insn->chanspec);
+ ((unsigned int *) dev->private)[chan] = data[0];
+ output = data[0];
+#ifdef wrong
+ // convert to complementary binary if range is bipolar
+ if ((CR_RANGE(insn->chanspec) & 0x2) == 0)
+ output = ~output;
+#endif
+ temp = (output << 4) & 0xf0;
+ outb(temp, dev->iobase + DAC02_LSB(chan));
+ temp = (output >> 4) & 0xff;
+ outb(temp, dev->iobase + DAC02_MSB(chan));
+
+ return 1;
+}
+
+static int pcl733_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ data[1] = inb(dev->iobase + 0);
+ data[1] |= (inb(dev->iobase + 1) << 8);
+ data[1] |= (inb(dev->iobase + 2) << 16);
+ data[1] |= (inb(dev->iobase + 3) << 24);
+
+ return 2;
+}
+
+static int pcl734_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= (data[0] & data[1]);
+ if ((data[0] >> 0) & 0xff)
+ outb((s->state >> 0) & 0xff, dev->iobase + 0);
+ if ((data[0] >> 8) & 0xff)
+ outb((s->state >> 8) & 0xff, dev->iobase + 1);
+ if ((data[0] >> 16) & 0xff)
+ outb((s->state >> 16) & 0xff, dev->iobase + 2);
+ if ((data[0] >> 24) & 0xff)
+ outb((s->state >> 24) & 0xff, dev->iobase + 3);
+ }
+ data[1] = s->state;
+
+ return 2;
+}
+
+COMEDI_INITCLEANUP(driver_poc);
diff --git a/drivers/staging/comedi/drivers/quatech_daqp_cs.c b/drivers/staging/comedi/drivers/quatech_daqp_cs.c
new file mode 100644
index 000000000000..795c4522867f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/quatech_daqp_cs.c
@@ -0,0 +1,1363 @@
+/*======================================================================
+
+ comedi/drivers/quatech_daqp_cs.c
+
+ Quatech DAQP PCMCIA data capture cards COMEDI client driver
+ Copyright (C) 2000, 2003 Brent Baccala <baccala@freesoft.org>
+ The DAQP interface code in this file is released into the public domain.
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+ http://www.comedi.org/
+
+ quatech_daqp_cs.c 1.10
+
+ Documentation for the DAQP PCMCIA cards can be found on Quatech's site:
+
+ ftp://ftp.quatech.com/Manuals/daqp-208.pdf
+
+ This manual is for both the DAQP-208 and the DAQP-308.
+
+ What works:
+
+ - A/D conversion
+ - 8 channels
+ - 4 gain ranges
+ - ground ref or differential
+ - single-shot and timed both supported
+ - D/A conversion, single-shot
+ - digital I/O
+
+ What doesn't:
+
+ - any kind of triggering - external or D/A channel 1
+ - the card's optional expansion board
+ - the card's timer (for anything other than A/D conversion)
+ - D/A update modes other than immediate (i.e, timed)
+ - fancier timing modes
+ - setting card's FIFO buffer thresholds to anything but default
+
+======================================================================*/
+
+/*
+Driver: quatech_daqp_cs
+Description: Quatech DAQP PCMCIA data capture cards
+Author: Brent Baccala <baccala@freesoft.org>
+Status: works
+Devices: [Quatech] DAQP-208 (daqp), DAQP-308
+*/
+
+#include "../comedidev.h"
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+/*
+ All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
+ you do not define PCMCIA_DEBUG at all, all the debug code will be
+ left out. If you compile with PCMCIA_DEBUG=0, the debug code will
+ be present but disabled -- but it can then be enabled for specific
+ modules at load time with a 'pc_debug=#' option to insmod.
+*/
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0644);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version = "quatech_daqp_cs.c 1.10 2003/04/21 (Brent Baccala)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/* Maximum number of separate DAQP devices we'll allow */
+#define MAX_DEV 4
+
+struct local_info_t {
+ struct pcmcia_device *link;
+ dev_node_t node;
+ int stop;
+ int table_index;
+ char board_name[32];
+
+ enum { semaphore, buffer } interrupt_mode;
+
+ struct semaphore eos;
+
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
+ int count;
+};
+
+/* A list of "instances" of the device. */
+
+static struct local_info_t *dev_table[MAX_DEV] = { NULL, /* ... */ };
+
+/* The DAQP communicates with the system through a 16 byte I/O window. */
+
+#define DAQP_FIFO_SIZE 4096
+
+#define DAQP_FIFO 0
+#define DAQP_SCANLIST 1
+#define DAQP_CONTROL 2
+#define DAQP_STATUS 2
+#define DAQP_DIGITAL_IO 3
+#define DAQP_PACER_LOW 4
+#define DAQP_PACER_MID 5
+#define DAQP_PACER_HIGH 6
+#define DAQP_COMMAND 7
+#define DAQP_DA 8
+#define DAQP_TIMER 10
+#define DAQP_AUX 15
+
+#define DAQP_SCANLIST_DIFFERENTIAL 0x4000
+#define DAQP_SCANLIST_GAIN(x) ((x)<<12)
+#define DAQP_SCANLIST_CHANNEL(x) ((x)<<8)
+#define DAQP_SCANLIST_START 0x0080
+#define DAQP_SCANLIST_EXT_GAIN(x) ((x)<<4)
+#define DAQP_SCANLIST_EXT_CHANNEL(x) (x)
+
+#define DAQP_CONTROL_PACER_100kHz 0xc0
+#define DAQP_CONTROL_PACER_1MHz 0x80
+#define DAQP_CONTROL_PACER_5MHz 0x40
+#define DAQP_CONTROL_PACER_EXTERNAL 0x00
+#define DAQP_CONTORL_EXPANSION 0x20
+#define DAQP_CONTROL_EOS_INT_ENABLE 0x10
+#define DAQP_CONTROL_FIFO_INT_ENABLE 0x08
+#define DAQP_CONTROL_TRIGGER_ONESHOT 0x00
+#define DAQP_CONTROL_TRIGGER_CONTINUOUS 0x04
+#define DAQP_CONTROL_TRIGGER_INTERNAL 0x00
+#define DAQP_CONTROL_TRIGGER_EXTERNAL 0x02
+#define DAQP_CONTROL_TRIGGER_RISING 0x00
+#define DAQP_CONTROL_TRIGGER_FALLING 0x01
+
+#define DAQP_STATUS_IDLE 0x80
+#define DAQP_STATUS_RUNNING 0x40
+#define DAQP_STATUS_EVENTS 0x38
+#define DAQP_STATUS_DATA_LOST 0x20
+#define DAQP_STATUS_END_OF_SCAN 0x10
+#define DAQP_STATUS_FIFO_THRESHOLD 0x08
+#define DAQP_STATUS_FIFO_FULL 0x04
+#define DAQP_STATUS_FIFO_NEARFULL 0x02
+#define DAQP_STATUS_FIFO_EMPTY 0x01
+
+#define DAQP_COMMAND_ARM 0x80
+#define DAQP_COMMAND_RSTF 0x40
+#define DAQP_COMMAND_RSTQ 0x20
+#define DAQP_COMMAND_STOP 0x10
+#define DAQP_COMMAND_LATCH 0x08
+#define DAQP_COMMAND_100kHz 0x00
+#define DAQP_COMMAND_50kHz 0x02
+#define DAQP_COMMAND_25kHz 0x04
+#define DAQP_COMMAND_FIFO_DATA 0x01
+#define DAQP_COMMAND_FIFO_PROGRAM 0x00
+
+#define DAQP_AUX_TRIGGER_TTL 0x00
+#define DAQP_AUX_TRIGGER_ANALOG 0x80
+#define DAQP_AUX_TRIGGER_PRETRIGGER 0x40
+#define DAQP_AUX_TIMER_INT_ENABLE 0x20
+#define DAQP_AUX_TIMER_RELOAD 0x00
+#define DAQP_AUX_TIMER_PAUSE 0x08
+#define DAQP_AUX_TIMER_GO 0x10
+#define DAQP_AUX_TIMER_GO_EXTERNAL 0x18
+#define DAQP_AUX_TIMER_EXTERNAL_SRC 0x04
+#define DAQP_AUX_TIMER_INTERNAL_SRC 0x00
+#define DAQP_AUX_DA_DIRECT 0x00
+#define DAQP_AUX_DA_OVERFLOW 0x01
+#define DAQP_AUX_DA_EXTERNAL 0x02
+#define DAQP_AUX_DA_PACER 0x03
+
+#define DAQP_AUX_RUNNING 0x80
+#define DAQP_AUX_TRIGGERED 0x40
+#define DAQP_AUX_DA_BUFFER 0x20
+#define DAQP_AUX_TIMER_OVERFLOW 0x10
+#define DAQP_AUX_CONVERSION 0x08
+#define DAQP_AUX_DATA_LOST 0x04
+#define DAQP_AUX_FIFO_NEARFULL 0x02
+#define DAQP_AUX_FIFO_EMPTY 0x01
+
+/* These range structures tell COMEDI how the sample values map to
+ * voltages. The A/D converter has four ranges: +/- 10V through
+ * +/- 1.25V, and the D/A converter has only one: +/- 5V.
+ */
+
+static const struct comedi_lrange range_daqp_ai = { 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range_daqp_ao = { 1, {BIP_RANGE(5)} };
+
+/*====================================================================*/
+
+/* comedi interface code */
+
+static int daqp_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int daqp_detach(struct comedi_device * dev);
+static struct comedi_driver driver_daqp = {
+ driver_name:"quatech_daqp_cs",
+ module:THIS_MODULE,
+ attach:daqp_attach,
+ detach:daqp_detach,
+};
+
+#ifdef DAQP_DEBUG
+
+static void daqp_dump(struct comedi_device * dev)
+{
+ printk("DAQP: status %02x; aux status %02x\n",
+ inb(dev->iobase + DAQP_STATUS), inb(dev->iobase + DAQP_AUX));
+}
+
+static void hex_dump(char *str, void *ptr, int len)
+{
+ unsigned char *cptr = ptr;
+ int i;
+
+ printk(str);
+
+ for (i = 0; i < len; i++) {
+ if (i % 16 == 0) {
+ printk("\n0x%08x:", (unsigned int)cptr);
+ }
+ printk(" %02x", *(cptr++));
+ }
+ printk("\n");
+}
+
+#endif
+
+/* Cancel a running acquisition */
+
+static int daqp_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct local_info_t *local = (struct local_info_t *) s->private;
+
+ if (local->stop) {
+ return -EIO;
+ }
+
+ outb(DAQP_COMMAND_STOP, dev->iobase + DAQP_COMMAND);
+
+ /* flush any linguring data in FIFO - superfluous here */
+ /* outb(DAQP_COMMAND_RSTF, dev->iobase+DAQP_COMMAND); */
+
+ local->interrupt_mode = semaphore;
+
+ return 0;
+}
+
+/* Interrupt handler
+ *
+ * Operates in one of two modes. If local->interrupt_mode is
+ * 'semaphore', just signal the local->eos semaphore and return
+ * (one-shot mode). Otherwise (continuous mode), read data in from
+ * the card, transfer it to the buffer provided by the higher-level
+ * comedi kernel module, and signal various comedi callback routines,
+ * which run pretty quick.
+ */
+
+static void daqp_interrupt(int irq, void *dev_id PT_REGS_ARG)
+{
+ struct local_info_t *local = (struct local_info_t *) dev_id;
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
+ int loop_limit = 10000;
+ int status;
+
+ if (local == NULL) {
+ printk(KERN_WARNING
+ "daqp_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ dev = local->dev;
+ if (dev == NULL) {
+ printk(KERN_WARNING "daqp_interrupt(): NULL comedi_device.\n");
+ return;
+ }
+
+ if (!dev->attached) {
+ printk(KERN_WARNING
+ "daqp_interrupt(): struct comedi_device not yet attached.\n");
+ return;
+ }
+
+ s = local->s;
+ if (s == NULL) {
+ printk(KERN_WARNING
+ "daqp_interrupt(): NULL comedi_subdevice.\n");
+ return;
+ }
+
+ if ((struct local_info_t *) s->private != local) {
+ printk(KERN_WARNING
+ "daqp_interrupt(): invalid comedi_subdevice.\n");
+ return;
+ }
+
+ switch (local->interrupt_mode) {
+
+ case semaphore:
+
+ up(&local->eos);
+ break;
+
+ case buffer:
+
+ while (!((status = inb(dev->iobase + DAQP_STATUS))
+ & DAQP_STATUS_FIFO_EMPTY)) {
+
+ short data;
+
+ if (status & DAQP_STATUS_DATA_LOST) {
+ s->async->events |=
+ COMEDI_CB_EOA | COMEDI_CB_OVERFLOW;
+ printk("daqp: data lost\n");
+ daqp_ai_cancel(dev, s);
+ break;
+ }
+
+ data = inb(dev->iobase + DAQP_FIFO);
+ data |= inb(dev->iobase + DAQP_FIFO) << 8;
+ data ^= 0x8000;
+
+ comedi_buf_put(s->async, data);
+
+ /* If there's a limit, decrement it
+ * and stop conversion if zero
+ */
+
+ if (local->count > 0) {
+ local->count--;
+ if (local->count == 0) {
+ daqp_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA;
+ break;
+ }
+ }
+
+ if ((loop_limit--) <= 0)
+ break;
+ }
+
+ if (loop_limit <= 0) {
+ printk(KERN_WARNING
+ "loop_limit reached in daqp_interrupt()\n");
+ daqp_ai_cancel(dev, s);
+ s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ }
+
+ s->async->events |= COMEDI_CB_BLOCK;
+
+ comedi_event(dev, s);
+ }
+}
+
+/* One-shot analog data acquisition routine */
+
+static int daqp_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ struct local_info_t *local = (struct local_info_t *) s->private;
+ int i;
+ int v;
+ int counter = 10000;
+
+ if (local->stop) {
+ return -EIO;
+ }
+
+ /* Stop any running conversion */
+ daqp_ai_cancel(dev, s);
+
+ outb(0, dev->iobase + DAQP_AUX);
+
+ /* Reset scan list queue */
+ outb(DAQP_COMMAND_RSTQ, dev->iobase + DAQP_COMMAND);
+
+ /* Program one scan list entry */
+
+ v = DAQP_SCANLIST_CHANNEL(CR_CHAN(insn->chanspec))
+ | DAQP_SCANLIST_GAIN(CR_RANGE(insn->chanspec));
+
+ if (CR_AREF(insn->chanspec) == AREF_DIFF) {
+ v |= DAQP_SCANLIST_DIFFERENTIAL;
+ }
+
+ v |= DAQP_SCANLIST_START;
+
+ outb(v & 0xff, dev->iobase + DAQP_SCANLIST);
+ outb(v >> 8, dev->iobase + DAQP_SCANLIST);
+
+ /* Reset data FIFO (see page 28 of DAQP User's Manual) */
+
+ outb(DAQP_COMMAND_RSTF, dev->iobase + DAQP_COMMAND);
+
+ /* Set trigger */
+
+ v = DAQP_CONTROL_TRIGGER_ONESHOT | DAQP_CONTROL_TRIGGER_INTERNAL
+ | DAQP_CONTROL_PACER_100kHz | DAQP_CONTROL_EOS_INT_ENABLE;
+
+ outb(v, dev->iobase + DAQP_CONTROL);
+
+ /* Reset any pending interrupts (my card has a tendancy to require
+ * require multiple reads on the status register to achieve this)
+ */
+
+ while (--counter
+ && (inb(dev->iobase + DAQP_STATUS) & DAQP_STATUS_EVENTS)) ;
+ if (!counter) {
+ printk("daqp: couldn't clear interrupts in status register\n");
+ return -1;
+ }
+
+ /* Make sure semaphore is blocked */
+ sema_init(&local->eos, 0);
+ local->interrupt_mode = semaphore;
+ local->dev = dev;
+ local->s = s;
+
+ for (i = 0; i < insn->n; i++) {
+
+ /* Start conversion */
+ outb(DAQP_COMMAND_ARM | DAQP_COMMAND_FIFO_DATA,
+ dev->iobase + DAQP_COMMAND);
+
+ /* Wait for interrupt service routine to unblock semaphore */
+ /* Maybe could use a timeout here, but it's interruptible */
+ if (down_interruptible(&local->eos))
+ return -EINTR;
+
+ data[i] = inb(dev->iobase + DAQP_FIFO);
+ data[i] |= inb(dev->iobase + DAQP_FIFO) << 8;
+ data[i] ^= 0x8000;
+ }
+
+ return insn->n;
+}
+
+/* This function converts ns nanoseconds to a counter value suitable
+ * for programming the device. We always use the DAQP's 5 MHz clock,
+ * which with its 24-bit counter, allows values up to 84 seconds.
+ * Also, the function adjusts ns so that it cooresponds to the actual
+ * time that the device will use.
+ */
+
+static int daqp_ns_to_timer(unsigned int *ns, int round)
+{
+ int timer;
+
+ timer = *ns / 200;
+ *ns = timer * 200;
+
+ return timer;
+}
+
+/* cmdtest tests a particular command to see if it is valid.
+ * Using the cmdtest ioctl, a user can create a valid cmd
+ * and then have it executed by the cmd ioctl.
+ *
+ * cmdtest returns 1,2,3,4 or 0, depending on which tests
+ * the command passes.
+ */
+
+static int daqp_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER | TRIG_NOW;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ /* note that mutual compatiblity is not an issue here */
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_FOLLOW)
+ err++;
+ if (cmd->convert_src != TRIG_NOW && cmd->convert_src != TRIG_TIMER)
+ err++;
+ if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+#define MAX_SPEED 10000 /* 100 kHz - in nanoseconds */
+
+ if (cmd->scan_begin_src == TRIG_TIMER
+ && cmd->scan_begin_arg < MAX_SPEED) {
+ cmd->scan_begin_arg = MAX_SPEED;
+ err++;
+ }
+
+ /* If both scan_begin and convert are both timer values, the only
+ * way that can make sense is if the scan time is the number of
+ * conversions times the convert time
+ */
+
+ if (cmd->scan_begin_src == TRIG_TIMER && cmd->convert_src == TRIG_TIMER
+ && cmd->scan_begin_arg !=
+ cmd->convert_arg * cmd->scan_end_arg) {
+ err++;
+ }
+
+ if (cmd->convert_src == TRIG_TIMER && cmd->convert_arg < MAX_SPEED) {
+ cmd->convert_arg = MAX_SPEED;
+ err++;
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (cmd->stop_arg > 0x00ffffff) {
+ cmd->stop_arg = 0x00ffffff;
+ err++;
+ }
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ daqp_ns_to_timer(&cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ daqp_ns_to_timer(&cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->convert_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int daqp_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
+{
+ struct local_info_t *local = (struct local_info_t *) s->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int counter = 100;
+ int scanlist_start_on_every_entry;
+ int threshold;
+
+ int i;
+ int v;
+
+ if (local->stop) {
+ return -EIO;
+ }
+
+ /* Stop any running conversion */
+ daqp_ai_cancel(dev, s);
+
+ outb(0, dev->iobase + DAQP_AUX);
+
+ /* Reset scan list queue */
+ outb(DAQP_COMMAND_RSTQ, dev->iobase + DAQP_COMMAND);
+
+ /* Program pacer clock
+ *
+ * There's two modes we can operate in. If convert_src is
+ * TRIG_TIMER, then convert_arg specifies the time between
+ * each conversion, so we program the pacer clock to that
+ * frequency and set the SCANLIST_START bit on every scanlist
+ * entry. Otherwise, convert_src is TRIG_NOW, which means
+ * we want the fastest possible conversions, scan_begin_src
+ * is TRIG_TIMER, and scan_begin_arg specifies the time between
+ * each scan, so we program the pacer clock to this frequency
+ * and only set the SCANLIST_START bit on the first entry.
+ */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ int counter = daqp_ns_to_timer(&cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ outb(counter & 0xff, dev->iobase + DAQP_PACER_LOW);
+ outb((counter >> 8) & 0xff, dev->iobase + DAQP_PACER_MID);
+ outb((counter >> 16) & 0xff, dev->iobase + DAQP_PACER_HIGH);
+ scanlist_start_on_every_entry = 1;
+ } else {
+ int counter = daqp_ns_to_timer(&cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ outb(counter & 0xff, dev->iobase + DAQP_PACER_LOW);
+ outb((counter >> 8) & 0xff, dev->iobase + DAQP_PACER_MID);
+ outb((counter >> 16) & 0xff, dev->iobase + DAQP_PACER_HIGH);
+ scanlist_start_on_every_entry = 0;
+ }
+
+ /* Program scan list */
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+
+ int chanspec = cmd->chanlist[i];
+
+ /* Program one scan list entry */
+
+ v = DAQP_SCANLIST_CHANNEL(CR_CHAN(chanspec))
+ | DAQP_SCANLIST_GAIN(CR_RANGE(chanspec));
+
+ if (CR_AREF(chanspec) == AREF_DIFF) {
+ v |= DAQP_SCANLIST_DIFFERENTIAL;
+ }
+
+ if (i == 0 || scanlist_start_on_every_entry) {
+ v |= DAQP_SCANLIST_START;
+ }
+
+ outb(v & 0xff, dev->iobase + DAQP_SCANLIST);
+ outb(v >> 8, dev->iobase + DAQP_SCANLIST);
+ }
+
+ /* Now it's time to program the FIFO threshold, basically the
+ * number of samples the card will buffer before it interrupts
+ * the CPU.
+ *
+ * If we don't have a stop count, then use half the size of
+ * the FIFO (the manufacturer's recommendation). Consider
+ * that the FIFO can hold 2K samples (4K bytes). With the
+ * threshold set at half the FIFO size, we have a margin of
+ * error of 1024 samples. At the chip's maximum sample rate
+ * of 100,000 Hz, the CPU would have to delay interrupt
+ * service for a full 10 milliseconds in order to lose data
+ * here (as opposed to higher up in the kernel). I've never
+ * seen it happen. However, for slow sample rates it may
+ * buffer too much data and introduce too much delay for the
+ * user application.
+ *
+ * If we have a stop count, then things get more interesting.
+ * If the stop count is less than the FIFO size (actually
+ * three-quarters of the FIFO size - see below), we just use
+ * the stop count itself as the threshold, the card interrupts
+ * us when that many samples have been taken, and we kill the
+ * acquisition at that point and are done. If the stop count
+ * is larger than that, then we divide it by 2 until it's less
+ * than three quarters of the FIFO size (we always leave the
+ * top quarter of the FIFO as protection against sluggish CPU
+ * interrupt response) and use that as the threshold. So, if
+ * the stop count is 4000 samples, we divide by two twice to
+ * get 1000 samples, use that as the threshold, take four
+ * interrupts to get our 4000 samples and are done.
+ *
+ * The algorithm could be more clever. For example, if 81000
+ * samples are requested, we could set the threshold to 1500
+ * samples and take 54 interrupts to get 81000. But 54 isn't
+ * a power of two, so this algorithm won't find that option.
+ * Instead, it'll set the threshold at 1266 and take 64
+ * interrupts to get 81024 samples, of which the last 24 will
+ * be discarded... but we won't get the last interrupt until
+ * they've been collected. To find the first option, the
+ * computer could look at the prime decomposition of the
+ * sample count (81000 = 3^4 * 5^3 * 2^3) and factor it into a
+ * threshold (1500 = 3 * 5^3 * 2^2) and an interrupt count (54
+ * = 3^3 * 2). Hmmm... a one-line while loop or prime
+ * decomposition of integers... I'll leave it the way it is.
+ *
+ * I'll also note a mini-race condition before ignoring it in
+ * the code. Let's say we're taking 4000 samples, as before.
+ * After 1000 samples, we get an interrupt. But before that
+ * interrupt is completely serviced, another sample is taken
+ * and loaded into the FIFO. Since the interrupt handler
+ * empties the FIFO before returning, it will read 1001 samples.
+ * If that happens four times, we'll end up taking 4004 samples,
+ * not 4000. The interrupt handler will discard the extra four
+ * samples (by halting the acquisition with four samples still
+ * in the FIFO), but we will have to wait for them.
+ *
+ * In short, this code works pretty well, but for either of
+ * the two reasons noted, might end up waiting for a few more
+ * samples than actually requested. Shouldn't make too much
+ * of a difference.
+ */
+
+ /* Save away the number of conversions we should perform, and
+ * compute the FIFO threshold (in bytes, not samples - that's
+ * why we multiple local->count by 2 = sizeof(sample))
+ */
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ local->count = cmd->stop_arg * cmd->scan_end_arg;
+ threshold = 2 * local->count;
+ while (threshold > DAQP_FIFO_SIZE * 3 / 4)
+ threshold /= 2;
+ } else {
+ local->count = -1;
+ threshold = DAQP_FIFO_SIZE / 2;
+ }
+
+ /* Reset data FIFO (see page 28 of DAQP User's Manual) */
+
+ outb(DAQP_COMMAND_RSTF, dev->iobase + DAQP_COMMAND);
+
+ /* Set FIFO threshold. First two bytes are near-empty
+ * threshold, which is unused; next two bytes are near-full
+ * threshold. We computed the number of bytes we want in the
+ * FIFO when the interrupt is generated, what the card wants
+ * is actually the number of available bytes left in the FIFO
+ * when the interrupt is to happen.
+ */
+
+ outb(0x00, dev->iobase + DAQP_FIFO);
+ outb(0x00, dev->iobase + DAQP_FIFO);
+
+ outb((DAQP_FIFO_SIZE - threshold) & 0xff, dev->iobase + DAQP_FIFO);
+ outb((DAQP_FIFO_SIZE - threshold) >> 8, dev->iobase + DAQP_FIFO);
+
+ /* Set trigger */
+
+ v = DAQP_CONTROL_TRIGGER_CONTINUOUS | DAQP_CONTROL_TRIGGER_INTERNAL
+ | DAQP_CONTROL_PACER_5MHz | DAQP_CONTROL_FIFO_INT_ENABLE;
+
+ outb(v, dev->iobase + DAQP_CONTROL);
+
+ /* Reset any pending interrupts (my card has a tendancy to require
+ * require multiple reads on the status register to achieve this)
+ */
+
+ while (--counter
+ && (inb(dev->iobase + DAQP_STATUS) & DAQP_STATUS_EVENTS)) ;
+ if (!counter) {
+ printk("daqp: couldn't clear interrupts in status register\n");
+ return -1;
+ }
+
+ local->interrupt_mode = buffer;
+ local->dev = dev;
+ local->s = s;
+
+ /* Start conversion */
+ outb(DAQP_COMMAND_ARM | DAQP_COMMAND_FIFO_DATA,
+ dev->iobase + DAQP_COMMAND);
+
+ return 0;
+}
+
+/* Single-shot analog output routine */
+
+static int daqp_ao_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ struct local_info_t *local = (struct local_info_t *) s->private;
+ int d;
+ unsigned int chan;
+
+ if (local->stop) {
+ return -EIO;
+ }
+
+ chan = CR_CHAN(insn->chanspec);
+ d = data[0];
+ d &= 0x0fff;
+ d ^= 0x0800; /* Flip the sign */
+ d |= chan << 12;
+
+ /* Make sure D/A update mode is direct update */
+ outb(0, dev->iobase + DAQP_AUX);
+
+ outw(d, dev->iobase + DAQP_DA);
+
+ return 1;
+}
+
+/* Digital input routine */
+
+static int daqp_di_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ struct local_info_t *local = (struct local_info_t *) s->private;
+
+ if (local->stop) {
+ return -EIO;
+ }
+
+ data[0] = inb(dev->iobase + DAQP_DIGITAL_IO);
+
+ return 1;
+}
+
+/* Digital output routine */
+
+static int daqp_do_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ struct local_info_t *local = (struct local_info_t *) s->private;
+
+ if (local->stop) {
+ return -EIO;
+ }
+
+ outw(data[0] & 0xf, dev->iobase + DAQP_DIGITAL_IO);
+
+ return 1;
+}
+
+/* daqp_attach is called via comedi_config to attach a comedi device
+ * to a /dev/comedi*. Note that this is different from daqp_cs_attach()
+ * which is called by the pcmcia subsystem to attach the PCMCIA card
+ * when it is inserted.
+ */
+
+static int daqp_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int ret;
+ struct local_info_t *local = dev_table[it->options[0]];
+ tuple_t tuple;
+ int i;
+ struct comedi_subdevice *s;
+
+ if (it->options[0] < 0 || it->options[0] >= MAX_DEV || !local) {
+ printk("comedi%d: No such daqp device %d\n",
+ dev->minor, it->options[0]);
+ return -EIO;
+ }
+
+ /* Typically brittle code that I don't completely understand,
+ * but "it works on my card". The intent is to pull the model
+ * number of the card out the PCMCIA CIS and stash it away as
+ * the COMEDI board_name. Looks like the third field in
+ * CISTPL_VERS_1 (offset 2) holds what we're looking for. If
+ * it doesn't work, who cares, just leave it as "DAQP".
+ */
+
+ strcpy(local->board_name, "DAQP");
+ dev->board_name = local->board_name;
+
+ tuple.DesiredTuple = CISTPL_VERS_1;
+ if (pcmcia_get_first_tuple(local->link, &tuple) == 0) {
+ u_char buf[128];
+
+ buf[0] = buf[sizeof(buf) - 1] = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.TupleOffset = 2;
+ if (pcmcia_get_tuple_data(local->link, &tuple) == 0) {
+
+ for (i = 0; i < tuple.TupleDataLen - 4; i++)
+ if (buf[i] == 0)
+ break;
+ for (i++; i < tuple.TupleDataLen - 4; i++)
+ if (buf[i] == 0)
+ break;
+ i++;
+ if ((i < tuple.TupleDataLen - 4)
+ && (strncmp(buf + i, "DAQP", 4) == 0)) {
+ strncpy(local->board_name, buf + i,
+ sizeof(local->board_name));
+ }
+ }
+ }
+
+ dev->iobase = local->link->io.BasePort1;
+
+ if ((ret = alloc_subdevices(dev, 4)) < 0)
+ return ret;
+
+ printk("comedi%d: attaching daqp%d (io 0x%04lx)\n",
+ dev->minor, it->options[0], dev->iobase);
+
+ s = dev->subdevices + 0;
+ dev->read_subdev = s;
+ s->private = local;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
+ s->n_chan = 8;
+ s->len_chanlist = 2048;
+ s->maxdata = 0xffff;
+ s->range_table = &range_daqp_ai;
+ s->insn_read = daqp_ai_insn_read;
+ s->do_cmdtest = daqp_ai_cmdtest;
+ s->do_cmd = daqp_ai_cmd;
+ s->cancel = daqp_ai_cancel;
+
+ s = dev->subdevices + 1;
+ dev->write_subdev = s;
+ s->private = local;
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITEABLE;
+ s->n_chan = 2;
+ s->len_chanlist = 1;
+ s->maxdata = 0x0fff;
+ s->range_table = &range_daqp_ao;
+ s->insn_write = daqp_ao_insn_write;
+
+ s = dev->subdevices + 2;
+ s->private = local;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 1;
+ s->len_chanlist = 1;
+ s->insn_read = daqp_di_insn_read;
+
+ s = dev->subdevices + 3;
+ s->private = local;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITEABLE;
+ s->n_chan = 1;
+ s->len_chanlist = 1;
+ s->insn_write = daqp_do_insn_write;
+
+ return 1;
+}
+
+/* daqp_detach (called from comedi_comdig) does nothing. If the PCMCIA
+ * card is removed, daqp_cs_detach() is called by the pcmcia subsystem.
+ */
+
+static int daqp_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: detaching daqp\n", dev->minor);
+
+ return 0;
+}
+
+/*====================================================================
+
+ PCMCIA interface code
+
+ The rest of the code in this file is based on dummy_cs.c v1.24
+ from the Linux pcmcia_cs distribution v3.1.8 and is subject
+ to the following license agreement.
+
+ The remaining contents of this file are subject to the Mozilla Public
+ License Version 1.1 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS
+ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ rights and limitations under the License.
+
+ The initial developer of the original code is David A. Hinds
+ <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the GNU Public License version 2 (the "GPL"), in which
+ case the provisions of the GPL are applicable instead of the
+ above. If you wish to allow the use of your version of this file
+ only under the terms of the GPL and not to allow others to use
+ your version of this file under the MPL, indicate your decision
+ by deleting the provisions above and replace them with the notice
+ and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
+======================================================================*/
+
+/*
+ The event() function is this driver's Card Services event handler.
+ It will be called by Card Services when an appropriate card status
+ event is received. The config() and release() entry points are
+ used to configure or release a socket, in response to card
+ insertion and ejection events.
+
+ Kernel version 2.6.16 upwards uses suspend() and resume() functions
+ instead of an event() function.
+*/
+
+static void daqp_cs_config(struct pcmcia_device *link);
+static void daqp_cs_release(struct pcmcia_device *link);
+static int daqp_cs_suspend(struct pcmcia_device *p_dev);
+static int daqp_cs_resume(struct pcmcia_device *p_dev);
+
+/*
+ The attach() and detach() entry points are used to create and destroy
+ "instances" of the driver, where each instance represents everything
+ needed to manage one actual PCMCIA card.
+*/
+
+static int daqp_cs_attach(struct pcmcia_device *);
+static void daqp_cs_detach(struct pcmcia_device *);
+
+/*
+ The dev_info variable is the "key" that is used to match up this
+ device driver with appropriate cards, through the card configuration
+ database.
+*/
+
+static const dev_info_t dev_info = "quatech_daqp_cs";
+
+/*======================================================================
+
+ daqp_cs_attach() creates an "instance" of the driver, allocating
+ local data structures for one device. The device is registered
+ with Card Services.
+
+ The dev_link structure is initialized, but we don't actually
+ configure the card at this point -- we wait until we receive a
+ card insertion event.
+
+======================================================================*/
+
+static int daqp_cs_attach(struct pcmcia_device *link)
+{
+ struct local_info_t *local;
+ int i;
+
+ DEBUG(0, "daqp_cs_attach()\n");
+
+ for (i = 0; i < MAX_DEV; i++)
+ if (dev_table[i] == NULL)
+ break;
+ if (i == MAX_DEV) {
+ printk(KERN_NOTICE "daqp_cs: no devices available\n");
+ return -ENODEV;
+ }
+
+ /* Allocate space for private device-specific data */
+ local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
+ if (!local)
+ return -ENOMEM;
+
+ local->table_index = i;
+ dev_table[i] = local;
+ local->link = link;
+ link->priv = local;
+
+ /* Interrupt setup */
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Handler = daqp_interrupt;
+ link->irq.Instance = local;
+
+ /*
+ General socket configuration defaults can go here. In this
+ client, we assume very little, and rely on the CIS for almost
+ everything. In most clients, many details (i.e., number, sizes,
+ and attributes of IO windows) are fixed by the nature of the
+ device, and can be hard-wired here.
+ */
+ link->conf.Attributes = 0;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ daqp_cs_config(link);
+
+ return 0;
+} /* daqp_cs_attach */
+
+/*======================================================================
+
+ This deletes a driver "instance". The device is de-registered
+ with Card Services. If it has been released, all local data
+ structures are freed. Otherwise, the structures will be freed
+ when the device is released.
+
+======================================================================*/
+
+static void daqp_cs_detach(struct pcmcia_device *link)
+{
+ struct local_info_t *dev = link->priv;
+
+ DEBUG(0, "daqp_cs_detach(0x%p)\n", link);
+
+ if (link->dev_node) {
+ dev->stop = 1;
+ daqp_cs_release(link);
+ }
+
+ /* Unlink device structure, and free it */
+ dev_table[dev->table_index] = NULL;
+ if (dev)
+ kfree(dev);
+
+} /* daqp_cs_detach */
+
+/*======================================================================
+
+ daqp_cs_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ device available to the system.
+
+======================================================================*/
+
+static void daqp_cs_config(struct pcmcia_device *link)
+{
+ struct local_info_t *dev = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ int last_ret;
+ u_char buf[64];
+
+ DEBUG(0, "daqp_cs_config(0x%p)\n", link);
+
+ /*
+ This reads the card's CONFIG tuple to find its configuration
+ registers.
+ */
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ tuple.Attributes = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.TupleOffset = 0;
+ if ((last_ret = pcmcia_get_first_tuple(link, &tuple))) {
+ cs_error(link, GetFirstTuple, last_ret);
+ goto cs_failed;
+ }
+ if ((last_ret = pcmcia_get_tuple_data(link, &tuple))) {
+ cs_error(link, GetTupleData, last_ret);
+ goto cs_failed;
+ }
+ if ((last_ret = pcmcia_parse_tuple(&tuple, &parse))) {
+ cs_error(link, ParseTuple, last_ret);
+ goto cs_failed;
+ }
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+
+ /*
+ In this loop, we scan the CIS for configuration table entries,
+ each of which describes a valid card configuration, including
+ voltage, IO window, memory window, and interrupt settings.
+
+ We make no assumptions about the card to be configured: we use
+ just the information available in the CIS. In an ideal world,
+ this would work for any PCMCIA card, but it requires a complete
+ and accurate CIS. In practice, a driver usually "knows" most of
+ these things without consulting the CIS, and most client drivers
+ will only use the CIS to fill in implementation-defined details.
+ */
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ if ((last_ret = pcmcia_get_first_tuple(link, &tuple))) {
+ cs_error(link, GetFirstTuple, last_ret);
+ goto cs_failed;
+ }
+ while (1) {
+ cistpl_cftable_entry_t dflt = { 0 };
+ cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
+ if (pcmcia_get_tuple_data(link, &tuple))
+ goto next_entry;
+ if (pcmcia_parse_tuple(&tuple, &parse))
+ goto next_entry;
+
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+ dflt = *cfg;
+ if (cfg->index == 0)
+ goto next_entry;
+ link->conf.ConfigIndex = cfg->index;
+
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
+ link->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ link->io.NumPorts1 = link->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ link->io.BasePort1 = io->win[0].base;
+ link->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ link->io.Attributes2 = link->io.Attributes1;
+ link->io.BasePort2 = io->win[1].base;
+ link->io.NumPorts2 = io->win[1].len;
+ }
+ }
+
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(link, &link->io))
+ goto next_entry;
+
+ /* If we got this far, we're cool! */
+ break;
+
+ next_entry:
+ if ((last_ret = pcmcia_get_next_tuple(link, &tuple))) {
+ cs_error(link, GetNextTuple, last_ret);
+ goto cs_failed;
+ }
+ }
+
+ /*
+ Allocate an interrupt line. Note that this does not assign a
+ handler to the interrupt, unless the 'Handler' member of the
+ irq structure is initialized.
+ */
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ if ((last_ret = pcmcia_request_irq(link, &link->irq))) {
+ cs_error(link, RequestIRQ, last_ret);
+ goto cs_failed;
+ }
+
+ /*
+ This actually configures the PCMCIA socket -- setting up
+ the I/O windows and the interrupt mapping, and putting the
+ card and host interface into "Memory and IO" mode.
+ */
+ if ((last_ret = pcmcia_request_configuration(link, &link->conf))) {
+ cs_error(link, RequestConfiguration, last_ret);
+ goto cs_failed;
+ }
+
+ /*
+ At this point, the dev_node_t structure(s) need to be
+ initialized and arranged in a linked list at link->dev.
+ */
+ /* Comedi's PCMCIA script uses this device name (extracted
+ * from /var/lib/pcmcia/stab) to pass to comedi_config
+ */
+ /* sprintf(dev->node.dev_name, "daqp%d", dev->table_index); */
+ sprintf(dev->node.dev_name, "quatech_daqp_cs");
+ dev->node.major = dev->node.minor = 0;
+ link->dev_node = &dev->node;
+
+ /* Finally, report what we've done */
+ printk(KERN_INFO "%s: index 0x%02x",
+ dev->node.dev_name, link->conf.ConfigIndex);
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ printk(", irq %u", link->irq.AssignedIRQ);
+ if (link->io.NumPorts1)
+ printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+ link->io.BasePort1 + link->io.NumPorts1 - 1);
+ if (link->io.NumPorts2)
+ printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+ link->io.BasePort2 + link->io.NumPorts2 - 1);
+ printk("\n");
+
+ return;
+
+ cs_failed:
+ daqp_cs_release(link);
+
+} /* daqp_cs_config */
+
+static void daqp_cs_release(struct pcmcia_device *link)
+{
+ DEBUG(0, "daqp_cs_release(0x%p)\n", link);
+
+ pcmcia_disable_device(link);
+} /* daqp_cs_release */
+
+/*======================================================================
+
+ The card status event handler. Mostly, this schedules other
+ stuff to run after an event is received.
+
+ When a CARD_REMOVAL event is received, we immediately set a
+ private flag to block future accesses to this device. All the
+ functions that actually access the device should check this flag
+ to make sure the card is still present.
+
+======================================================================*/
+
+static int daqp_cs_suspend(struct pcmcia_device *link)
+{
+ struct local_info_t *local = link->priv;
+
+ /* Mark the device as stopped, to block IO until later */
+ local->stop = 1;
+ return 0;
+}
+
+static int daqp_cs_resume(struct pcmcia_device *link)
+{
+ struct local_info_t *local = link->priv;
+
+ local->stop = 0;
+
+ return 0;
+}
+
+/*====================================================================*/
+
+#ifdef MODULE
+
+static struct pcmcia_device_id daqp_cs_id_table[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0027),
+ PCMCIA_DEVICE_NULL
+};
+
+MODULE_DEVICE_TABLE(pcmcia, daqp_cs_id_table);
+
+struct pcmcia_driver daqp_cs_driver = {
+ .probe = daqp_cs_attach,
+ .remove = daqp_cs_detach,
+ .suspend = daqp_cs_suspend,
+ .resume = daqp_cs_resume,
+ .id_table = daqp_cs_id_table,
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = dev_info,
+ },
+};
+
+int __init init_module(void)
+{
+ DEBUG(0, "%s\n", version);
+ pcmcia_register_driver(&daqp_cs_driver);
+ comedi_driver_register(&driver_daqp);
+ return 0;
+}
+
+void __exit cleanup_module(void)
+{
+ DEBUG(0, "daqp_cs: unloading\n");
+ comedi_driver_unregister(&driver_daqp);
+ pcmcia_unregister_driver(&daqp_cs_driver);
+}
+
+#endif
diff --git a/drivers/staging/comedi/drivers/rtd520.c b/drivers/staging/comedi/drivers/rtd520.c
index 65d5242a2585..ca347f21d140 100644
--- a/drivers/staging/comedi/drivers/rtd520.c
+++ b/drivers/staging/comedi/drivers/rtd520.c
@@ -196,7 +196,7 @@ Configuration options:
/*
The board has 3 input modes and the gains of 1,2,4,...32 (, 64, 128)
*/
-static const comedi_lrange rtd_ai_7520_range = { 18, {
+static const struct comedi_lrange rtd_ai_7520_range = { 18, {
/* +-5V input range gain steps */
BIP_RANGE(5.0),
BIP_RANGE(5.0 / 2),
@@ -223,7 +223,7 @@ static const comedi_lrange rtd_ai_7520_range = { 18, {
};
/* PCI4520 has two more gains (6 more entries) */
-static const comedi_lrange rtd_ai_4520_range = { 24, {
+static const struct comedi_lrange rtd_ai_4520_range = { 24, {
/* +-5V input range gain steps */
BIP_RANGE(5.0),
BIP_RANGE(5.0 / 2),
@@ -255,7 +255,7 @@ static const comedi_lrange rtd_ai_4520_range = { 24, {
};
/* Table order matches range values */
-static const comedi_lrange rtd_ao_range = { 4, {
+static const struct comedi_lrange rtd_ao_range = { 4, {
RANGE(0, 5),
RANGE(0, 10),
RANGE(-5, 5),
@@ -266,7 +266,7 @@ static const comedi_lrange rtd_ao_range = { 4, {
/*
Board descriptions
*/
-typedef struct rtdBoard_struct {
+struct rtdBoard {
const char *name; /* must be first */
int device_id;
int aiChans;
@@ -274,12 +274,12 @@ typedef struct rtdBoard_struct {
int aiMaxGain;
int range10Start; /* start of +-10V range */
int rangeUniStart; /* start of +10V range */
-} rtdBoard;
+};
-static const rtdBoard rtd520Boards[] = {
+static const struct rtdBoard rtd520Boards[] = {
{
name: "DM7520",
- device_id:0x7520,
+ device_id : 0x7520,
aiChans: 16,
aiBits: 12,
aiMaxGain:32,
@@ -288,7 +288,7 @@ static const rtdBoard rtd520Boards[] = {
},
{
name: "PCI4520",
- device_id:0x4520,
+ device_id : 0x4520,
aiChans: 16,
aiBits: 12,
aiMaxGain:128,
@@ -308,13 +308,13 @@ MODULE_DEVICE_TABLE(pci, rtd520_pci_table);
/*
* Useful for shorthand access to the particular board structure
*/
-#define thisboard ((const rtdBoard *)dev->board_ptr)
+#define thisboard ((const struct rtdBoard *)dev->board_ptr)
/*
This structure is for data unique to this hardware driver.
This is also unique for each board in the system.
*/
-typedef struct {
+struct rtdPrivate {
/* memory mapped board structures */
void *las0;
void *las1;
@@ -334,7 +334,7 @@ typedef struct {
unsigned char chanBipolar[RTD_MAX_CHANLIST / 8]; /* bit array */
/* read back data */
- lsampl_t aoValue[2]; /* Used for AO read back */
+ unsigned int aoValue[2]; /* Used for AO read back */
/* timer gate (when enabled) */
u8 utcGate[4]; /* 1 extra allows simple range check */
@@ -358,7 +358,7 @@ typedef struct {
u8 dma1Control;
#endif /* USE_DMA */
unsigned fifoLen;
-} rtdPrivate;
+};
/* bit defines for "flags" */
#define SEND_EOS 0x01 /* send End Of Scan events */
@@ -366,18 +366,18 @@ typedef struct {
#define DMA1_ACTIVE 0x04 /* DMA1 is active */
/* Macros for accessing channel list bit array */
-#define CHAN_ARRAY_TEST(array,index) \
+#define CHAN_ARRAY_TEST(array, index) \
(((array)[(index)/8] >> ((index) & 0x7)) & 0x1)
-#define CHAN_ARRAY_SET(array,index) \
+#define CHAN_ARRAY_SET(array, index) \
(((array)[(index)/8] |= 1 << ((index) & 0x7)))
-#define CHAN_ARRAY_CLEAR(array,index) \
+#define CHAN_ARRAY_CLEAR(array, index) \
(((array)[(index)/8] &= ~(1 << ((index) & 0x7))))
/*
* most drivers define the following macro to make it easy to
* access the private structure.
*/
-#define devpriv ((rtdPrivate *)dev->private)
+#define devpriv ((struct rtdPrivate *)dev->private)
/* Macros to access registers */
@@ -394,15 +394,15 @@ typedef struct {
writel (0, devpriv->las0+LAS0_CGT_CLEAR)
/* Reset channel gain table read and write pointers */
-#define RtdEnableCGT(dev,v) \
+#define RtdEnableCGT(dev, v) \
writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_CGT_ENABLE)
/* Write channel gain table entry */
-#define RtdWriteCGTable(dev,v) \
+#define RtdWriteCGTable(dev, v) \
writel (v, devpriv->las0+LAS0_CGT_WRITE)
/* Write Channel Gain Latch */
-#define RtdWriteCGLatch(dev,v) \
+#define RtdWriteCGLatch(dev, v) \
writel (v, devpriv->las0+LAS0_CGL_WRITE)
/* Reset ADC FIFO */
@@ -410,39 +410,39 @@ typedef struct {
writel (0, devpriv->las0+LAS0_ADC_FIFO_CLEAR)
/* Set ADC start conversion source select (write only) */
-#define RtdAdcConversionSource(dev,v) \
+#define RtdAdcConversionSource(dev, v) \
writel (v, devpriv->las0+LAS0_ADC_CONVERSION)
/* Set burst start source select (write only) */
-#define RtdBurstStartSource(dev,v) \
+#define RtdBurstStartSource(dev, v) \
writel (v, devpriv->las0+LAS0_BURST_START)
/* Set Pacer start source select (write only) */
-#define RtdPacerStartSource(dev,v) \
+#define RtdPacerStartSource(dev, v) \
writel (v, devpriv->las0+LAS0_PACER_START)
/* Set Pacer stop source select (write only) */
-#define RtdPacerStopSource(dev,v) \
+#define RtdPacerStopSource(dev, v) \
writel (v, devpriv->las0+LAS0_PACER_STOP)
/* Set Pacer clock source select (write only) 0=external 1=internal */
-#define RtdPacerClockSource(dev,v) \
+#define RtdPacerClockSource(dev, v) \
writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_PACER_SELECT)
/* Set sample counter source select (write only) */
-#define RtdAdcSampleCounterSource(dev,v) \
+#define RtdAdcSampleCounterSource(dev, v) \
writel (v, devpriv->las0+LAS0_ADC_SCNT_SRC)
/* Set Pacer trigger mode select (write only) 0=single cycle, 1=repeat */
-#define RtdPacerTriggerMode(dev,v) \
+#define RtdPacerTriggerMode(dev, v) \
writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_PACER_REPEAT)
/* Set About counter stop enable (write only) */
-#define RtdAboutStopEnable(dev,v) \
+#define RtdAboutStopEnable(dev, v) \
writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_ACNT_STOP_ENABLE)
/* Set external trigger polarity (write only) 0=positive edge, 1=negative */
-#define RtdTriggerPolarity(dev,v) \
+#define RtdTriggerPolarity(dev, v) \
writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_ETRG_POLARITY)
/* Start single ADC conversion */
@@ -473,15 +473,15 @@ typedef struct {
readw (devpriv->las0+LAS0_IT)
/* Interrupt mask */
-#define RtdInterruptMask(dev,v) \
- writew ((devpriv->intMask = (v)),devpriv->las0+LAS0_IT)
+#define RtdInterruptMask(dev, v) \
+ writew ((devpriv->intMask = (v)), devpriv->las0+LAS0_IT)
/* Interrupt status clear (only bits set in mask) */
#define RtdInterruptClear(dev) \
readw (devpriv->las0+LAS0_CLEAR)
/* Interrupt clear mask */
-#define RtdInterruptClearMask(dev,v) \
+#define RtdInterruptClearMask(dev, v) \
writew ((devpriv->intClearMask = (v)), devpriv->las0+LAS0_CLEAR)
/* Interrupt overrun status */
@@ -495,92 +495,92 @@ typedef struct {
/* Pacer counter, 24bit */
#define RtdPacerCount(dev) \
readl (devpriv->las0+LAS0_PCLK)
-#define RtdPacerCounter(dev,v) \
- writel ((v) & 0xffffff,devpriv->las0+LAS0_PCLK)
+#define RtdPacerCounter(dev, v) \
+ writel ((v) & 0xffffff, devpriv->las0+LAS0_PCLK)
/* Burst counter, 10bit */
#define RtdBurstCount(dev) \
readl (devpriv->las0+LAS0_BCLK)
-#define RtdBurstCounter(dev,v) \
- writel ((v) & 0x3ff,devpriv->las0+LAS0_BCLK)
+#define RtdBurstCounter(dev, v) \
+ writel ((v) & 0x3ff, devpriv->las0+LAS0_BCLK)
/* Delay counter, 16bit */
#define RtdDelayCount(dev) \
readl (devpriv->las0+LAS0_DCLK)
-#define RtdDelayCounter(dev,v) \
+#define RtdDelayCounter(dev, v) \
writel ((v) & 0xffff, devpriv->las0+LAS0_DCLK)
/* About counter, 16bit */
#define RtdAboutCount(dev) \
readl (devpriv->las0+LAS0_ACNT)
-#define RtdAboutCounter(dev,v) \
+#define RtdAboutCounter(dev, v) \
writel ((v) & 0xffff, devpriv->las0+LAS0_ACNT)
/* ADC sample counter, 10bit */
#define RtdAdcSampleCount(dev) \
readl (devpriv->las0+LAS0_ADC_SCNT)
-#define RtdAdcSampleCounter(dev,v) \
+#define RtdAdcSampleCounter(dev, v) \
writel ((v) & 0x3ff, devpriv->las0+LAS0_ADC_SCNT)
/* User Timer/Counter (8254) */
-#define RtdUtcCounterGet(dev,n) \
+#define RtdUtcCounterGet(dev, n) \
readb (devpriv->las0 \
+ ((n <= 0) ? LAS0_UTC0 : ((1 == n) ? LAS0_UTC1 : LAS0_UTC2)))
-#define RtdUtcCounterPut(dev,n,v) \
+#define RtdUtcCounterPut(dev, n, v) \
writeb ((v) & 0xff, devpriv->las0 \
+ ((n <= 0) ? LAS0_UTC0 : ((1 == n) ? LAS0_UTC1 : LAS0_UTC2)))
/* Set UTC (8254) control byte */
-#define RtdUtcCtrlPut(dev,n,v) \
+#define RtdUtcCtrlPut(dev, n, v) \
writeb (devpriv->utcCtrl[(n) & 3] = (((n) & 3) << 6) | ((v) & 0x3f), \
devpriv->las0 + LAS0_UTC_CTRL)
/* Set UTCn clock source (write only) */
-#define RtdUtcClockSource(dev,n,v) \
+#define RtdUtcClockSource(dev, n, v) \
writew (v, devpriv->las0 \
+ ((n <= 0) ? LAS0_UTC0_CLOCK : \
((1 == n) ? LAS0_UTC1_CLOCK : LAS0_UTC2_CLOCK)))
/* Set UTCn gate source (write only) */
-#define RtdUtcGateSource(dev,n,v) \
+#define RtdUtcGateSource(dev, n, v) \
writew (v, devpriv->las0 \
+ ((n <= 0) ? LAS0_UTC0_GATE : \
((1 == n) ? LAS0_UTC1_GATE : LAS0_UTC2_GATE)))
/* User output N source select (write only) */
-#define RtdUsrOutSource(dev,n,v) \
- writel (v,devpriv->las0+((n <= 0) ? LAS0_UOUT0_SELECT : LAS0_UOUT1_SELECT))
+#define RtdUsrOutSource(dev, n, v) \
+ writel (v, devpriv->las0+((n <= 0) ? LAS0_UOUT0_SELECT : LAS0_UOUT1_SELECT))
/* Digital IO */
#define RtdDio0Read(dev) \
(readw (devpriv->las0+LAS0_DIO0) & 0xff)
-#define RtdDio0Write(dev,v) \
+#define RtdDio0Write(dev, v) \
writew ((v) & 0xff, devpriv->las0+LAS0_DIO0)
#define RtdDio1Read(dev) \
(readw (devpriv->las0+LAS0_DIO1) & 0xff)
-#define RtdDio1Write(dev,v) \
+#define RtdDio1Write(dev, v) \
writew ((v) & 0xff, devpriv->las0+LAS0_DIO1)
#define RtdDioStatusRead(dev) \
(readw (devpriv->las0+LAS0_DIO_STATUS) & 0xff)
-#define RtdDioStatusWrite(dev,v) \
+#define RtdDioStatusWrite(dev, v) \
writew ((devpriv->dioStatus = (v)), devpriv->las0+LAS0_DIO_STATUS)
#define RtdDio0CtrlRead(dev) \
(readw (devpriv->las0+LAS0_DIO0_CTRL) & 0xff)
-#define RtdDio0CtrlWrite(dev,v) \
+#define RtdDio0CtrlWrite(dev, v) \
writew ((v) & 0xff, devpriv->las0+LAS0_DIO0_CTRL)
/* Digital to Analog converter */
/* Write one data value (sign + 12bit + marker bits) */
/* Note: matches what DMA would put. Actual value << 3 */
-#define RtdDacFifoPut(dev,n,v) \
+#define RtdDacFifoPut(dev, n, v) \
writew ((v), devpriv->las1 +(((n) == 0) ? LAS1_DAC1_FIFO : LAS1_DAC2_FIFO))
/* Start single DAC conversion */
-#define RtdDacUpdate(dev,n) \
+#define RtdDacUpdate(dev, n) \
writew (0, devpriv->las0 +(((n) == 0) ? LAS0_DAC1 : LAS0_DAC2))
/* Start single DAC conversion on both DACs */
@@ -588,20 +588,20 @@ typedef struct {
writew (0, devpriv->las0+LAS0_DAC)
/* Set DAC output type and range */
-#define RtdDacRange(dev,n,v) \
+#define RtdDacRange(dev, n, v) \
writew ((v) & 7, devpriv->las0 \
+(((n) == 0) ? LAS0_DAC1_CTRL : LAS0_DAC2_CTRL))
/* Reset DAC FIFO */
-#define RtdDacClearFifo(dev,n) \
+#define RtdDacClearFifo(dev, n) \
writel (0, devpriv->las0+(((n) == 0) ? LAS0_DAC1_RESET : LAS0_DAC2_RESET))
/* Set source for DMA 0 (write only, shadow?) */
-#define RtdDma0Source(dev,n) \
+#define RtdDma0Source(dev, n) \
writel ((n) & 0xf, devpriv->las0+LAS0_DMA0_SRC)
/* Set source for DMA 1 (write only, shadow?) */
-#define RtdDma1Source(dev,n) \
+#define RtdDma1Source(dev, n) \
writel ((n) & 0xf, devpriv->las0+LAS0_DMA1_SRC)
/* Reset board state for DMA 0 */
@@ -615,51 +615,51 @@ typedef struct {
/* PLX9080 interrupt mask and status */
#define RtdPlxInterruptRead(dev) \
readl (devpriv->lcfg+LCFG_ITCSR)
-#define RtdPlxInterruptWrite(dev,v) \
+#define RtdPlxInterruptWrite(dev, v) \
writel (v, devpriv->lcfg+LCFG_ITCSR)
/* Set mode for DMA 0 */
-#define RtdDma0Mode(dev,m) \
+#define RtdDma0Mode(dev, m) \
writel ((m), devpriv->lcfg+LCFG_DMAMODE0)
/* Set PCI address for DMA 0 */
-#define RtdDma0PciAddr(dev,a) \
+#define RtdDma0PciAddr(dev, a) \
writel ((a), devpriv->lcfg+LCFG_DMAPADR0)
/* Set local address for DMA 0 */
-#define RtdDma0LocalAddr(dev,a) \
+#define RtdDma0LocalAddr(dev, a) \
writel ((a), devpriv->lcfg+LCFG_DMALADR0)
/* Set byte count for DMA 0 */
-#define RtdDma0Count(dev,c) \
+#define RtdDma0Count(dev, c) \
writel ((c), devpriv->lcfg+LCFG_DMASIZ0)
/* Set next descriptor for DMA 0 */
-#define RtdDma0Next(dev,a) \
+#define RtdDma0Next(dev, a) \
writel ((a), devpriv->lcfg+LCFG_DMADPR0)
/* Set mode for DMA 1 */
-#define RtdDma1Mode(dev,m) \
+#define RtdDma1Mode(dev, m) \
writel ((m), devpriv->lcfg+LCFG_DMAMODE1)
/* Set PCI address for DMA 1 */
-#define RtdDma1PciAddr(dev,a) \
+#define RtdDma1PciAddr(dev, a) \
writel ((a), devpriv->lcfg+LCFG_DMAADR1)
/* Set local address for DMA 1 */
-#define RtdDma1LocalAddr(dev,a) \
+#define RtdDma1LocalAddr(dev, a) \
writel ((a), devpriv->lcfg+LCFG_DMALADR1)
/* Set byte count for DMA 1 */
-#define RtdDma1Count(dev,c) \
+#define RtdDma1Count(dev, c) \
writel ((c), devpriv->lcfg+LCFG_DMASIZ1)
/* Set next descriptor for DMA 1 */
-#define RtdDma1Next(dev,a) \
+#define RtdDma1Next(dev, a) \
writel ((a), devpriv->lcfg+LCFG_DMADPR1)
/* Set control for DMA 0 (write only, shadow?) */
-#define RtdDma0Control(dev,n) \
+#define RtdDma0Control(dev, n) \
writeb (devpriv->dma0Control = (n), devpriv->lcfg+LCFG_DMACSR0)
/* Get status for DMA 0 */
@@ -667,7 +667,7 @@ typedef struct {
readb (devpriv->lcfg+LCFG_DMACSR0)
/* Set control for DMA 1 (write only, shadow?) */
-#define RtdDma1Control(dev,n) \
+#define RtdDma1Control(dev, n) \
writeb (devpriv->dma1Control = (n), devpriv->lcfg+LCFG_DMACSR1)
/* Get status for DMA 1 */
@@ -675,39 +675,39 @@ typedef struct {
readb (devpriv->lcfg+LCFG_DMACSR1)
/*
- * The comedi_driver structure tells the Comedi core module
+ * The struct comedi_driver structure tells the Comedi core module
* which functions to call to configure/deconfigure (attac/detach)
* the board, and also about the kernel module that contains
* the device code.
*/
-static int rtd_attach(comedi_device * dev, comedi_devconfig * it);
-static int rtd_detach(comedi_device * dev);
+static int rtd_attach(struct comedi_device *dev, struct comedi_devconfig *it);
+static int rtd_detach(struct comedi_device *dev);
-static comedi_driver rtd520Driver = {
+static struct comedi_driver rtd520Driver = {
driver_name: DRV_NAME,
- module:THIS_MODULE,
- attach:rtd_attach,
- detach:rtd_detach,
+ module : THIS_MODULE,
+ attach : rtd_attach,
+ detach : rtd_detach,
};
-static int rtd_ai_rinsn(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data);
-static int rtd_ao_winsn(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data);
-static int rtd_ao_rinsn(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data);
-static int rtd_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data);
-static int rtd_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data);
-static int rtd_ai_cmdtest(comedi_device * dev, comedi_subdevice * s,
- comedi_cmd * cmd);
-static int rtd_ai_cmd(comedi_device * dev, comedi_subdevice * s);
-static int rtd_ai_cancel(comedi_device * dev, comedi_subdevice * s);
-//static int rtd_ai_poll (comedi_device *dev,comedi_subdevice *s);
+static int rtd_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int rtd_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int rtd_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int rtd_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int rtd_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int rtd_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd);
+static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
+static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
+/* static int rtd_ai_poll (struct comedi_device *dev,struct comedi_subdevice *s); */
static int rtd_ns_to_timer(unsigned int *ns, int roundMode);
static irqreturn_t rtd_interrupt(int irq, void *d PT_REGS_ARG);
-static int rtd520_probe_fifo_depth(comedi_device *dev);
+static int rtd520_probe_fifo_depth(struct comedi_device *dev);
/*
* Attach is called by the Comedi core to configure the driver
@@ -715,9 +715,9 @@ static int rtd520_probe_fifo_depth(comedi_device *dev);
* in the driver structure, dev->board_ptr contains that
* address.
*/
-static int rtd_attach(comedi_device * dev, comedi_devconfig * it)
+static int rtd_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{ /* board name and options flags */
- comedi_subdevice *s;
+ struct comedi_subdevice *s;
struct pci_dev *pcidev;
int ret;
resource_size_t physLas0; /* configuation */
@@ -739,7 +739,7 @@ static int rtd_attach(comedi_device * dev, comedi_devconfig * it)
* Allocate the private structure area. alloc_private() is a
* convenient macro defined in comedidev.h.
*/
- if (alloc_private(dev, sizeof(rtdPrivate)) < 0)
+ if (alloc_private(dev, sizeof(struct rtdPrivate)) < 0)
return -ENOMEM;
/*
@@ -779,7 +779,8 @@ static int rtd_attach(comedi_device * dev, comedi_devconfig * it)
devpriv->pci_dev = pcidev;
dev->board_name = thisboard->name;
- if ((ret = comedi_pci_enable(pcidev, DRV_NAME)) < 0) {
+ ret = comedi_pci_enable(pcidev, DRV_NAME);
+ if (ret < 0) {
printk("Failed to enable PCI device and request regions.\n");
return ret;
}
@@ -866,7 +867,7 @@ static int rtd_attach(comedi_device * dev, comedi_devconfig * it)
s->do_cmd = rtd_ai_cmd;
s->do_cmdtest = rtd_ai_cmdtest;
s->cancel = rtd_ai_cancel;
- /*s->poll = rtd_ai_poll; *//* not ready yet */
+ /* s->poll = rtd_ai_poll; */ /* not ready yet */
s = dev->subdevices + 1;
/* analog output subdevice */
@@ -918,8 +919,10 @@ static int rtd_attach(comedi_device * dev, comedi_devconfig * it)
/* TODO: set user out source ??? */
/* check if our interrupt is available and get it */
- if ((ret = comedi_request_irq(devpriv->pci_dev->irq, rtd_interrupt,
- IRQF_SHARED, DRV_NAME, dev)) < 0) {
+ ret = comedi_request_irq(devpriv->pci_dev->irq, rtd_interrupt,
+ IRQF_SHARED, DRV_NAME, dev);
+
+ if (ret < 0) {
printk("Could not get interrupt! (%u)\n",
devpriv->pci_dev->irq);
return ret;
@@ -1005,7 +1008,7 @@ static int rtd_attach(comedi_device * dev, comedi_devconfig * it)
#if 0
/* hit an error, clean up memory and return ret */
-//rtd_attach_die_error:
+/* rtd_attach_die_error: */
#ifdef USE_DMA
for (index = 0; index < DMA_CHAIN_COUNT; index++) {
if (NULL != devpriv->dma0Buff[index]) { /* free buffer memory */
@@ -1057,7 +1060,7 @@ static int rtd_attach(comedi_device * dev, comedi_devconfig * it)
* allocated by _attach(). dev->private and dev->subdevices are
* deallocated automatically by the core.
*/
-static int rtd_detach(comedi_device * dev)
+static int rtd_detach(struct comedi_device *dev)
{
#ifdef USE_DMA
int index;
@@ -1137,7 +1140,7 @@ static int rtd_detach(comedi_device * dev)
/*
Convert a single comedi channel-gain entry to a RTD520 table entry
*/
-static unsigned short rtdConvertChanGain(comedi_device * dev,
+static unsigned short rtdConvertChanGain(struct comedi_device *dev,
unsigned int comediChan, int chanIndex)
{ /* index in channel list */
unsigned int chan, range, aref;
@@ -1187,7 +1190,7 @@ static unsigned short rtdConvertChanGain(comedi_device * dev,
/*
Setup the channel-gain table from a comedi list
*/
-static void rtd_load_channelgain_list(comedi_device * dev,
+static void rtd_load_channelgain_list(struct comedi_device *dev,
unsigned int n_chan, unsigned int *list)
{
if (n_chan > 1) { /* setup channel gain table */
@@ -1206,9 +1209,9 @@ static void rtd_load_channelgain_list(comedi_device * dev,
/* determine fifo size by doing adc conversions until the fifo half
empty status flag clears */
-static int rtd520_probe_fifo_depth(comedi_device *dev)
+static int rtd520_probe_fifo_depth(struct comedi_device *dev)
{
- lsampl_t chanspec = CR_PACK(0, 0, AREF_GROUND);
+ unsigned int chanspec = CR_PACK(0, 0, AREF_GROUND);
unsigned i;
static const unsigned limit = 0x2000;
unsigned fifo_size = 0;
@@ -1234,7 +1237,7 @@ static int rtd520_probe_fifo_depth(comedi_device *dev)
return -EIO;
}
RtdAdcClearFifo(dev);
- if(fifo_size != 0x400 || fifo_size != 0x2000)
+ if(fifo_size != 0x400 && fifo_size != 0x2000)
{
rt_printk("\ncomedi: %s: unexpected fifo size of %i, expected 1024 or 8192.\n",
DRV_NAME, fifo_size);
@@ -1251,8 +1254,8 @@ static int rtd520_probe_fifo_depth(comedi_device *dev)
Note, we don't do any settling delays. Use a instruction list to
select, delay, then read.
*/
-static int rtd_ai_rinsn(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+static int rtd_ai_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
{
int n, ii;
int stat;
@@ -1304,12 +1307,12 @@ static int rtd_ai_rinsn(comedi_device * dev,
The manual claims that we can do a lword read, but it doesn't work here.
*/
-static int ai_read_n(comedi_device * dev, comedi_subdevice * s, int count)
+static int ai_read_n(struct comedi_device *dev, struct comedi_subdevice *s, int count)
{
int ii;
for (ii = 0; ii < count; ii++) {
- sampl_t sample;
+ short sample;
s16 d;
if (0 == devpriv->aiCount) { /* done */
@@ -1343,10 +1346,10 @@ static int ai_read_n(comedi_device * dev, comedi_subdevice * s, int count)
/*
unknown amout of data is waiting in fifo.
*/
-static int ai_read_dregs(comedi_device * dev, comedi_subdevice * s)
+static int ai_read_dregs(struct comedi_device *dev, struct comedi_subdevice *s)
{
while (RtdFifoStatus(dev) & FS_ADC_NOT_EMPTY) { /* 1 -> not empty */
- sampl_t sample;
+ short sample;
s16 d = RtdAdcFifoGet(dev); /* get 2s comp value */
if (0 == devpriv->aiCount) { /* done */
@@ -1372,20 +1375,20 @@ static int ai_read_dregs(comedi_device * dev, comedi_subdevice * s)
/*
Terminate a DMA transfer and wait for everything to quiet down
*/
-void abort_dma(comedi_device * dev, unsigned int channel)
+void abort_dma(struct comedi_device *dev, unsigned int channel)
{ /* DMA channel 0, 1 */
unsigned long dma_cs_addr; /* the control/status register */
uint8_t status;
unsigned int ii;
- //unsigned long flags;
+ /* unsigned long flags; */
dma_cs_addr = (unsigned long)devpriv->lcfg
+ ((channel == 0) ? LCFG_DMACSR0 : LCFG_DMACSR1);
- // spinlock for plx dma control/status reg
- //comedi_spin_lock_irqsave( &dev->spinlock, flags );
+ /* spinlock for plx dma control/status reg */
+ /* comedi_spin_lock_irqsave( &dev->spinlock, flags ); */
- // abort dma transfer if necessary
+ /* abort dma transfer if necessary */
status = readb(dma_cs_addr);
if ((status & PLX_DMA_EN_BIT) == 0) { /* not enabled (Error?) */
DPRINTK("rtd520: AbortDma on non-active channel %d (0x%x)\n",
@@ -1410,7 +1413,7 @@ void abort_dma(comedi_device * dev, unsigned int channel)
/* set abort bit for channel */
writeb(PLX_DMA_ABORT_BIT, dma_cs_addr);
- // wait for dma done bit to be set
+ /* wait for dma done bit to be set */
status = readb(dma_cs_addr);
for (ii = 0;
(status & PLX_DMA_DONE_BIT) == 0 && ii < RTD_DMA_TIMEOUT;
@@ -1424,14 +1427,14 @@ void abort_dma(comedi_device * dev, unsigned int channel)
}
abortDmaExit:
- //comedi_spin_unlock_irqrestore( &dev->spinlock, flags );
+ /* comedi_spin_unlock_irqrestore( &dev->spinlock, flags ); */
}
/*
Process what is in the DMA transfer buffer and pass to comedi
Note: this is not re-entrant
*/
-static int ai_process_dma(comedi_device * dev, comedi_subdevice * s)
+static int ai_process_dma(struct comedi_device *dev, struct comedi_subdevice *s)
{
int ii, n;
s16 *dp;
@@ -1441,7 +1444,7 @@ static int ai_process_dma(comedi_device * dev, comedi_subdevice * s)
dp = devpriv->dma0Buff[devpriv->dma0Offset];
for (ii = 0; ii < devpriv->fifoLen / 2;) { /* convert samples */
- sampl_t sample;
+ short sample;
if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) {
sample = (*dp >> 3) + 2048; /* convert to comedi unsigned data */
@@ -1494,10 +1497,10 @@ static irqreturn_t rtd_interrupt(int irq, /* interrupt number (ignored) */
void *d /* our data */
PT_REGS_ARG)
{ /* cpu context (ignored) */
- comedi_device *dev = d; /* must be called "dev" for devpriv */
+ struct comedi_device *dev = d; /* must be called "dev" for devpriv */
u16 status;
u16 fifoStatus;
- comedi_subdevice *s = dev->subdevices + 0; /* analog in subdevice */
+ struct comedi_subdevice *s = dev->subdevices + 0; /* analog in subdevice */
if (!dev->attached) {
return IRQ_NONE;
@@ -1645,7 +1648,7 @@ static irqreturn_t rtd_interrupt(int irq, /* interrupt number (ignored) */
/*
return the number of samples available
*/
-static int rtd_ai_poll(comedi_device * dev, comedi_subdevice * s)
+static int rtd_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
{
/* TODO: This needs to mask interrupts, read_dregs, and then re-enable */
/* Not sure what to do if DMA is active */
@@ -1662,8 +1665,8 @@ static int rtd_ai_poll(comedi_device * dev, comedi_subdevice * s)
the command passes.
*/
-static int rtd_ai_cmdtest(comedi_device * dev,
- comedi_subdevice * s, comedi_cmd * cmd)
+static int rtd_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
int err = 0;
int tmp;
@@ -1867,9 +1870,9 @@ static int rtd_ai_cmdtest(comedi_device * dev,
This is usually done by an interrupt handler.
Userland gets to the data using read calls.
*/
-static int rtd_ai_cmd(comedi_device * dev, comedi_subdevice * s)
+static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
- comedi_cmd *cmd = &s->async->cmd;
+ struct comedi_cmd *cmd = &s->async->cmd;
int timer;
/* stop anything currently running */
@@ -2064,7 +2067,7 @@ static int rtd_ai_cmd(comedi_device * dev, comedi_subdevice * s)
/*
Stop a running data aquisition.
*/
-static int rtd_ai_cancel(comedi_device * dev, comedi_subdevice * s)
+static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
u16 status;
@@ -2132,8 +2135,8 @@ static int rtd_ns_to_timer(unsigned int *ns, int round_mode)
/*
Output one (or more) analog values to a single port as fast as possible.
*/
-static int rtd_ao_winsn(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+static int rtd_ao_winsn(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
{
int i;
int chan = CR_CHAN(insn->chanspec);
@@ -2152,7 +2155,7 @@ static int rtd_ao_winsn(comedi_device * dev,
/* VERIFY: comedi range and offset conversions */
if ((range > 1) /* bipolar */
- &&(data[i] < 2048)) {
+ && (data[i] < 2048)) {
/* offset and sign extend */
val = (((int)data[i]) - 2048) << 3;
} else { /* unipolor */
@@ -2187,8 +2190,8 @@ static int rtd_ao_winsn(comedi_device * dev,
/* AO subdevices should have a read insn as well as a write insn.
* Usually this means copying a value stored in devpriv. */
-static int rtd_ao_rinsn(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+static int rtd_ao_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
{
int i;
int chan = CR_CHAN(insn->chanspec);
@@ -2210,8 +2213,8 @@ static int rtd_ao_rinsn(comedi_device * dev,
* This allows packed reading/writing of the DIO channels. The
* comedi core can convert between insn_bits and insn_read/write
*/
-static int rtd_dio_insn_bits(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+static int rtd_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
{
if (insn->n != 2)
return -EINVAL;
@@ -2237,8 +2240,8 @@ static int rtd_dio_insn_bits(comedi_device * dev,
/*
Configure one bit on a IO port as Input or Output (hence the name :-).
*/
-static int rtd_dio_insn_config(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+static int rtd_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
{
int chan = CR_CHAN(insn->chanspec);
diff --git a/drivers/staging/comedi/drivers/rtd520.h b/drivers/staging/comedi/drivers/rtd520.h
index 0eb50b8e605e..a3ec2599c4b8 100644
--- a/drivers/staging/comedi/drivers/rtd520.h
+++ b/drivers/staging/comedi/drivers/rtd520.h
@@ -29,366 +29,366 @@
LAS0 Runtime Area
Local Address Space 0 Offset Read Function Write Function
*/
-#define LAS0_SPARE_00 0x0000 // - -
-#define LAS0_SPARE_04 0x0004 // - -
-#define LAS0_USER_IO 0x0008 // Read User Inputs Write User Outputs
-#define LAS0_SPARE_0C 0x000C // - -
-#define LAS0_ADC 0x0010 // Read FIFO Status Software A/D Start
-#define LAS0_DAC1 0x0014 // - Software D/A1 Update
-#define LAS0_DAC2 0x0018 // - Software D/A2 Update
-#define LAS0_SPARE_1C 0x001C // - -
-#define LAS0_SPARE_20 0x0020 // - -
-#define LAS0_DAC 0x0024 // - Software Simultaneous D/A1 and D/A2 Update
-#define LAS0_PACER 0x0028 // Software Pacer Start Software Pacer Stop
-#define LAS0_TIMER 0x002C // Read Timer Counters Status HDIN Software Trigger
-#define LAS0_IT 0x0030 // Read Interrupt Status Write Interrupt Enable Mask Register
-#define LAS0_CLEAR 0x0034 // Clear ITs set by Clear Mask Set Interrupt Clear Mask
-#define LAS0_OVERRUN 0x0038 // Read pending interrupts Clear Overrun Register
-#define LAS0_SPARE_3C 0x003C // - -
+#define LAS0_SPARE_00 0x0000 /* - - */
+#define LAS0_SPARE_04 0x0004 /* - - */
+#define LAS0_USER_IO 0x0008 /* Read User Inputs Write User Outputs */
+#define LAS0_SPARE_0C 0x000C /* - - */
+#define LAS0_ADC 0x0010 /* Read FIFO Status Software A/D Start */
+#define LAS0_DAC1 0x0014 /* - Software D/A1 Update */
+#define LAS0_DAC2 0x0018 /* - Software D/A2 Update */
+#define LAS0_SPARE_1C 0x001C /* - - */
+#define LAS0_SPARE_20 0x0020 /* - - */
+#define LAS0_DAC 0x0024 /* - Software Simultaneous D/A1 and D/A2 Update */
+#define LAS0_PACER 0x0028 /* Software Pacer Start Software Pacer Stop */
+#define LAS0_TIMER 0x002C /* Read Timer Counters Status HDIN Software Trigger */
+#define LAS0_IT 0x0030 /* Read Interrupt Status Write Interrupt Enable Mask Register */
+#define LAS0_CLEAR 0x0034 /* Clear ITs set by Clear Mask Set Interrupt Clear Mask */
+#define LAS0_OVERRUN 0x0038 /* Read pending interrupts Clear Overrun Register */
+#define LAS0_SPARE_3C 0x003C /* - - */
/*
LAS0 Runtime Area Timer/Counter,Dig.IO
Name Local Address Function
*/
-#define LAS0_PCLK 0x0040 // Pacer Clock value (24bit) Pacer Clock load (24bit)
-#define LAS0_BCLK 0x0044 // Burst Clock value (10bit) Burst Clock load (10bit)
-#define LAS0_ADC_SCNT 0x0048 // A/D Sample counter value (10bit) A/D Sample counter load (10bit)
-#define LAS0_DAC1_UCNT 0x004C // D/A1 Update counter value (10 bit) D/A1 Update counter load (10bit)
-#define LAS0_DAC2_UCNT 0x0050 // D/A2 Update counter value (10 bit) D/A2 Update counter load (10bit)
-#define LAS0_DCNT 0x0054 // Delay counter value (16 bit) Delay counter load (16bit)
-#define LAS0_ACNT 0x0058 // About counter value (16 bit) About counter load (16bit)
-#define LAS0_DAC_CLK 0x005C // DAC clock value (16bit) DAC clock load (16bit)
-#define LAS0_UTC0 0x0060 // 8254 TC Counter 0 User TC 0 value Load count in TC Counter 0
-#define LAS0_UTC1 0x0064 // 8254 TC Counter 1 User TC 1 value Load count in TC Counter 1
-#define LAS0_UTC2 0x0068 // 8254 TC Counter 2 User TC 2 value Load count in TC Counter 2
-#define LAS0_UTC_CTRL 0x006C // 8254 TC Control Word Program counter mode for TC
-#define LAS0_DIO0 0x0070 // Digital I/O Port 0 Read Port Digital I/O Port 0 Write Port
-#define LAS0_DIO1 0x0074 // Digital I/O Port 1 Read Port Digital I/O Port 1 Write Port
-#define LAS0_DIO0_CTRL 0x0078 // Clear digital IRQ status flag/read Clear digital chip/program Port 0
-#define LAS0_DIO_STATUS 0x007C // Read Digital I/O Status word Program digital control register &
+#define LAS0_PCLK 0x0040 /* Pacer Clock value (24bit) Pacer Clock load (24bit) */
+#define LAS0_BCLK 0x0044 /* Burst Clock value (10bit) Burst Clock load (10bit) */
+#define LAS0_ADC_SCNT 0x0048 /* A/D Sample counter value (10bit) A/D Sample counter load (10bit) */
+#define LAS0_DAC1_UCNT 0x004C /* D/A1 Update counter value (10 bit) D/A1 Update counter load (10bit) */
+#define LAS0_DAC2_UCNT 0x0050 /* D/A2 Update counter value (10 bit) D/A2 Update counter load (10bit) */
+#define LAS0_DCNT 0x0054 /* Delay counter value (16 bit) Delay counter load (16bit) */
+#define LAS0_ACNT 0x0058 /* About counter value (16 bit) About counter load (16bit) */
+#define LAS0_DAC_CLK 0x005C /* DAC clock value (16bit) DAC clock load (16bit) */
+#define LAS0_UTC0 0x0060 /* 8254 TC Counter 0 User TC 0 value Load count in TC Counter 0 */
+#define LAS0_UTC1 0x0064 /* 8254 TC Counter 1 User TC 1 value Load count in TC Counter 1 */
+#define LAS0_UTC2 0x0068 /* 8254 TC Counter 2 User TC 2 value Load count in TC Counter 2 */
+#define LAS0_UTC_CTRL 0x006C /* 8254 TC Control Word Program counter mode for TC */
+#define LAS0_DIO0 0x0070 /* Digital I/O Port 0 Read Port Digital I/O Port 0 Write Port */
+#define LAS0_DIO1 0x0074 /* Digital I/O Port 1 Read Port Digital I/O Port 1 Write Port */
+#define LAS0_DIO0_CTRL 0x0078 /* Clear digital IRQ status flag/read Clear digital chip/program Port 0 */
+#define LAS0_DIO_STATUS 0x007C /* Read Digital I/O Status word Program digital control register & */
/*
LAS0 Setup Area
Name Local Address Function
*/
-#define LAS0_BOARD_RESET 0x0100 // Board reset
-#define LAS0_DMA0_SRC 0x0104 // DMA 0 Sources select
-#define LAS0_DMA1_SRC 0x0108 // DMA 1 Sources select
-#define LAS0_ADC_CONVERSION 0x010C // A/D Conversion Signal select
-#define LAS0_BURST_START 0x0110 // Burst Clock Start Trigger select
-#define LAS0_PACER_START 0x0114 // Pacer Clock Start Trigger select
-#define LAS0_PACER_STOP 0x0118 // Pacer Clock Stop Trigger select
-#define LAS0_ACNT_STOP_ENABLE 0x011C // About Counter Stop Enable
-#define LAS0_PACER_REPEAT 0x0120 // Pacer Start Trigger Mode select
-#define LAS0_DIN_START 0x0124 // High Speed Digital Input Sampling Signal select
-#define LAS0_DIN_FIFO_CLEAR 0x0128 // Digital Input FIFO Clear
-#define LAS0_ADC_FIFO_CLEAR 0x012C // A/D FIFO Clear
-#define LAS0_CGT_WRITE 0x0130 // Channel Gain Table Write
-#define LAS0_CGL_WRITE 0x0134 // Channel Gain Latch Write
-#define LAS0_CG_DATA 0x0138 // Digital Table Write
-#define LAS0_CGT_ENABLE 0x013C // Channel Gain Table Enable
-#define LAS0_CG_ENABLE 0x0140 // Digital Table Enable
-#define LAS0_CGT_PAUSE 0x0144 // Table Pause Enable
-#define LAS0_CGT_RESET 0x0148 // Reset Channel Gain Table
-#define LAS0_CGT_CLEAR 0x014C // Clear Channel Gain Table
-#define LAS0_DAC1_CTRL 0x0150 // D/A1 output type/range
-#define LAS0_DAC1_SRC 0x0154 // D/A1 update source
-#define LAS0_DAC1_CYCLE 0x0158 // D/A1 cycle mode
-#define LAS0_DAC1_RESET 0x015C // D/A1 FIFO reset
-#define LAS0_DAC1_FIFO_CLEAR 0x0160 // D/A1 FIFO clear
-#define LAS0_DAC2_CTRL 0x0164 // D/A2 output type/range
-#define LAS0_DAC2_SRC 0x0168 // D/A2 update source
-#define LAS0_DAC2_CYCLE 0x016C // D/A2 cycle mode
-#define LAS0_DAC2_RESET 0x0170 // D/A2 FIFO reset
-#define LAS0_DAC2_FIFO_CLEAR 0x0174 // D/A2 FIFO clear
-#define LAS0_ADC_SCNT_SRC 0x0178 // A/D Sample Counter Source select
-#define LAS0_PACER_SELECT 0x0180 // Pacer Clock select
-#define LAS0_SBUS0_SRC 0x0184 // SyncBus 0 Source select
-#define LAS0_SBUS0_ENABLE 0x0188 // SyncBus 0 enable
-#define LAS0_SBUS1_SRC 0x018C // SyncBus 1 Source select
-#define LAS0_SBUS1_ENABLE 0x0190 // SyncBus 1 enable
-#define LAS0_SBUS2_SRC 0x0198 // SyncBus 2 Source select
-#define LAS0_SBUS2_ENABLE 0x019C // SyncBus 2 enable
-#define LAS0_ETRG_POLARITY 0x01A4 // External Trigger polarity select
-#define LAS0_EINT_POLARITY 0x01A8 // External Interrupt polarity select
-#define LAS0_UTC0_CLOCK 0x01AC // UTC0 Clock select
-#define LAS0_UTC0_GATE 0x01B0 // UTC0 Gate select
-#define LAS0_UTC1_CLOCK 0x01B4 // UTC1 Clock select
-#define LAS0_UTC1_GATE 0x01B8 // UTC1 Gate select
-#define LAS0_UTC2_CLOCK 0x01BC // UTC2 Clock select
-#define LAS0_UTC2_GATE 0x01C0 // UTC2 Gate select
-#define LAS0_UOUT0_SELECT 0x01C4 // User Output 0 source select
-#define LAS0_UOUT1_SELECT 0x01C8 // User Output 1 source select
-#define LAS0_DMA0_RESET 0x01CC // DMA0 Request state machine reset
-#define LAS0_DMA1_RESET 0x01D0 // DMA1 Request state machine reset
+#define LAS0_BOARD_RESET 0x0100 /* Board reset */
+#define LAS0_DMA0_SRC 0x0104 /* DMA 0 Sources select */
+#define LAS0_DMA1_SRC 0x0108 /* DMA 1 Sources select */
+#define LAS0_ADC_CONVERSION 0x010C /* A/D Conversion Signal select */
+#define LAS0_BURST_START 0x0110 /* Burst Clock Start Trigger select */
+#define LAS0_PACER_START 0x0114 /* Pacer Clock Start Trigger select */
+#define LAS0_PACER_STOP 0x0118 /* Pacer Clock Stop Trigger select */
+#define LAS0_ACNT_STOP_ENABLE 0x011C /* About Counter Stop Enable */
+#define LAS0_PACER_REPEAT 0x0120 /* Pacer Start Trigger Mode select */
+#define LAS0_DIN_START 0x0124 /* High Speed Digital Input Sampling Signal select */
+#define LAS0_DIN_FIFO_CLEAR 0x0128 /* Digital Input FIFO Clear */
+#define LAS0_ADC_FIFO_CLEAR 0x012C /* A/D FIFO Clear */
+#define LAS0_CGT_WRITE 0x0130 /* Channel Gain Table Write */
+#define LAS0_CGL_WRITE 0x0134 /* Channel Gain Latch Write */
+#define LAS0_CG_DATA 0x0138 /* Digital Table Write */
+#define LAS0_CGT_ENABLE 0x013C /* Channel Gain Table Enable */
+#define LAS0_CG_ENABLE 0x0140 /* Digital Table Enable */
+#define LAS0_CGT_PAUSE 0x0144 /* Table Pause Enable */
+#define LAS0_CGT_RESET 0x0148 /* Reset Channel Gain Table */
+#define LAS0_CGT_CLEAR 0x014C /* Clear Channel Gain Table */
+#define LAS0_DAC1_CTRL 0x0150 /* D/A1 output type/range */
+#define LAS0_DAC1_SRC 0x0154 /* D/A1 update source */
+#define LAS0_DAC1_CYCLE 0x0158 /* D/A1 cycle mode */
+#define LAS0_DAC1_RESET 0x015C /* D/A1 FIFO reset */
+#define LAS0_DAC1_FIFO_CLEAR 0x0160 /* D/A1 FIFO clear */
+#define LAS0_DAC2_CTRL 0x0164 /* D/A2 output type/range */
+#define LAS0_DAC2_SRC 0x0168 /* D/A2 update source */
+#define LAS0_DAC2_CYCLE 0x016C /* D/A2 cycle mode */
+#define LAS0_DAC2_RESET 0x0170 /* D/A2 FIFO reset */
+#define LAS0_DAC2_FIFO_CLEAR 0x0174 /* D/A2 FIFO clear */
+#define LAS0_ADC_SCNT_SRC 0x0178 /* A/D Sample Counter Source select */
+#define LAS0_PACER_SELECT 0x0180 /* Pacer Clock select */
+#define LAS0_SBUS0_SRC 0x0184 /* SyncBus 0 Source select */
+#define LAS0_SBUS0_ENABLE 0x0188 /* SyncBus 0 enable */
+#define LAS0_SBUS1_SRC 0x018C /* SyncBus 1 Source select */
+#define LAS0_SBUS1_ENABLE 0x0190 /* SyncBus 1 enable */
+#define LAS0_SBUS2_SRC 0x0198 /* SyncBus 2 Source select */
+#define LAS0_SBUS2_ENABLE 0x019C /* SyncBus 2 enable */
+#define LAS0_ETRG_POLARITY 0x01A4 /* External Trigger polarity select */
+#define LAS0_EINT_POLARITY 0x01A8 /* External Interrupt polarity select */
+#define LAS0_UTC0_CLOCK 0x01AC /* UTC0 Clock select */
+#define LAS0_UTC0_GATE 0x01B0 /* UTC0 Gate select */
+#define LAS0_UTC1_CLOCK 0x01B4 /* UTC1 Clock select */
+#define LAS0_UTC1_GATE 0x01B8 /* UTC1 Gate select */
+#define LAS0_UTC2_CLOCK 0x01BC /* UTC2 Clock select */
+#define LAS0_UTC2_GATE 0x01C0 /* UTC2 Gate select */
+#define LAS0_UOUT0_SELECT 0x01C4 /* User Output 0 source select */
+#define LAS0_UOUT1_SELECT 0x01C8 /* User Output 1 source select */
+#define LAS0_DMA0_RESET 0x01CC /* DMA0 Request state machine reset */
+#define LAS0_DMA1_RESET 0x01D0 /* DMA1 Request state machine reset */
/*
LAS1
Name Local Address Function
*/
-#define LAS1_ADC_FIFO 0x0000 // Read A/D FIFO (16bit) -
-#define LAS1_HDIO_FIFO 0x0004 // Read High Speed Digital Input FIFO (16bit) -
-#define LAS1_DAC1_FIFO 0x0008 // - Write D/A1 FIFO (16bit)
-#define LAS1_DAC2_FIFO 0x000C // - Write D/A2 FIFO (16bit)
+#define LAS1_ADC_FIFO 0x0000 /* Read A/D FIFO (16bit) - */
+#define LAS1_HDIO_FIFO 0x0004 /* Read High Speed Digital Input FIFO (16bit) - */
+#define LAS1_DAC1_FIFO 0x0008 /* - Write D/A1 FIFO (16bit) */
+#define LAS1_DAC2_FIFO 0x000C /* - Write D/A2 FIFO (16bit) */
/*
LCFG: PLX 9080 local config & runtime registers
Name Local Address Function
*/
-#define LCFG_ITCSR 0x0068 // INTCSR, Interrupt Control/Status Register
-#define LCFG_DMAMODE0 0x0080 // DMA Channel 0 Mode Register
-#define LCFG_DMAPADR0 0x0084 // DMA Channel 0 PCI Address Register
-#define LCFG_DMALADR0 0x0088 // DMA Channel 0 Local Address Reg
-#define LCFG_DMASIZ0 0x008C // DMA Channel 0 Transfer Size (Bytes) Register
-#define LCFG_DMADPR0 0x0090 // DMA Channel 0 Descriptor Pointer Register
-#define LCFG_DMAMODE1 0x0094 // DMA Channel 1 Mode Register
-#define LCFG_DMAPADR1 0x0098 // DMA Channel 1 PCI Address Register
-#define LCFG_DMALADR1 0x009C // DMA Channel 1 Local Address Register
-#define LCFG_DMASIZ1 0x00A0 // DMA Channel 1 Transfer Size (Bytes) Register
-#define LCFG_DMADPR1 0x00A4 // DMA Channel 1 Descriptor Pointer Register
-#define LCFG_DMACSR0 0x00A8 // DMA Channel 0 Command/Status Register
-#define LCFG_DMACSR1 0x00A9 // DMA Channel 0 Command/Status Register
-#define LCFG_DMAARB 0x00AC // DMA Arbitration Register
-#define LCFG_DMATHR 0x00B0 // DMA Threshold Register
+#define LCFG_ITCSR 0x0068 /* INTCSR, Interrupt Control/Status Register */
+#define LCFG_DMAMODE0 0x0080 /* DMA Channel 0 Mode Register */
+#define LCFG_DMAPADR0 0x0084 /* DMA Channel 0 PCI Address Register */
+#define LCFG_DMALADR0 0x0088 /* DMA Channel 0 Local Address Reg */
+#define LCFG_DMASIZ0 0x008C /* DMA Channel 0 Transfer Size (Bytes) Register */
+#define LCFG_DMADPR0 0x0090 /* DMA Channel 0 Descriptor Pointer Register */
+#define LCFG_DMAMODE1 0x0094 /* DMA Channel 1 Mode Register */
+#define LCFG_DMAPADR1 0x0098 /* DMA Channel 1 PCI Address Register */
+#define LCFG_DMALADR1 0x009C /* DMA Channel 1 Local Address Register */
+#define LCFG_DMASIZ1 0x00A0 /* DMA Channel 1 Transfer Size (Bytes) Register */
+#define LCFG_DMADPR1 0x00A4 /* DMA Channel 1 Descriptor Pointer Register */
+#define LCFG_DMACSR0 0x00A8 /* DMA Channel 0 Command/Status Register */
+#define LCFG_DMACSR1 0x00A9 /* DMA Channel 0 Command/Status Register */
+#define LCFG_DMAARB 0x00AC /* DMA Arbitration Register */
+#define LCFG_DMATHR 0x00B0 /* DMA Threshold Register */
/*======================================================================
Resister bit definitions
======================================================================*/
-// FIFO Status Word Bits (RtdFifoStatus)
-#define FS_DAC1_NOT_EMPTY 0x0001 // D0 - DAC1 FIFO not empty
-#define FS_DAC1_HEMPTY 0x0002 // D1 - DAC1 FIFO half empty
-#define FS_DAC1_NOT_FULL 0x0004 // D2 - DAC1 FIFO not full
-#define FS_DAC2_NOT_EMPTY 0x0010 // D4 - DAC2 FIFO not empty
-#define FS_DAC2_HEMPTY 0x0020 // D5 - DAC2 FIFO half empty
-#define FS_DAC2_NOT_FULL 0x0040 // D6 - DAC2 FIFO not full
-#define FS_ADC_NOT_EMPTY 0x0100 // D8 - ADC FIFO not empty
-#define FS_ADC_HEMPTY 0x0200 // D9 - ADC FIFO half empty
-#define FS_ADC_NOT_FULL 0x0400 // D10 - ADC FIFO not full
-#define FS_DIN_NOT_EMPTY 0x1000 // D12 - DIN FIFO not empty
-#define FS_DIN_HEMPTY 0x2000 // D13 - DIN FIFO half empty
-#define FS_DIN_NOT_FULL 0x4000 // D14 - DIN FIFO not full
-
-// Timer Status Word Bits (GetTimerStatus)
+/* FIFO Status Word Bits (RtdFifoStatus) */
+#define FS_DAC1_NOT_EMPTY 0x0001 /* D0 - DAC1 FIFO not empty */
+#define FS_DAC1_HEMPTY 0x0002 /* D1 - DAC1 FIFO half empty */
+#define FS_DAC1_NOT_FULL 0x0004 /* D2 - DAC1 FIFO not full */
+#define FS_DAC2_NOT_EMPTY 0x0010 /* D4 - DAC2 FIFO not empty */
+#define FS_DAC2_HEMPTY 0x0020 /* D5 - DAC2 FIFO half empty */
+#define FS_DAC2_NOT_FULL 0x0040 /* D6 - DAC2 FIFO not full */
+#define FS_ADC_NOT_EMPTY 0x0100 /* D8 - ADC FIFO not empty */
+#define FS_ADC_HEMPTY 0x0200 /* D9 - ADC FIFO half empty */
+#define FS_ADC_NOT_FULL 0x0400 /* D10 - ADC FIFO not full */
+#define FS_DIN_NOT_EMPTY 0x1000 /* D12 - DIN FIFO not empty */
+#define FS_DIN_HEMPTY 0x2000 /* D13 - DIN FIFO half empty */
+#define FS_DIN_NOT_FULL 0x4000 /* D14 - DIN FIFO not full */
+
+/* Timer Status Word Bits (GetTimerStatus) */
#define TS_PCLK_GATE 0x0001
-// D0 - Pacer Clock Gate [0 - gated, 1 - enabled]
+/* D0 - Pacer Clock Gate [0 - gated, 1 - enabled] */
#define TS_BCLK_GATE 0x0002
-// D1 - Burst Clock Gate [0 - disabled, 1 - running]
+/* D1 - Burst Clock Gate [0 - disabled, 1 - running] */
#define TS_DCNT_GATE 0x0004
-// D2 - Pacer Clock Delayed Start Trigger [0 - delay over, 1 - delay in
-// progress]
+/* D2 - Pacer Clock Delayed Start Trigger [0 - delay over, 1 - delay in */
+/* progress] */
#define TS_ACNT_GATE 0x0008
-// D3 - Pacer Clock About Trigger [0 - completed, 1 - in progress]
+/* D3 - Pacer Clock About Trigger [0 - completed, 1 - in progress] */
#define TS_PCLK_RUN 0x0010
-// D4 - Pacer Clock Shutdown Flag [0 - Pacer Clock cannot be start
-// triggered only by Software Pacer Start Command, 1 - Pacer Clock can
-// be start triggered]
-
-// External Trigger polarity select
-// External Interrupt polarity select
-#define POL_POSITIVE 0x0 // positive edge
-#define POL_NEGATIVE 0x1 // negative edge
-
-// User Output Signal select (SetUout0Source, SetUout1Source)
-#define UOUT_ADC 0x0 // A/D Conversion Signal
-#define UOUT_DAC1 0x1 // D/A1 Update
-#define UOUT_DAC2 0x2 // D/A2 Update
-#define UOUT_SOFTWARE 0x3 // Software Programmable
-
-// Pacer clock select (SetPacerSource)
-#define PCLK_INTERNAL 1 // Internal Pacer Clock
-#define PCLK_EXTERNAL 0 // External Pacer Clock
-
-// A/D Sample Counter Sources (SetAdcntSource, SetupSampleCounter)
-#define ADC_SCNT_CGT_RESET 0x0 // needs restart with StartPacer
+/* D4 - Pacer Clock Shutdown Flag [0 - Pacer Clock cannot be start */
+/* triggered only by Software Pacer Start Command, 1 - Pacer Clock can */
+/* be start triggered] */
+
+/* External Trigger polarity select */
+/* External Interrupt polarity select */
+#define POL_POSITIVE 0x0 /* positive edge */
+#define POL_NEGATIVE 0x1 /* negative edge */
+
+/* User Output Signal select (SetUout0Source, SetUout1Source) */
+#define UOUT_ADC 0x0 /* A/D Conversion Signal */
+#define UOUT_DAC1 0x1 /* D/A1 Update */
+#define UOUT_DAC2 0x2 /* D/A2 Update */
+#define UOUT_SOFTWARE 0x3 /* Software Programmable */
+
+/* Pacer clock select (SetPacerSource) */
+#define PCLK_INTERNAL 1 /* Internal Pacer Clock */
+#define PCLK_EXTERNAL 0 /* External Pacer Clock */
+
+/* A/D Sample Counter Sources (SetAdcntSource, SetupSampleCounter) */
+#define ADC_SCNT_CGT_RESET 0x0 /* needs restart with StartPacer */
#define ADC_SCNT_FIFO_WRITE 0x1
-// A/D Conversion Signal Select (for SetConversionSelect)
-#define ADC_START_SOFTWARE 0x0 // Software A/D Start
-#define ADC_START_PCLK 0x1 // Pacer Clock (Ext. Int. see Func.509)
-#define ADC_START_BCLK 0x2 // Burst Clock
-#define ADC_START_DIGITAL_IT 0x3 // Digital Interrupt
-#define ADC_START_DAC1_MARKER1 0x4 // D/A 1 Data Marker 1
-#define ADC_START_DAC2_MARKER1 0x5 // D/A 2 Data Marker 1
-#define ADC_START_SBUS0 0x6 // SyncBus 0
-#define ADC_START_SBUS1 0x7 // SyncBus 1
-#define ADC_START_SBUS2 0x8 // SyncBus 2
-
-// Burst Clock start trigger select (SetBurstStart)
-#define BCLK_START_SOFTWARE 0x0 // Software A/D Start (StartBurst)
-#define BCLK_START_PCLK 0x1 // Pacer Clock
-#define BCLK_START_ETRIG 0x2 // External Trigger
-#define BCLK_START_DIGITAL_IT 0x3 // Digital Interrupt
-#define BCLK_START_SBUS0 0x4 // SyncBus 0
-#define BCLK_START_SBUS1 0x5 // SyncBus 1
-#define BCLK_START_SBUS2 0x6 // SyncBus 2
-
-// Pacer Clock start trigger select (SetPacerStart)
-#define PCLK_START_SOFTWARE 0x0 // Software Pacer Start (StartPacer)
-#define PCLK_START_ETRIG 0x1 // External trigger
-#define PCLK_START_DIGITAL_IT 0x2 // Digital interrupt
-#define PCLK_START_UTC2 0x3 // User TC 2 out
-#define PCLK_START_SBUS0 0x4 // SyncBus 0
-#define PCLK_START_SBUS1 0x5 // SyncBus 1
-#define PCLK_START_SBUS2 0x6 // SyncBus 2
-#define PCLK_START_D_SOFTWARE 0x8 // Delayed Software Pacer Start
-#define PCLK_START_D_ETRIG 0x9 // Delayed external trigger
-#define PCLK_START_D_DIGITAL_IT 0xA // Delayed digital interrupt
-#define PCLK_START_D_UTC2 0xB // Delayed User TC 2 out
-#define PCLK_START_D_SBUS0 0xC // Delayed SyncBus 0
-#define PCLK_START_D_SBUS1 0xD // Delayed SyncBus 1
-#define PCLK_START_D_SBUS2 0xE // Delayed SyncBus 2
-#define PCLK_START_ETRIG_GATED 0xF // External Trigger Gated controlled mode
-
-// Pacer Clock Stop Trigger select (SetPacerStop)
-#define PCLK_STOP_SOFTWARE 0x0 // Software Pacer Stop (StopPacer)
-#define PCLK_STOP_ETRIG 0x1 // External Trigger
-#define PCLK_STOP_DIGITAL_IT 0x2 // Digital Interrupt
-#define PCLK_STOP_ACNT 0x3 // About Counter
-#define PCLK_STOP_UTC2 0x4 // User TC2 out
-#define PCLK_STOP_SBUS0 0x5 // SyncBus 0
-#define PCLK_STOP_SBUS1 0x6 // SyncBus 1
-#define PCLK_STOP_SBUS2 0x7 // SyncBus 2
-#define PCLK_STOP_A_SOFTWARE 0x8 // About Software Pacer Stop
-#define PCLK_STOP_A_ETRIG 0x9 // About External Trigger
-#define PCLK_STOP_A_DIGITAL_IT 0xA // About Digital Interrupt
-#define PCLK_STOP_A_UTC2 0xC // About User TC2 out
-#define PCLK_STOP_A_SBUS0 0xD // About SyncBus 0
-#define PCLK_STOP_A_SBUS1 0xE // About SyncBus 1
-#define PCLK_STOP_A_SBUS2 0xF // About SyncBus 2
-
-// About Counter Stop Enable
-#define ACNT_STOP 0x0 // stop enable
-#define ACNT_NO_STOP 0x1 // stop disabled
-
-// DAC update source (SetDAC1Start & SetDAC2Start)
-#define DAC_START_SOFTWARE 0x0 // Software Update
-#define DAC_START_CGT 0x1 // CGT controlled Update
-#define DAC_START_DAC_CLK 0x2 // D/A Clock
-#define DAC_START_EPCLK 0x3 // External Pacer Clock
-#define DAC_START_SBUS0 0x4 // SyncBus 0
-#define DAC_START_SBUS1 0x5 // SyncBus 1
-#define DAC_START_SBUS2 0x6 // SyncBus 2
-
-// DAC Cycle Mode (SetDAC1Cycle, SetDAC2Cycle, SetupDAC)
-#define DAC_CYCLE_SINGLE 0x0 // not cycle
-#define DAC_CYCLE_MULTI 0x1 // cycle
-
-// 8254 Operation Modes (Set8254Mode, SetupTimerCounter)
-#define M8254_EVENT_COUNTER 0 // Event Counter
-#define M8254_HW_ONE_SHOT 1 // Hardware-Retriggerable One-Shot
-#define M8254_RATE_GENERATOR 2 // Rate Generator
-#define M8254_SQUARE_WAVE 3 // Square Wave Mode
-#define M8254_SW_STROBE 4 // Software Triggered Strobe
-#define M8254_HW_STROBE 5 // Hardware Triggered Strobe (Retriggerable)
-
-// User Timer/Counter 0 Clock Select (SetUtc0Clock)
-#define CUTC0_8MHZ 0x0 // 8MHz
-#define CUTC0_EXT_TC_CLOCK1 0x1 // Ext. TC Clock 1
-#define CUTC0_EXT_TC_CLOCK2 0x2 // Ext. TC Clock 2
-#define CUTC0_EXT_PCLK 0x3 // Ext. Pacer Clock
-
-// User Timer/Counter 1 Clock Select (SetUtc1Clock)
-#define CUTC1_8MHZ 0x0 // 8MHz
-#define CUTC1_EXT_TC_CLOCK1 0x1 // Ext. TC Clock 1
-#define CUTC1_EXT_TC_CLOCK2 0x2 // Ext. TC Clock 2
-#define CUTC1_EXT_PCLK 0x3 // Ext. Pacer Clock
-#define CUTC1_UTC0_OUT 0x4 // User Timer/Counter 0 out
-#define CUTC1_DIN_SIGNAL 0x5 // High-Speed Digital Input Sampling signal
-
-// User Timer/Counter 2 Clock Select (SetUtc2Clock)
-#define CUTC2_8MHZ 0x0 // 8MHz
-#define CUTC2_EXT_TC_CLOCK1 0x1 // Ext. TC Clock 1
-#define CUTC2_EXT_TC_CLOCK2 0x2 // Ext. TC Clock 2
-#define CUTC2_EXT_PCLK 0x3 // Ext. Pacer Clock
-#define CUTC2_UTC1_OUT 0x4 // User Timer/Counter 1 out
-
-// User Timer/Counter 0 Gate Select (SetUtc0Gate)
-#define GUTC0_NOT_GATED 0x0 // Not gated
-#define GUTC0_GATED 0x1 // Gated
-#define GUTC0_EXT_TC_GATE1 0x2 // Ext. TC Gate 1
-#define GUTC0_EXT_TC_GATE2 0x3 // Ext. TC Gate 2
-
-// User Timer/Counter 1 Gate Select (SetUtc1Gate)
-#define GUTC1_NOT_GATED 0x0 // Not gated
-#define GUTC1_GATED 0x1 // Gated
-#define GUTC1_EXT_TC_GATE1 0x2 // Ext. TC Gate 1
-#define GUTC1_EXT_TC_GATE2 0x3 // Ext. TC Gate 2
-#define GUTC1_UTC0_OUT 0x4 // User Timer/Counter 0 out
-
-// User Timer/Counter 2 Gate Select (SetUtc2Gate)
-#define GUTC2_NOT_GATED 0x0 // Not gated
-#define GUTC2_GATED 0x1 // Gated
-#define GUTC2_EXT_TC_GATE1 0x2 // Ext. TC Gate 1
-#define GUTC2_EXT_TC_GATE2 0x3 // Ext. TC Gate 2
-#define GUTC2_UTC1_OUT 0x4 // User Timer/Counter 1 out
-
-// Interrupt Source Masks (SetITMask, ClearITMask, GetITStatus)
-#define IRQM_ADC_FIFO_WRITE 0x0001 // ADC FIFO Write
-#define IRQM_CGT_RESET 0x0002 // Reset CGT
-#define IRQM_CGT_PAUSE 0x0008 // Pause CGT
-#define IRQM_ADC_ABOUT_CNT 0x0010 // About Counter out
-#define IRQM_ADC_DELAY_CNT 0x0020 // Delay Counter out
-#define IRQM_ADC_SAMPLE_CNT 0x0040 // ADC Sample Counter
-#define IRQM_DAC1_UCNT 0x0080 // DAC1 Update Counter
-#define IRQM_DAC2_UCNT 0x0100 // DAC2 Update Counter
-#define IRQM_UTC1 0x0200 // User TC1 out
-#define IRQM_UTC1_INV 0x0400 // User TC1 out, inverted
-#define IRQM_UTC2 0x0800 // User TC2 out
-#define IRQM_DIGITAL_IT 0x1000 // Digital Interrupt
-#define IRQM_EXTERNAL_IT 0x2000 // External Interrupt
-#define IRQM_ETRIG_RISING 0x4000 // External Trigger rising-edge
-#define IRQM_ETRIG_FALLING 0x8000 // External Trigger falling-edge
-
-// DMA Request Sources (LAS0)
-#define DMAS_DISABLED 0x0 // DMA Disabled
-#define DMAS_ADC_SCNT 0x1 // ADC Sample Counter
-#define DMAS_DAC1_UCNT 0x2 // D/A1 Update Counter
-#define DMAS_DAC2_UCNT 0x3 // D/A2 Update Counter
-#define DMAS_UTC1 0x4 // User TC1 out
-#define DMAS_ADFIFO_HALF_FULL 0x8 // A/D FIFO half full
-#define DMAS_DAC1_FIFO_HALF_EMPTY 0x9 // D/A1 FIFO half empty
-#define DMAS_DAC2_FIFO_HALF_EMPTY 0xA // D/A2 FIFO half empty
-
-// DMA Local Addresses (0x40000000+LAS1 offset)
-#define DMALADDR_ADC 0x40000000 // A/D FIFO
-#define DMALADDR_HDIN 0x40000004 // High Speed Digital Input FIFO
-#define DMALADDR_DAC1 0x40000008 // D/A1 FIFO
-#define DMALADDR_DAC2 0x4000000C // D/A2 FIFO
-
-// Port 0 compare modes (SetDIO0CompareMode)
-#define DIO_MODE_EVENT 0 // Event Mode
-#define DIO_MODE_MATCH 1 // Match Mode
-
-// Digital Table Enable (Port 1 disable)
-#define DTBL_DISABLE 0 // Enable Digital Table
-#define DTBL_ENABLE 1 // Disable Digital Table
-
-// Sampling Signal for High Speed Digital Input (SetHdinStart)
-#define HDIN_SOFTWARE 0x0 // Software Trigger
-#define HDIN_ADC 0x1 // A/D Conversion Signal
-#define HDIN_UTC0 0x2 // User TC out 0
-#define HDIN_UTC1 0x3 // User TC out 1
-#define HDIN_UTC2 0x4 // User TC out 2
-#define HDIN_EPCLK 0x5 // External Pacer Clock
-#define HDIN_ETRG 0x6 // External Trigger
-
-// Channel Gain Table / Channel Gain Latch
-#define CSC_LATCH 0 // Channel Gain Latch mode
-#define CSC_CGT 1 // Channel Gain Table mode
-
-// Channel Gain Table Pause Enable
-#define CGT_PAUSE_DISABLE 0 // Channel Gain Table Pause Disable
-#define CGT_PAUSE_ENABLE 1 // Channel Gain Table Pause Enable
-
-// DAC output type/range (p63)
-#define AOUT_UNIP5 0 // 0..+5 Volt
-#define AOUT_UNIP10 1 // 0..+10 Volt
-#define AOUT_BIP5 2 // -5..+5 Volt
-#define AOUT_BIP10 3 // -10..+10 Volt
-
-// Ghannel Gain Table field definitions (p61)
-// Gain
+/* A/D Conversion Signal Select (for SetConversionSelect) */
+#define ADC_START_SOFTWARE 0x0 /* Software A/D Start */
+#define ADC_START_PCLK 0x1 /* Pacer Clock (Ext. Int. see Func.509) */
+#define ADC_START_BCLK 0x2 /* Burst Clock */
+#define ADC_START_DIGITAL_IT 0x3 /* Digital Interrupt */
+#define ADC_START_DAC1_MARKER1 0x4 /* D/A 1 Data Marker 1 */
+#define ADC_START_DAC2_MARKER1 0x5 /* D/A 2 Data Marker 1 */
+#define ADC_START_SBUS0 0x6 /* SyncBus 0 */
+#define ADC_START_SBUS1 0x7 /* SyncBus 1 */
+#define ADC_START_SBUS2 0x8 /* SyncBus 2 */
+
+/* Burst Clock start trigger select (SetBurstStart) */
+#define BCLK_START_SOFTWARE 0x0 /* Software A/D Start (StartBurst) */
+#define BCLK_START_PCLK 0x1 /* Pacer Clock */
+#define BCLK_START_ETRIG 0x2 /* External Trigger */
+#define BCLK_START_DIGITAL_IT 0x3 /* Digital Interrupt */
+#define BCLK_START_SBUS0 0x4 /* SyncBus 0 */
+#define BCLK_START_SBUS1 0x5 /* SyncBus 1 */
+#define BCLK_START_SBUS2 0x6 /* SyncBus 2 */
+
+/* Pacer Clock start trigger select (SetPacerStart) */
+#define PCLK_START_SOFTWARE 0x0 /* Software Pacer Start (StartPacer) */
+#define PCLK_START_ETRIG 0x1 /* External trigger */
+#define PCLK_START_DIGITAL_IT 0x2 /* Digital interrupt */
+#define PCLK_START_UTC2 0x3 /* User TC 2 out */
+#define PCLK_START_SBUS0 0x4 /* SyncBus 0 */
+#define PCLK_START_SBUS1 0x5 /* SyncBus 1 */
+#define PCLK_START_SBUS2 0x6 /* SyncBus 2 */
+#define PCLK_START_D_SOFTWARE 0x8 /* Delayed Software Pacer Start */
+#define PCLK_START_D_ETRIG 0x9 /* Delayed external trigger */
+#define PCLK_START_D_DIGITAL_IT 0xA /* Delayed digital interrupt */
+#define PCLK_START_D_UTC2 0xB /* Delayed User TC 2 out */
+#define PCLK_START_D_SBUS0 0xC /* Delayed SyncBus 0 */
+#define PCLK_START_D_SBUS1 0xD /* Delayed SyncBus 1 */
+#define PCLK_START_D_SBUS2 0xE /* Delayed SyncBus 2 */
+#define PCLK_START_ETRIG_GATED 0xF /* External Trigger Gated controlled mode */
+
+/* Pacer Clock Stop Trigger select (SetPacerStop) */
+#define PCLK_STOP_SOFTWARE 0x0 /* Software Pacer Stop (StopPacer) */
+#define PCLK_STOP_ETRIG 0x1 /* External Trigger */
+#define PCLK_STOP_DIGITAL_IT 0x2 /* Digital Interrupt */
+#define PCLK_STOP_ACNT 0x3 /* About Counter */
+#define PCLK_STOP_UTC2 0x4 /* User TC2 out */
+#define PCLK_STOP_SBUS0 0x5 /* SyncBus 0 */
+#define PCLK_STOP_SBUS1 0x6 /* SyncBus 1 */
+#define PCLK_STOP_SBUS2 0x7 /* SyncBus 2 */
+#define PCLK_STOP_A_SOFTWARE 0x8 /* About Software Pacer Stop */
+#define PCLK_STOP_A_ETRIG 0x9 /* About External Trigger */
+#define PCLK_STOP_A_DIGITAL_IT 0xA /* About Digital Interrupt */
+#define PCLK_STOP_A_UTC2 0xC /* About User TC2 out */
+#define PCLK_STOP_A_SBUS0 0xD /* About SyncBus 0 */
+#define PCLK_STOP_A_SBUS1 0xE /* About SyncBus 1 */
+#define PCLK_STOP_A_SBUS2 0xF /* About SyncBus 2 */
+
+/* About Counter Stop Enable */
+#define ACNT_STOP 0x0 /* stop enable */
+#define ACNT_NO_STOP 0x1 /* stop disabled */
+
+/* DAC update source (SetDAC1Start & SetDAC2Start) */
+#define DAC_START_SOFTWARE 0x0 /* Software Update */
+#define DAC_START_CGT 0x1 /* CGT controlled Update */
+#define DAC_START_DAC_CLK 0x2 /* D/A Clock */
+#define DAC_START_EPCLK 0x3 /* External Pacer Clock */
+#define DAC_START_SBUS0 0x4 /* SyncBus 0 */
+#define DAC_START_SBUS1 0x5 /* SyncBus 1 */
+#define DAC_START_SBUS2 0x6 /* SyncBus 2 */
+
+/* DAC Cycle Mode (SetDAC1Cycle, SetDAC2Cycle, SetupDAC) */
+#define DAC_CYCLE_SINGLE 0x0 /* not cycle */
+#define DAC_CYCLE_MULTI 0x1 /* cycle */
+
+/* 8254 Operation Modes (Set8254Mode, SetupTimerCounter) */
+#define M8254_EVENT_COUNTER 0 /* Event Counter */
+#define M8254_HW_ONE_SHOT 1 /* Hardware-Retriggerable One-Shot */
+#define M8254_RATE_GENERATOR 2 /* Rate Generator */
+#define M8254_SQUARE_WAVE 3 /* Square Wave Mode */
+#define M8254_SW_STROBE 4 /* Software Triggered Strobe */
+#define M8254_HW_STROBE 5 /* Hardware Triggered Strobe (Retriggerable) */
+
+/* User Timer/Counter 0 Clock Select (SetUtc0Clock) */
+#define CUTC0_8MHZ 0x0 /* 8MHz */
+#define CUTC0_EXT_TC_CLOCK1 0x1 /* Ext. TC Clock 1 */
+#define CUTC0_EXT_TC_CLOCK2 0x2 /* Ext. TC Clock 2 */
+#define CUTC0_EXT_PCLK 0x3 /* Ext. Pacer Clock */
+
+/* User Timer/Counter 1 Clock Select (SetUtc1Clock) */
+#define CUTC1_8MHZ 0x0 /* 8MHz */
+#define CUTC1_EXT_TC_CLOCK1 0x1 /* Ext. TC Clock 1 */
+#define CUTC1_EXT_TC_CLOCK2 0x2 /* Ext. TC Clock 2 */
+#define CUTC1_EXT_PCLK 0x3 /* Ext. Pacer Clock */
+#define CUTC1_UTC0_OUT 0x4 /* User Timer/Counter 0 out */
+#define CUTC1_DIN_SIGNAL 0x5 /* High-Speed Digital Input Sampling signal */
+
+/* User Timer/Counter 2 Clock Select (SetUtc2Clock) */
+#define CUTC2_8MHZ 0x0 /* 8MHz */
+#define CUTC2_EXT_TC_CLOCK1 0x1 /* Ext. TC Clock 1 */
+#define CUTC2_EXT_TC_CLOCK2 0x2 /* Ext. TC Clock 2 */
+#define CUTC2_EXT_PCLK 0x3 /* Ext. Pacer Clock */
+#define CUTC2_UTC1_OUT 0x4 /* User Timer/Counter 1 out */
+
+/* User Timer/Counter 0 Gate Select (SetUtc0Gate) */
+#define GUTC0_NOT_GATED 0x0 /* Not gated */
+#define GUTC0_GATED 0x1 /* Gated */
+#define GUTC0_EXT_TC_GATE1 0x2 /* Ext. TC Gate 1 */
+#define GUTC0_EXT_TC_GATE2 0x3 /* Ext. TC Gate 2 */
+
+/* User Timer/Counter 1 Gate Select (SetUtc1Gate) */
+#define GUTC1_NOT_GATED 0x0 /* Not gated */
+#define GUTC1_GATED 0x1 /* Gated */
+#define GUTC1_EXT_TC_GATE1 0x2 /* Ext. TC Gate 1 */
+#define GUTC1_EXT_TC_GATE2 0x3 /* Ext. TC Gate 2 */
+#define GUTC1_UTC0_OUT 0x4 /* User Timer/Counter 0 out */
+
+/* User Timer/Counter 2 Gate Select (SetUtc2Gate) */
+#define GUTC2_NOT_GATED 0x0 /* Not gated */
+#define GUTC2_GATED 0x1 /* Gated */
+#define GUTC2_EXT_TC_GATE1 0x2 /* Ext. TC Gate 1 */
+#define GUTC2_EXT_TC_GATE2 0x3 /* Ext. TC Gate 2 */
+#define GUTC2_UTC1_OUT 0x4 /* User Timer/Counter 1 out */
+
+/* Interrupt Source Masks (SetITMask, ClearITMask, GetITStatus) */
+#define IRQM_ADC_FIFO_WRITE 0x0001 /* ADC FIFO Write */
+#define IRQM_CGT_RESET 0x0002 /* Reset CGT */
+#define IRQM_CGT_PAUSE 0x0008 /* Pause CGT */
+#define IRQM_ADC_ABOUT_CNT 0x0010 /* About Counter out */
+#define IRQM_ADC_DELAY_CNT 0x0020 /* Delay Counter out */
+#define IRQM_ADC_SAMPLE_CNT 0x0040 /* ADC Sample Counter */
+#define IRQM_DAC1_UCNT 0x0080 /* DAC1 Update Counter */
+#define IRQM_DAC2_UCNT 0x0100 /* DAC2 Update Counter */
+#define IRQM_UTC1 0x0200 /* User TC1 out */
+#define IRQM_UTC1_INV 0x0400 /* User TC1 out, inverted */
+#define IRQM_UTC2 0x0800 /* User TC2 out */
+#define IRQM_DIGITAL_IT 0x1000 /* Digital Interrupt */
+#define IRQM_EXTERNAL_IT 0x2000 /* External Interrupt */
+#define IRQM_ETRIG_RISING 0x4000 /* External Trigger rising-edge */
+#define IRQM_ETRIG_FALLING 0x8000 /* External Trigger falling-edge */
+
+/* DMA Request Sources (LAS0) */
+#define DMAS_DISABLED 0x0 /* DMA Disabled */
+#define DMAS_ADC_SCNT 0x1 /* ADC Sample Counter */
+#define DMAS_DAC1_UCNT 0x2 /* D/A1 Update Counter */
+#define DMAS_DAC2_UCNT 0x3 /* D/A2 Update Counter */
+#define DMAS_UTC1 0x4 /* User TC1 out */
+#define DMAS_ADFIFO_HALF_FULL 0x8 /* A/D FIFO half full */
+#define DMAS_DAC1_FIFO_HALF_EMPTY 0x9 /* D/A1 FIFO half empty */
+#define DMAS_DAC2_FIFO_HALF_EMPTY 0xA /* D/A2 FIFO half empty */
+
+/* DMA Local Addresses (0x40000000+LAS1 offset) */
+#define DMALADDR_ADC 0x40000000 /* A/D FIFO */
+#define DMALADDR_HDIN 0x40000004 /* High Speed Digital Input FIFO */
+#define DMALADDR_DAC1 0x40000008 /* D/A1 FIFO */
+#define DMALADDR_DAC2 0x4000000C /* D/A2 FIFO */
+
+/* Port 0 compare modes (SetDIO0CompareMode) */
+#define DIO_MODE_EVENT 0 /* Event Mode */
+#define DIO_MODE_MATCH 1 /* Match Mode */
+
+/* Digital Table Enable (Port 1 disable) */
+#define DTBL_DISABLE 0 /* Enable Digital Table */
+#define DTBL_ENABLE 1 /* Disable Digital Table */
+
+/* Sampling Signal for High Speed Digital Input (SetHdinStart) */
+#define HDIN_SOFTWARE 0x0 /* Software Trigger */
+#define HDIN_ADC 0x1 /* A/D Conversion Signal */
+#define HDIN_UTC0 0x2 /* User TC out 0 */
+#define HDIN_UTC1 0x3 /* User TC out 1 */
+#define HDIN_UTC2 0x4 /* User TC out 2 */
+#define HDIN_EPCLK 0x5 /* External Pacer Clock */
+#define HDIN_ETRG 0x6 /* External Trigger */
+
+/* Channel Gain Table / Channel Gain Latch */
+#define CSC_LATCH 0 /* Channel Gain Latch mode */
+#define CSC_CGT 1 /* Channel Gain Table mode */
+
+/* Channel Gain Table Pause Enable */
+#define CGT_PAUSE_DISABLE 0 /* Channel Gain Table Pause Disable */
+#define CGT_PAUSE_ENABLE 1 /* Channel Gain Table Pause Enable */
+
+/* DAC output type/range (p63) */
+#define AOUT_UNIP5 0 /* 0..+5 Volt */
+#define AOUT_UNIP10 1 /* 0..+10 Volt */
+#define AOUT_BIP5 2 /* -5..+5 Volt */
+#define AOUT_BIP10 3 /* -10..+10 Volt */
+
+/* Ghannel Gain Table field definitions (p61) */
+/* Gain */
#define GAIN1 0
#define GAIN2 1
#define GAIN4 2
@@ -398,15 +398,15 @@
#define GAIN64 6
#define GAIN128 7
-// Input range/polarity
-#define AIN_BIP5 0 // -5..+5 Volt
-#define AIN_BIP10 1 // -10..+10 Volt
-#define AIN_UNIP10 2 // 0..+10 Volt
+/* Input range/polarity */
+#define AIN_BIP5 0 /* -5..+5 Volt */
+#define AIN_BIP10 1 /* -10..+10 Volt */
+#define AIN_UNIP10 2 /* 0..+10 Volt */
-// non referenced single ended select bit
-#define NRSE_AGND 0 // AGND referenced SE input
-#define NRSE_AINS 1 // AIN SENSE referenced SE input
+/* non referenced single ended select bit */
+#define NRSE_AGND 0 /* AGND referenced SE input */
+#define NRSE_AINS 1 /* AIN SENSE referenced SE input */
-// single ended vs differential
-#define GND_SE 0 // Single-Ended
-#define GND_DIFF 1 // Differential
+/* single ended vs differential */
+#define GND_SE 0 /* Single-Ended */
+#define GND_DIFF 1 /* Differential */
diff --git a/drivers/staging/comedi/drivers/rti800.c b/drivers/staging/comedi/drivers/rti800.c
new file mode 100644
index 000000000000..334ac5773a13
--- /dev/null
+++ b/drivers/staging/comedi/drivers/rti800.c
@@ -0,0 +1,458 @@
+/*
+ comedi/drivers/rti800.c
+ Hardware driver for Analog Devices RTI-800/815 board
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+/*
+Driver: rti800
+Description: Analog Devices RTI-800/815
+Author: ds
+Status: unknown
+Updated: Fri, 05 Sep 2008 14:50:44 +0100
+Devices: [Analog Devices] RTI-800 (rti800), RTI-815 (rti815)
+
+Configuration options:
+ [0] - I/O port base address
+ [1] - IRQ
+ [2] - A/D reference
+ 0 = differential
+ 1 = pseudodifferential (common)
+ 2 = single-ended
+ [3] - A/D range
+ 0 = [-10,10]
+ 1 = [-5,5]
+ 2 = [0,10]
+ [4] - A/D encoding
+ 0 = two's complement
+ 1 = straight binary
+ [5] - DAC 0 range
+ 0 = [-10,10]
+ 1 = [0,10]
+ [6] - DAC 0 encoding
+ 0 = two's complement
+ 1 = straight binary
+ [7] - DAC 1 range (same as DAC 0)
+ [8] - DAC 1 encoding (same as DAC 0)
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+#define RTI800_SIZE 16
+
+#define RTI800_CSR 0
+#define RTI800_MUXGAIN 1
+#define RTI800_CONVERT 2
+#define RTI800_ADCLO 3
+#define RTI800_ADCHI 4
+#define RTI800_DAC0LO 5
+#define RTI800_DAC0HI 6
+#define RTI800_DAC1LO 7
+#define RTI800_DAC1HI 8
+#define RTI800_CLRFLAGS 9
+#define RTI800_DI 10
+#define RTI800_DO 11
+#define RTI800_9513A_DATA 12
+#define RTI800_9513A_CNTRL 13
+#define RTI800_9513A_STATUS 13
+
+/*
+ * flags for CSR register
+ */
+
+#define RTI800_BUSY 0x80
+#define RTI800_DONE 0x40
+#define RTI800_OVERRUN 0x20
+#define RTI800_TCR 0x10
+#define RTI800_DMA_ENAB 0x08
+#define RTI800_INTR_TC 0x04
+#define RTI800_INTR_EC 0x02
+#define RTI800_INTR_OVRN 0x01
+
+#define Am9513_8BITBUS
+
+#define Am9513_output_control(a) outb(a,dev->iobase+RTI800_9513A_CNTRL)
+#define Am9513_output_data(a) outb(a,dev->iobase+RTI800_9513A_DATA)
+#define Am9513_input_data() inb(dev->iobase+RTI800_9513A_DATA)
+#define Am9513_input_status() inb(dev->iobase+RTI800_9513A_STATUS)
+
+#include "am9513.h"
+
+static const struct comedi_lrange range_rti800_ai_10_bipolar = { 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.02)
+ }
+};
+static const struct comedi_lrange range_rti800_ai_5_bipolar = { 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.01)
+ }
+};
+static const struct comedi_lrange range_rti800_ai_unipolar = { 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.02)
+ }
+};
+
+struct rti800_board {
+
+ const char *name;
+ int has_ao;
+};
+
+static const struct rti800_board boardtypes[] = {
+ {"rti800", 0},
+ {"rti815", 1},
+};
+
+#define this_board ((const struct rti800_board *)dev->board_ptr)
+
+static int rti800_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int rti800_detach(struct comedi_device * dev);
+static struct comedi_driver driver_rti800 = {
+ driver_name:"rti800",
+ module:THIS_MODULE,
+ attach:rti800_attach,
+ detach:rti800_detach,
+ num_names:sizeof(boardtypes) / sizeof(struct rti800_board),
+ board_name:&boardtypes[0].name,
+ offset:sizeof(struct rti800_board),
+};
+
+COMEDI_INITCLEANUP(driver_rti800);
+
+static irqreturn_t rti800_interrupt(int irq, void *dev PT_REGS_ARG);
+
+struct rti800_private {
+ 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;
+ const struct comedi_lrange *ao_range_type_list[2];
+ unsigned int ao_readback[2];
+ int muxgain_bits;
+};
+
+#define devpriv ((struct rti800_private *)dev->private)
+
+#define RTI800_TIMEOUT 100
+
+static irqreturn_t rti800_interrupt(int irq, void *dev PT_REGS_ARG)
+{
+ return IRQ_HANDLED;
+}
+
+// settling delay times in usec for different gains
+static const int gaindelay[] = { 10, 20, 40, 80 };
+
+static int rti800_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i, t;
+ int status;
+ int chan = CR_CHAN(insn->chanspec);
+ unsigned gain = CR_RANGE(insn->chanspec);
+ unsigned muxgain_bits;
+
+ inb(dev->iobase + RTI800_ADCHI);
+ outb(0, dev->iobase + RTI800_CLRFLAGS);
+
+ muxgain_bits = chan | (gain << 5);
+ if (muxgain_bits != devpriv->muxgain_bits) {
+ devpriv->muxgain_bits = muxgain_bits;
+ outb(devpriv->muxgain_bits, dev->iobase + RTI800_MUXGAIN);
+ /* without a delay here, the RTI_OVERRUN bit
+ * gets set, and you will have an error. */
+ if (insn->n > 0) {
+ BUG_ON(gain >=
+ sizeof(gaindelay) / sizeof(gaindelay[0]));
+ comedi_udelay(gaindelay[gain]);
+ }
+ }
+
+ for (i = 0; i < insn->n; i++) {
+ outb(0, dev->iobase + RTI800_CONVERT);
+ for (t = RTI800_TIMEOUT; t; t--) {
+ status = inb(dev->iobase + RTI800_CSR);
+ if (status & RTI800_OVERRUN) {
+ rt_printk("rti800: a/d overrun\n");
+ outb(0, dev->iobase + RTI800_CLRFLAGS);
+ return -EIO;
+ }
+ if (status & RTI800_DONE)
+ break;
+ comedi_udelay(1);
+ }
+ if (t == 0) {
+ rt_printk("rti800: timeout\n");
+ return -ETIME;
+ }
+ data[i] = inb(dev->iobase + RTI800_ADCLO);
+ data[i] |= (0xf & inb(dev->iobase + RTI800_ADCHI)) << 8;
+
+ if (devpriv->adc_coding == adc_2comp) {
+ data[i] ^= 0x800;
+ }
+ }
+
+ return i;
+}
+
+static int rti800_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = devpriv->ao_readback[chan];
+
+ return i;
+}
+
+static int rti800_ao_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+ int d;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ devpriv->ao_readback[chan] = d = data[i];
+ if (devpriv->dac0_coding == dac_2comp) {
+ d ^= 0x800;
+ }
+ outb(d & 0xff,
+ dev->iobase + (chan ? RTI800_DAC1LO : RTI800_DAC0LO));
+ outb(d >> 8,
+ dev->iobase + (chan ? RTI800_DAC1HI : RTI800_DAC0HI));
+ }
+ return i;
+}
+
+static int rti800_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+ data[1] = inb(dev->iobase + RTI800_DI);
+ return 2;
+}
+
+static int rti800_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+ /* Outputs are inverted... */
+ outb(s->state ^ 0xff, dev->iobase + RTI800_DO);
+ }
+
+ data[1] = s->state;
+
+ return 2;
+}
+
+/*
+ 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_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ unsigned int irq;
+ unsigned long iobase;
+ int ret;
+ struct comedi_subdevice *s;
+
+ iobase = it->options[0];
+ printk("comedi%d: rti800: 0x%04lx ", dev->minor, iobase);
+ if (!request_region(iobase, RTI800_SIZE, "rti800")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+#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) {
+ printk("( irq = %u )", irq);
+ if ((ret = comedi_request_irq(irq, rti800_interrupt, 0,
+ "rti800", dev)) < 0) {
+ printk(" Failed to allocate IRQ\n");
+ return ret;
+ }
+ dev->irq = irq;
+ } else {
+ printk("( no irq )");
+ }
+
+ dev->board_name = this_board->name;
+
+ if ((ret = alloc_subdevices(dev, 4)) < 0)
+ return ret;
+ if ((ret = alloc_private(dev, sizeof(struct 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];
+ devpriv->muxgain_bits = -1;
+
+ s = dev->subdevices + 0;
+ /* ai subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = (devpriv->adc_mux ? 16 : 8);
+ s->insn_read = rti800_ai_insn_read;
+ s->maxdata = 0xfff;
+ switch (devpriv->adc_range) {
+ case adc_bipolar10:
+ s->range_table = &range_rti800_ai_10_bipolar;
+ break;
+ case adc_bipolar5:
+ s->range_table = &range_rti800_ai_5_bipolar;
+ break;
+ case adc_unipolar10:
+ s->range_table = &range_rti800_ai_unipolar;
+ break;
+ }
+
+ s++;
+ if (this_board->has_ao) {
+ /* ao subdevice (only on rti815) */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->insn_read = rti800_ao_insn_read;
+ s->insn_write = rti800_ao_insn_write;
+ s->maxdata = 0xfff;
+ s->range_table_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;
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s++;
+ /* di */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 8;
+ s->insn_bits = rti800_di_insn_bits;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+
+ s++;
+ /* do */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 8;
+ s->insn_bits = rti800_do_insn_bits;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+
+/* don't yet know how to deal with counter/timers */
+#if 0
+ s++;
+ /* do */
+ s->type = COMEDI_SUBD_TIMER;
+#endif
+
+ printk("\n");
+
+ return 0;
+}
+
+static int rti800_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: rti800: remove\n", dev->minor);
+
+ if (dev->iobase)
+ release_region(dev->iobase, RTI800_SIZE);
+
+ if (dev->irq)
+ comedi_free_irq(dev->irq, dev);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/rti802.c b/drivers/staging/comedi/drivers/rti802.c
new file mode 100644
index 000000000000..cc2c385db0b0
--- /dev/null
+++ b/drivers/staging/comedi/drivers/rti802.c
@@ -0,0 +1,151 @@
+/*
+ comedi/drivers/rti802.c
+ Hardware driver for Analog Devices RTI-802 board
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+/*
+Driver: rti802
+Description: Analog Devices RTI-802
+Author: Anders Blomdell <anders.blomdell@control.lth.se>
+Devices: [Analog Devices] RTI-802 (rti802)
+Status: works
+
+Configuration 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 ...
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+#define RTI802_SIZE 4
+
+#define RTI802_SELECT 0
+#define RTI802_DATALOW 1
+#define RTI802_DATAHIGH 2
+
+static int rti802_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int rti802_detach(struct comedi_device * dev);
+static struct comedi_driver driver_rti802 = {
+ driver_name:"rti802",
+ module:THIS_MODULE,
+ attach:rti802_attach,
+ detach:rti802_detach,
+};
+
+COMEDI_INITCLEANUP(driver_rti802);
+
+struct rti802_private {
+ enum {
+ dac_2comp, dac_straight
+ } dac_coding[8];
+ const struct comedi_lrange *range_type_list[8];
+ unsigned int ao_readback[8];
+};
+
+#define devpriv ((struct rti802_private *)dev->private)
+
+static int rti802_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = devpriv->ao_readback[CR_CHAN(insn->chanspec)];
+
+ return i;
+}
+
+static int rti802_ao_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i, d;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++) {
+ d = devpriv->ao_readback[chan] = data[i];
+ if (devpriv->dac_coding[chan] == dac_2comp)
+ d ^= 0x800;
+ outb(chan, dev->iobase + RTI802_SELECT);
+ outb(d & 0xff, dev->iobase + RTI802_DATALOW);
+ outb(d >> 8, dev->iobase + RTI802_DATAHIGH);
+ }
+ return i;
+}
+
+static int rti802_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int i;
+ unsigned long iobase;
+
+ iobase = it->options[0];
+ printk("comedi%d: rti802: 0x%04lx ", dev->minor, iobase);
+ if (!request_region(iobase, RTI802_SIZE, "rti802")) {
+ printk("I/O port conflict\n");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ dev->board_name = "rti802";
+
+ if (alloc_subdevices(dev, 1) < 0
+ || alloc_private(dev, sizeof(struct rti802_private))) {
+ return -ENOMEM;
+ }
+
+ s = dev->subdevices;
+ /* ao subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->maxdata = 0xfff;
+ s->n_chan = 8;
+ s->insn_read = rti802_ao_insn_read;
+ s->insn_write = rti802_ao_insn_write;
+ s->range_table_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 int rti802_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: rti802: remove\n", dev->minor);
+
+ if (dev->iobase)
+ release_region(dev->iobase, RTI802_SIZE);
+
+ return 0;
+}
diff --git a/drivers/staging/comedi/drivers/s526.c b/drivers/staging/comedi/drivers/s526.c
new file mode 100644
index 000000000000..a7b6f711afca
--- /dev/null
+++ b/drivers/staging/comedi/drivers/s526.c
@@ -0,0 +1,977 @@
+/*
+ comedi/drivers/s526.c
+ Sensoray s526 Comedi driver
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: s526
+Description: Sensoray 526 driver
+Devices: [Sensoray] 526 (s526)
+Author: Richie
+ Everett Wang <everett.wang@everteq.com>
+Updated: Thu, 14 Sep. 2006
+Status: experimental
+
+Encoder works
+Analog input works
+Analog output works
+PWM output works
+Commands are not supported yet.
+
+Configuration Options:
+
+comedi_config /dev/comedi0 s526 0x2C0,0x3
+
+*/
+
+#include "../comedidev.h"
+#include <linux/ioport.h>
+
+#define S526_SIZE 64
+
+#define S526_START_AI_CONV 0
+#define S526_AI_READ 0
+
+/* Ports */
+#define S526_IOSIZE 0x40
+#define S526_NUM_PORTS 27
+
+/* registers */
+#define REG_TCR 0x00
+#define REG_WDC 0x02
+#define REG_DAC 0x04
+#define REG_ADC 0x06
+#define REG_ADD 0x08
+#define REG_DIO 0x0A
+#define REG_IER 0x0C
+#define REG_ISR 0x0E
+#define REG_MSC 0x10
+#define REG_C0L 0x12
+#define REG_C0H 0x14
+#define REG_C0M 0x16
+#define REG_C0C 0x18
+#define REG_C1L 0x1A
+#define REG_C1H 0x1C
+#define REG_C1M 0x1E
+#define REG_C1C 0x20
+#define REG_C2L 0x22
+#define REG_C2H 0x24
+#define REG_C2M 0x26
+#define REG_C2C 0x28
+#define REG_C3L 0x2A
+#define REG_C3H 0x2C
+#define REG_C3M 0x2E
+#define REG_C3C 0x30
+#define REG_EED 0x32
+#define REG_EEC 0x34
+
+static const int s526_ports[] = {
+ REG_TCR,
+ REG_WDC,
+ REG_DAC,
+ REG_ADC,
+ REG_ADD,
+ REG_DIO,
+ REG_IER,
+ REG_ISR,
+ REG_MSC,
+ REG_C0L,
+ REG_C0H,
+ REG_C0M,
+ REG_C0C,
+ REG_C1L,
+ REG_C1H,
+ REG_C1M,
+ REG_C1C,
+ REG_C2L,
+ REG_C2H,
+ REG_C2M,
+ REG_C2C,
+ REG_C3L,
+ REG_C3H,
+ REG_C3M,
+ REG_C3C,
+ REG_EED,
+ REG_EEC
+};
+
+struct counter_mode_register_t {
+ unsigned short coutSource:1;
+ unsigned short coutPolarity:1;
+ unsigned short autoLoadResetRcap:3;
+ unsigned short hwCtEnableSource:2;
+ unsigned short ctEnableCtrl:2;
+ unsigned short clockSource:2;
+ unsigned short countDir:1;
+ unsigned short countDirCtrl:1;
+ unsigned short outputRegLatchCtrl:1;
+ unsigned short preloadRegSel:1;
+ unsigned short reserved:1;
+};
+
+union {
+ struct counter_mode_register_t reg;
+ unsigned short value;
+} cmReg;
+
+#define MAX_GPCT_CONFIG_DATA 6
+
+/* Different Application Classes for GPCT Subdevices */
+/* The list is not exhaustive and needs discussion! */
+enum S526_GPCT_APP_CLASS {
+ CountingAndTimeMeasurement,
+ SinglePulseGeneration,
+ PulseTrainGeneration,
+ PositionMeasurement,
+ Miscellaneous
+};
+
+/* Config struct for different GPCT subdevice Application Classes and
+ their options
+*/
+struct s526GPCTConfig {
+ enum S526_GPCT_APP_CLASS app;
+ int data[MAX_GPCT_CONFIG_DATA];
+};
+
+/*
+ * Board descriptions for two imaginary boards. Describing the
+ * boards in this way is optional, and completely driver-dependent.
+ * Some drivers use arrays such as this, other do not.
+ */
+struct s526_board {
+ const char *name;
+ int gpct_chans;
+ int gpct_bits;
+ int ad_chans;
+ int ad_bits;
+ int da_chans;
+ int da_bits;
+ int have_dio;
+};
+
+static const struct s526_board s526_boards[] = {
+ {
+ name: "s526",
+ gpct_chans:4,
+ gpct_bits:24,
+ ad_chans:8,
+ ad_bits: 16,
+ da_chans:4,
+ da_bits: 16,
+ have_dio:1,
+ }
+};
+
+#define ADDR_REG(reg) (dev->iobase + (reg))
+#define ADDR_CHAN_REG(reg, chan) (dev->iobase + (reg) + (chan) * 8)
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct s526_board *)dev->board_ptr)
+
+/* 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 struct comedi_device struct. */
+struct s526_private {
+
+ int data;
+
+ /* would be useful for a PCI device */
+ struct pci_dev *pci_dev;
+
+ /* Used for AO readback */
+ unsigned int ao_readback[2];
+
+ struct s526GPCTConfig s526_gpct_config[4];
+ unsigned short s526_ai_config;
+};
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((struct s526_private *)dev->private)
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int s526_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int s526_detach(struct comedi_device * dev);
+static struct comedi_driver driver_s526 = {
+ driver_name:"s526",
+ module:THIS_MODULE,
+ attach:s526_attach,
+ detach:s526_detach,
+/* It is not necessary to implement the following members if you are
+ * writing a driver for a ISA PnP or PCI card */
+ /* Most drivers will support multiple types of boards by
+ * having an array of board structures. These were defined
+ * in s526_boards[] above. Note that the element 'name'
+ * was first in the structure -- Comedi uses this fact to
+ * extract the name of the board without knowing any details
+ * about the structure except for its length.
+ * When a device is attached (by comedi_config), the name
+ * of the device is given to Comedi, and Comedi tries to
+ * match it by going through the list of board names. If
+ * there is a match, the address of the pointer is put
+ * into dev->board_ptr and driver->attach() is called.
+ *
+ * Note that these are not necessary if you can determine
+ * the type of board in software. ISA PnP, PCI, and PCMCIA
+ * devices are such boards.
+ */
+ board_name:&s526_boards[0].name,
+ offset:sizeof(struct s526_board),
+ num_names:sizeof(s526_boards) / sizeof(struct s526_board),
+};
+
+static int s526_gpct_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int s526_gpct_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int s526_gpct_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int s526_ai_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int s526_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int s526_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int s526_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int s526_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int s526_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int s526_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+ int iobase;
+ int i, n;
+// short value;
+// int subdev_channel = 0;
+
+ printk("comedi%d: s526: ", dev->minor);
+
+ iobase = it->options[0];
+ if (!iobase || !request_region(iobase, S526_IOSIZE, thisboard->name)) {
+ comedi_error(dev, "I/O port conflict");
+ return -EIO;
+ }
+ dev->iobase = iobase;
+
+ printk("iobase=0x%lx\n", dev->iobase);
+
+ /*** make it a little quieter, exw, 8/29/06
+ for (i = 0; i < S526_NUM_PORTS; i++) {
+ printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
+ }
+ ***/
+
+/*
+ * Initialize dev->board_name. Note that we can use the "thisboard"
+ * macro now, since we just initialized it in the last line.
+ */
+ dev->board_ptr = &s526_boards[0];
+
+ dev->board_name = thisboard->name;
+
+/*
+ * Allocate the private structure area. alloc_private() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_private(dev, sizeof(struct s526_private)) < 0)
+ return -ENOMEM;
+
+/*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ */
+ dev->n_subdevices = 4;
+ if (alloc_subdevices(dev, dev->n_subdevices) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
+ /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
+ s->n_chan = thisboard->gpct_chans;
+ s->maxdata = 0x00ffffff; /* 24 bit counter */
+ s->insn_read = s526_gpct_rinsn;
+ s->insn_config = s526_gpct_insn_config;
+ s->insn_write = s526_gpct_winsn;
+
+ /* Command are not implemented yet, however they are necessary to
+ allocate the necessary memory for the comedi_async struct (used
+ to trigger the GPCT in case of pulsegenerator function */
+ //s->do_cmd = s526_gpct_cmd;
+ //s->do_cmdtest = s526_gpct_cmdtest;
+ //s->cancel = s526_gpct_cancel;
+
+ s = dev->subdevices + 1;
+ //dev->read_subdev=s;
+ /* analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ /* we support differential */
+ s->subdev_flags = SDF_READABLE | SDF_DIFF;
+ /* channels 0 to 7 are the regular differential inputs */
+ /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
+ s->n_chan = 10;
+ s->maxdata = 0xffff;
+ s->range_table = &range_bipolar10;
+ s->len_chanlist = 16; /* This is the maximum chanlist length that
+ the board can handle */
+ s->insn_read = s526_ai_rinsn;
+ s->insn_config = s526_ai_insn_config;
+
+ s = dev->subdevices + 2;
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 0xffff;
+ s->range_table = &range_bipolar10;
+ s->insn_write = s526_ao_winsn;
+ s->insn_read = s526_ao_rinsn;
+
+ s = dev->subdevices + 3;
+ /* digital i/o subdevice */
+ if (thisboard->have_dio) {
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = s526_dio_insn_bits;
+ s->insn_config = s526_dio_insn_config;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ printk("attached\n");
+
+ return 1;
+
+#if 0
+ // Example of Counter Application
+ //One-shot (software trigger)
+ cmReg.reg.coutSource = 0; // out RCAP
+ cmReg.reg.coutPolarity = 1; // Polarity inverted
+ cmReg.reg.autoLoadResetRcap = 1; // Auto load 0:disabled, 1:enabled
+ cmReg.reg.hwCtEnableSource = 3; // NOT RCAP
+ cmReg.reg.ctEnableCtrl = 2; // Hardware
+ cmReg.reg.clockSource = 2; // Internal
+ cmReg.reg.countDir = 1; // Down
+ cmReg.reg.countDirCtrl = 1; // Software
+ cmReg.reg.outputRegLatchCtrl = 0; // latch on read
+ cmReg.reg.preloadRegSel = 0; // PR0
+ cmReg.reg.reserved = 0;
+
+ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+ outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+ outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+
+ outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
+ outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
+
+ outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset RCAP (fires one-shot)
+
+#else
+
+ // Set Counter Mode Register
+ cmReg.reg.coutSource = 0; // out RCAP
+ cmReg.reg.coutPolarity = 0; // Polarity inverted
+ cmReg.reg.autoLoadResetRcap = 0; // Auto load disabled
+ cmReg.reg.hwCtEnableSource = 2; // NOT RCAP
+ cmReg.reg.ctEnableCtrl = 1; // 1: Software, >1 : Hardware
+ cmReg.reg.clockSource = 3; // x4
+ cmReg.reg.countDir = 0; // up
+ cmReg.reg.countDirCtrl = 0; // quadrature
+ cmReg.reg.outputRegLatchCtrl = 0; // latch on read
+ cmReg.reg.preloadRegSel = 0; // PR0
+ cmReg.reg.reserved = 0;
+
+ n = 0;
+ printk("Mode reg=0x%04x, 0x%04lx\n", cmReg.value, ADDR_CHAN_REG(REG_C0M,
+ n));
+ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
+ udelay(1000);
+ printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
+
+ // Load the pre-laod register high word
+// value = (short) (0x55);
+// outw(value, ADDR_CHAN_REG(REG_C0H, n));
+
+ // Load the pre-laod register low word
+// value = (short)(0xaa55);
+// outw(value, ADDR_CHAN_REG(REG_C0L, n));
+
+ // Write the Counter Control Register
+// outw(value, ADDR_CHAN_REG(REG_C0C, 0));
+
+ // Reset the counter if it is software preload
+ if (cmReg.reg.autoLoadResetRcap == 0) {
+ outw(0x8000, ADDR_CHAN_REG(REG_C0C, n)); // Reset the counter
+ outw(0x4000, ADDR_CHAN_REG(REG_C0C, n)); // Load the counter from PR0
+ }
+
+ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
+ udelay(1000);
+ printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
+
+#endif
+ printk("Current registres:\n");
+
+ for (i = 0; i < S526_NUM_PORTS; i++) {
+ printk("0x%02lx: 0x%04x\n", ADDR_REG(s526_ports[i]),
+ inw(ADDR_REG(s526_ports[i])));
+ }
+ return 1;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int s526_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: s526: remove\n", dev->minor);
+
+ if (dev->iobase > 0)
+ release_region(dev->iobase, S526_IOSIZE);
+
+ return 0;
+}
+
+static int s526_gpct_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i; // counts the Data
+ int counter_channel = CR_CHAN(insn->chanspec);
+ unsigned short datalow;
+ unsigned short datahigh;
+
+ // Check if (n > 0)
+ if (insn->n <= 0) {
+ printk("s526: INSN_READ: n should be > 0\n");
+ return -EINVAL;
+ }
+ // Read the low word first
+ for (i = 0; i < insn->n; i++) {
+ datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
+ datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
+ data[i] = (int)(datahigh & 0x00FF);
+ data[i] = (data[i] << 16) | (datalow & 0xFFFF);
+// printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n", counter_channel, data[i], datahigh, datalow);
+ }
+ return i;
+}
+
+static int s526_gpct_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int subdev_channel = CR_CHAN(insn->chanspec); // Unpack chanspec
+ int i;
+ short value;
+
+// printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n", subdev_channel);
+
+ for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
+ devpriv->s526_gpct_config[subdev_channel].data[i] =
+ insn->data[i];
+// printk("data[%d]=%x\n", i, insn->data[i]);
+ }
+
+ // Check what type of Counter the user requested, data[0] contains
+ // the Application type
+ switch (insn->data[0]) {
+ case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
+ /*
+ data[0]: Application Type
+ data[1]: Counter Mode Register Value
+ data[2]: Pre-load Register Value
+ data[3]: Conter Control Register
+ */
+ printk("s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
+ devpriv->s526_gpct_config[subdev_channel].app =
+ PositionMeasurement;
+
+/*
+ // Example of Counter Application
+ //One-shot (software trigger)
+ cmReg.reg.coutSource = 0; // out RCAP
+ cmReg.reg.coutPolarity = 1; // Polarity inverted
+ cmReg.reg.autoLoadResetRcap = 0; // Auto load disabled
+ cmReg.reg.hwCtEnableSource = 3; // NOT RCAP
+ cmReg.reg.ctEnableCtrl = 2; // Hardware
+ cmReg.reg.clockSource = 2; // Internal
+ cmReg.reg.countDir = 1; // Down
+ cmReg.reg.countDirCtrl = 1; // Software
+ cmReg.reg.outputRegLatchCtrl = 0; // latch on read
+ cmReg.reg.preloadRegSel = 0; // PR0
+ cmReg.reg.reserved = 0;
+
+ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+ outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+ outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+
+ outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
+ outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
+
+ outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset RCAP (fires one-shot)
+
+*/
+
+#if 1
+ // Set Counter Mode Register
+ cmReg.reg.coutSource = 0; // out RCAP
+ cmReg.reg.coutPolarity = 0; // Polarity inverted
+ cmReg.reg.autoLoadResetRcap = 0; // Auto load disabled
+ cmReg.reg.hwCtEnableSource = 2; // NOT RCAP
+ cmReg.reg.ctEnableCtrl = 1; // 1: Software, >1 : Hardware
+ cmReg.reg.clockSource = 3; // x4
+ cmReg.reg.countDir = 0; // up
+ cmReg.reg.countDirCtrl = 0; // quadrature
+ cmReg.reg.outputRegLatchCtrl = 0; // latch on read
+ cmReg.reg.preloadRegSel = 0; // PR0
+ cmReg.reg.reserved = 0;
+
+ // Set Counter Mode Register
+// printk("s526: Counter Mode register=%x\n", cmReg.value);
+ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+ // Reset the counter if it is software preload
+ if (cmReg.reg.autoLoadResetRcap == 0) {
+ outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
+// outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
+ }
+#else
+ cmReg.reg.countDirCtrl = 0; // 0 quadrature, 1 software control
+
+ // data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4
+ if (insn->data[1] == GPCT_X2) {
+ cmReg.reg.clockSource = 1;
+ } else if (insn->data[1] == GPCT_X4) {
+ cmReg.reg.clockSource = 2;
+ } else {
+ cmReg.reg.clockSource = 0;
+ }
+
+ // When to take into account the indexpulse:
+ if (insn->data[2] == GPCT_IndexPhaseLowLow) {
+ } else if (insn->data[2] == GPCT_IndexPhaseLowHigh) {
+ } else if (insn->data[2] == GPCT_IndexPhaseHighLow) {
+ } else if (insn->data[2] == GPCT_IndexPhaseHighHigh) {
+ }
+ // Take into account the index pulse?
+ if (insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX)
+ cmReg.reg.autoLoadResetRcap = 4; // Auto load with INDEX^
+
+ // Set Counter Mode Register
+ cmReg.value = (short) (insn->data[1] & 0xFFFF);
+ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+ // Load the pre-laod register high word
+ value = (short) ((insn->data[2] >> 16) & 0xFFFF);
+ outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+
+ // Load the pre-laod register low word
+ value = (short) (insn->data[2] & 0xFFFF);
+ outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+
+ // Write the Counter Control Register
+ if (insn->data[3] != 0) {
+ value = (short) (insn->data[3] & 0xFFFF);
+ outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+ }
+ // Reset the counter if it is software preload
+ if (cmReg.reg.autoLoadResetRcap == 0) {
+ outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
+ outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
+ }
+#endif
+ break;
+
+ case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
+ /*
+ data[0]: Application Type
+ data[1]: Counter Mode Register Value
+ data[2]: Pre-load Register 0 Value
+ data[3]: Pre-load Register 1 Value
+ data[4]: Conter Control Register
+ */
+ printk("s526: GPCT_INSN_CONFIG: Configuring SPG\n");
+ devpriv->s526_gpct_config[subdev_channel].app =
+ SinglePulseGeneration;
+
+ // Set Counter Mode Register
+ cmReg.value = (short) (insn->data[1] & 0xFFFF);
+ cmReg.reg.preloadRegSel = 0; // PR0
+ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+ // Load the pre-laod register 0 high word
+ value = (short) ((insn->data[2] >> 16) & 0xFFFF);
+ outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+
+ // Load the pre-laod register 0 low word
+ value = (short) (insn->data[2] & 0xFFFF);
+ outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+
+ // Set Counter Mode Register
+ cmReg.value = (short) (insn->data[1] & 0xFFFF);
+ cmReg.reg.preloadRegSel = 1; // PR1
+ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+ // Load the pre-laod register 1 high word
+ value = (short) ((insn->data[3] >> 16) & 0xFFFF);
+ outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+
+ // Load the pre-laod register 1 low word
+ value = (short) (insn->data[3] & 0xFFFF);
+ outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+
+ // Write the Counter Control Register
+ if (insn->data[3] != 0) {
+ value = (short) (insn->data[3] & 0xFFFF);
+ outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+ }
+ break;
+
+ case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
+ /*
+ data[0]: Application Type
+ data[1]: Counter Mode Register Value
+ data[2]: Pre-load Register 0 Value
+ data[3]: Pre-load Register 1 Value
+ data[4]: Conter Control Register
+ */
+ printk("s526: GPCT_INSN_CONFIG: Configuring PTG\n");
+ devpriv->s526_gpct_config[subdev_channel].app =
+ PulseTrainGeneration;
+
+ // Set Counter Mode Register
+ cmReg.value = (short) (insn->data[1] & 0xFFFF);
+ cmReg.reg.preloadRegSel = 0; // PR0
+ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+ // Load the pre-laod register 0 high word
+ value = (short) ((insn->data[2] >> 16) & 0xFFFF);
+ outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+
+ // Load the pre-laod register 0 low word
+ value = (short) (insn->data[2] & 0xFFFF);
+ outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+
+ // Set Counter Mode Register
+ cmReg.value = (short) (insn->data[1] & 0xFFFF);
+ cmReg.reg.preloadRegSel = 1; // PR1
+ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+
+ // Load the pre-laod register 1 high word
+ value = (short) ((insn->data[3] >> 16) & 0xFFFF);
+ outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+
+ // Load the pre-laod register 1 low word
+ value = (short) (insn->data[3] & 0xFFFF);
+ outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+
+ // Write the Counter Control Register
+ if (insn->data[3] != 0) {
+ value = (short) (insn->data[3] & 0xFFFF);
+ outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+ }
+ break;
+
+ default:
+ printk("s526: unsupported GPCT_insn_config\n");
+ return -EINVAL;
+ break;
+ }
+
+ return insn->n;
+}
+
+static int s526_gpct_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int subdev_channel = CR_CHAN(insn->chanspec); // Unpack chanspec
+ short value;
+
+ printk("s526: GPCT_INSN_WRITE on channel %d\n", subdev_channel);
+ cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
+ printk("s526: Counter Mode Register: %x\n", cmReg.value);
+ // Check what Application of Counter this channel is configured for
+ switch (devpriv->s526_gpct_config[subdev_channel].app) {
+ case PositionMeasurement:
+ printk("S526: INSN_WRITE: PM\n");
+ outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
+ subdev_channel));
+ outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
+ break;
+
+ case SinglePulseGeneration:
+ printk("S526: INSN_WRITE: SPG\n");
+ outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
+ subdev_channel));
+ outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
+ break;
+
+ case PulseTrainGeneration:
+ /* data[0] contains the PULSE_WIDTH
+ data[1] contains the PULSE_PERIOD
+ @pre PULSE_PERIOD > PULSE_WIDTH > 0
+ The above periods must be expressed as a multiple of the
+ pulse frequency on the selected source
+ */
+ printk("S526: INSN_WRITE: PTG\n");
+ if ((insn->data[1] > insn->data[0]) && (insn->data[0] > 0)) {
+ (devpriv->s526_gpct_config[subdev_channel]).data[0] =
+ insn->data[0];
+ (devpriv->s526_gpct_config[subdev_channel]).data[1] =
+ insn->data[1];
+ } else {
+ printk("%d \t %d\n", insn->data[1], insn->data[2]);
+ printk("s526: INSN_WRITE: PTG: Problem with Pulse params\n");
+ return -EINVAL;
+ }
+
+ value = (short) ((*data >> 16) & 0xFFFF);
+ outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+ value = (short) (*data & 0xFFFF);
+ outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+ break;
+ default: // Impossible
+ printk("s526: INSN_WRITE: Functionality %d not implemented yet\n", devpriv->s526_gpct_config[subdev_channel].app);
+ return -EINVAL;
+ break;
+ }
+ // return the number of samples written
+ return insn->n;
+}
+
+#define ISR_ADC_DONE 0x4
+static int s526_ai_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int result = -EINVAL;
+
+ if (insn->n < 1)
+ return result;
+
+ result = insn->n;
+
+ /* data[0] : channels was set in relevant bits.
+ data[1] : delay
+ */
+ /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
+ * enable channels here. The channel should be enabled in the
+ * INSN_READ handler. */
+
+ // Enable ADC interrupt
+ outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
+// printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC)));
+ devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
+ if (data[1] > 0)
+ devpriv->s526_ai_config |= 0x8000; //set the delay
+
+ devpriv->s526_ai_config |= 0x0001; // ADC start bit.
+
+ return result;
+}
+
+/*
+ * "instructions" read/write data in "one-shot" or "software-triggered"
+ * mode.
+ */
+static int s526_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, i;
+ int chan = CR_CHAN(insn->chanspec);
+ unsigned short value;
+ unsigned int d;
+ unsigned int status;
+
+ /* Set configured delay, enable channel for this channel only,
+ * select "ADC read" channel, set "ADC start" bit. */
+ value = (devpriv->s526_ai_config & 0x8000) |
+ ((1 << 5) << chan) | (chan << 1) | 0x0001;
+
+ /* convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion */
+ outw(value, ADDR_REG(REG_ADC));
+// printk("s526: Wrote 0x%04x to ADC\n", value);
+// printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC)));
+
+#define TIMEOUT 100
+ /* wait for conversion to end */
+ for (i = 0; i < TIMEOUT; i++) {
+ status = inw(ADDR_REG(REG_ISR));
+ if (status & ISR_ADC_DONE) {
+ outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
+ break;
+ }
+ }
+ if (i == TIMEOUT) {
+ /* rt_printk() should be used instead of printk()
+ * whenever the code can be called from real-time. */
+ rt_printk("s526: ADC(0x%04x) timeout\n",
+ inw(ADDR_REG(REG_ISR)));
+ return -ETIMEDOUT;
+ }
+
+ /* read data */
+ d = inw(ADDR_REG(REG_ADD));
+// printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF));
+
+ /* munge data */
+ data[n] = d ^ 0x8000;
+ }
+
+ /* return the number of samples read/written */
+ return n;
+}
+
+static int s526_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+ unsigned short val;
+
+// printk("s526_ao_winsn\n");
+ val = chan << 1;
+// outw(val, dev->iobase + REG_DAC);
+ outw(val, ADDR_REG(REG_DAC));
+
+ /* Writing a list of values to an AO channel is probably not
+ * very useful, but that's how the interface is defined. */
+ for (i = 0; i < insn->n; i++) {
+ /* a typical programming sequence */
+// outw(data[i], dev->iobase + REG_ADD); // write the data to preload register
+ outw(data[i], ADDR_REG(REG_ADD)); // write the data to preload register
+ devpriv->ao_readback[chan] = data[i];
+// outw(val + 1, dev->iobase + REG_DAC); // starts the D/A conversion.
+ outw(val + 1, ADDR_REG(REG_DAC)); // starts the D/A conversion.
+ }
+
+ /* return the number of samples read/written */
+ return i;
+}
+
+/* AO subdevices should have a read insn as well as a write insn.
+ * Usually this means copying a value stored in devpriv. */
+static int s526_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = devpriv->ao_readback[chan];
+
+ return i;
+}
+
+/* DIO devices are slightly special. Although it is possible to
+ * implement the insn_read/insn_write interface, it is much more
+ * useful to applications if you implement the insn_bits interface.
+ * This allows packed reading/writing of the DIO channels. The
+ * comedi core can convert between insn_bits and insn_read/write */
+static int s526_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ /* The insn data is a mask in data[0] and the new data
+ * in data[1], each channel cooresponding to a bit. */
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+ /* Write out the new digital output lines */
+ outw(s->state, ADDR_REG(REG_DIO));
+ }
+
+ /* on return, data[1] contains the value of the digital
+ * input and output lines. */
+ data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; // low 8 bits are the data
+ /* or we could just return the software copy of the output values if
+ * it was a purely digital output subdevice */
+ //data[1]=s->state;
+
+ return 2;
+}
+
+static int s526_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+ short value;
+
+ printk("S526 DIO insn_config\n");
+
+ if (insn->n != 1)
+ return -EINVAL;
+
+ value = inw(ADDR_REG(REG_DIO));
+
+ /* The input or output configuration of each digital line is
+ * configured by a special insn_config instruction. chanspec
+ * contains the channel to be changed, and data[0] contains the
+ * value COMEDI_INPUT or COMEDI_OUTPUT. */
+
+ if (data[0] == COMEDI_OUTPUT) {
+ value |= 1 << (chan + 10); // bit 10/11 set the group 1/2's mode
+ s->io_bits |= (0xF << chan);
+ } else {
+ value &= ~(1 << (chan + 10)); // 1 is output, 0 is input.
+ s->io_bits &= ~(0xF << chan);
+ }
+ outw(value, ADDR_REG(REG_DIO));
+
+ return 1;
+}
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_INITCLEANUP(driver_s526);
diff --git a/drivers/staging/comedi/drivers/s626.c b/drivers/staging/comedi/drivers/s626.c
index 469ee8c474c9..30dec9dab19b 100644
--- a/drivers/staging/comedi/drivers/s626.c
+++ b/drivers/staging/comedi/drivers/s626.c
@@ -82,7 +82,7 @@ MODULE_AUTHOR("Gianluca Palli <gpalli@deis.unibo.it>");
MODULE_DESCRIPTION("Sensoray 626 Comedi driver module");
MODULE_LICENSE("GPL");
-typedef struct s626_board_struct {
+struct s626_board {
const char *name;
int ai_chans;
int ai_bits;
@@ -91,22 +91,22 @@ typedef struct s626_board_struct {
int dio_chans;
int dio_banks;
int enc_chans;
-} s626_board;
+};
-static const s626_board s626_boards[] = {
+static const struct s626_board s626_boards[] = {
{
name: "s626",
- ai_chans:S626_ADC_CHANNELS,
+ ai_chans : S626_ADC_CHANNELS,
ai_bits: 14,
- ao_chans:S626_DAC_CHANNELS,
+ ao_chans : S626_DAC_CHANNELS,
ao_bits: 13,
- dio_chans:S626_DIO_CHANNELS,
- dio_banks:S626_DIO_BANKS,
- enc_chans:S626_ENCODER_CHANNELS,
+ dio_chans : S626_DIO_CHANNELS,
+ dio_banks : S626_DIO_BANKS,
+ enc_chans : S626_ENCODER_CHANNELS,
}
};
-#define thisboard ((const s626_board *)dev->board_ptr)
+#define thisboard ((const struct s626_board *)dev->board_ptr)
#define PCI_VENDOR_ID_S626 0x1131
#define PCI_DEVICE_ID_S626 0x7146
@@ -118,57 +118,49 @@ static DEFINE_PCI_DEVICE_TABLE(s626_pci_table) = {
MODULE_DEVICE_TABLE(pci, s626_pci_table);
-static int s626_attach(comedi_device * dev, comedi_devconfig * it);
-static int s626_detach(comedi_device * dev);
+static int s626_attach(struct comedi_device *dev, struct comedi_devconfig *it);
+static int s626_detach(struct comedi_device *dev);
-static comedi_driver driver_s626 = {
+static struct comedi_driver driver_s626 = {
driver_name:"s626",
- module:THIS_MODULE,
- attach:s626_attach,
- detach:s626_detach,
+ module : THIS_MODULE,
+ attach : s626_attach,
+ detach : s626_detach,
};
-typedef struct {
+struct s626_private {
struct pci_dev *pdev;
void *base_addr;
int got_regions;
short allocatedBuf;
- uint8_t ai_cmd_running; // ai_cmd is running
- uint8_t ai_continous; // continous aquisition
- int ai_sample_count; // number of samples to aquire
- unsigned int ai_sample_timer; // time between samples in
- // units of the timer
- int ai_convert_count; // conversion counter
- unsigned int ai_convert_timer; // time between conversion in
- // units of the timer
- uint16_t CounterIntEnabs; //Counter interrupt enable
- //mask for MISC2 register.
- uint8_t AdcItems; //Number of items in ADC poll
- //list.
- DMABUF RPSBuf; //DMA buffer used to hold ADC
- //(RPS1) program.
- DMABUF ANABuf; //DMA buffer used to receive
- //ADC data and hold DAC data.
- uint32_t *pDacWBuf; //Pointer to logical adrs of
- //DMA buffer used to hold DAC
- //data.
- uint16_t Dacpol; //Image of DAC polarity
- //register.
- uint8_t TrimSetpoint[12]; //Images of TrimDAC setpoints.
- //registers.
- uint16_t ChargeEnabled; //Image of MISC2 Battery
- //Charge Enabled (0 or
- //WRMISC2_CHARGE_ENABLE).
- uint16_t WDInterval; //Image of MISC2 watchdog
- //interval control bits.
- uint32_t I2CAdrs; //I2C device address for
- //onboard EEPROM (board rev
- //dependent).
- // short I2Cards;
- lsampl_t ao_readback[S626_DAC_CHANNELS];
-} s626_private;
-
-typedef struct {
+ uint8_t ai_cmd_running; /* ai_cmd is running */
+ uint8_t ai_continous; /* continous aquisition */
+ int ai_sample_count; /* number of samples to aquire */
+ unsigned int ai_sample_timer;
+ /* time between samples in units of the timer */
+ int ai_convert_count; /* conversion counter */
+ unsigned int ai_convert_timer;
+ /* time between conversion in units of the timer */
+ uint16_t CounterIntEnabs;
+ /* Counter interrupt enable mask for MISC2 register. */
+ uint8_t AdcItems; /* Number of items in ADC poll list. */
+ struct bufferDMA RPSBuf; /* DMA buffer used to hold ADC (RPS1) program. */
+ struct bufferDMA ANABuf;
+ /* DMA buffer used to receive ADC data and hold DAC data. */
+ uint32_t *pDacWBuf;
+ /* Pointer to logical adrs of DMA buffer used to hold DAC data. */
+ uint16_t Dacpol; /* Image of DAC polarity register. */
+ uint8_t TrimSetpoint[12]; /* Images of TrimDAC setpoints */
+ uint16_t ChargeEnabled; /* Image of MISC2 Battery */
+ /* Charge Enabled (0 or WRMISC2_CHARGE_ENABLE). */
+ uint16_t WDInterval; /* Image of MISC2 watchdog interval control bits. */
+ uint32_t I2CAdrs;
+ /* I2C device address for onboard EEPROM (board rev dependent). */
+ /* short I2Cards; */
+ unsigned int ao_readback[S626_DAC_CHANNELS];
+};
+
+struct dio_private {
uint16_t RDDIn;
uint16_t WRDOut;
uint16_t RDEdgSel;
@@ -178,324 +170,322 @@ typedef struct {
uint16_t RDCapFlg;
uint16_t RDIntSel;
uint16_t WRIntSel;
-} dio_private;
+};
-static dio_private dio_private_A = {
+static struct dio_private dio_private_A = {
RDDIn:LP_RDDINA,
- WRDOut:LP_WRDOUTA,
- RDEdgSel:LP_RDEDGSELA,
- WREdgSel:LP_WREDGSELA,
- RDCapSel:LP_RDCAPSELA,
- WRCapSel:LP_WRCAPSELA,
- RDCapFlg:LP_RDCAPFLGA,
- RDIntSel:LP_RDINTSELA,
- WRIntSel:LP_WRINTSELA,
+ WRDOut : LP_WRDOUTA,
+ RDEdgSel : LP_RDEDGSELA,
+ WREdgSel : LP_WREDGSELA,
+ RDCapSel : LP_RDCAPSELA,
+ WRCapSel : LP_WRCAPSELA,
+ RDCapFlg : LP_RDCAPFLGA,
+ RDIntSel : LP_RDINTSELA,
+ WRIntSel : LP_WRINTSELA,
};
-static dio_private dio_private_B = {
+static struct dio_private dio_private_B = {
RDDIn:LP_RDDINB,
- WRDOut:LP_WRDOUTB,
- RDEdgSel:LP_RDEDGSELB,
- WREdgSel:LP_WREDGSELB,
- RDCapSel:LP_RDCAPSELB,
- WRCapSel:LP_WRCAPSELB,
- RDCapFlg:LP_RDCAPFLGB,
- RDIntSel:LP_RDINTSELB,
- WRIntSel:LP_WRINTSELB,
+ WRDOut : LP_WRDOUTB,
+ RDEdgSel : LP_RDEDGSELB,
+ WREdgSel : LP_WREDGSELB,
+ RDCapSel : LP_RDCAPSELB,
+ WRCapSel : LP_WRCAPSELB,
+ RDCapFlg : LP_RDCAPFLGB,
+ RDIntSel : LP_RDINTSELB,
+ WRIntSel : LP_WRINTSELB,
};
-static dio_private dio_private_C = {
+static struct dio_private dio_private_C = {
RDDIn:LP_RDDINC,
- WRDOut:LP_WRDOUTC,
- RDEdgSel:LP_RDEDGSELC,
- WREdgSel:LP_WREDGSELC,
- RDCapSel:LP_RDCAPSELC,
- WRCapSel:LP_WRCAPSELC,
- RDCapFlg:LP_RDCAPFLGC,
- RDIntSel:LP_RDINTSELC,
- WRIntSel:LP_WRINTSELC,
+ WRDOut : LP_WRDOUTC,
+ RDEdgSel : LP_RDEDGSELC,
+ WREdgSel : LP_WREDGSELC,
+ RDCapSel : LP_RDCAPSELC,
+ WRCapSel : LP_WRCAPSELC,
+ RDCapFlg : LP_RDCAPFLGC,
+ RDIntSel : LP_RDINTSELC,
+ WRIntSel : LP_WRINTSELC,
};
/* to group dio devices (48 bits mask and data are not allowed ???)
-static dio_private *dio_private_word[]={
+static struct dio_private *dio_private_word[]={
&dio_private_A,
&dio_private_B,
&dio_private_C,
};
*/
-#define devpriv ((s626_private *)dev->private)
-#define diopriv ((dio_private *)s->private)
+#define devpriv ((struct s626_private *)dev->private)
+#define diopriv ((struct dio_private *)s->private)
COMEDI_PCI_INITCLEANUP_NOMODULE(driver_s626, s626_pci_table);
-//ioctl routines
-static int s626_ai_insn_config(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data);
-/* static int s626_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); */
-static int s626_ai_insn_read(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data);
-static int s626_ai_cmd(comedi_device * dev, comedi_subdevice * s);
-static int s626_ai_cmdtest(comedi_device * dev, comedi_subdevice * s,
- comedi_cmd * cmd);
-static int s626_ai_cancel(comedi_device * dev, comedi_subdevice * s);
-static int s626_ao_winsn(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data);
-static int s626_ao_rinsn(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data);
-static int s626_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data);
-static int s626_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data);
-static int s626_dio_set_irq(comedi_device * dev, unsigned int chan);
-static int s626_dio_reset_irq(comedi_device * dev, unsigned int gruop,
+/* ioctl routines */
+static int s626_ai_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+/* static int s626_ai_rinsn(struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data); */
+static int s626_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int s626_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
+static int s626_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd);
+static int s626_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
+static int s626_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int s626_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int s626_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int s626_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int s626_dio_set_irq(struct comedi_device *dev, unsigned int chan);
+static int s626_dio_reset_irq(struct comedi_device *dev, unsigned int gruop,
unsigned int mask);
-static int s626_dio_clear_irq(comedi_device * dev);
-static int s626_enc_insn_config(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data);
-static int s626_enc_insn_read(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data);
-static int s626_enc_insn_write(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data);
+static int s626_dio_clear_irq(struct comedi_device *dev);
+static int s626_enc_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int s626_enc_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
+static int s626_enc_insn_write(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data);
static int s626_ns_to_timer(int *nanosec, int round_mode);
-static int s626_ai_load_polllist(uint8_t * ppl, comedi_cmd * cmd);
-static int s626_ai_inttrig(comedi_device * dev, comedi_subdevice * s,
+static int s626_ai_load_polllist(uint8_t *ppl, struct comedi_cmd *cmd);
+static int s626_ai_inttrig(struct comedi_device *dev, struct comedi_subdevice *s,
unsigned int trignum);
static irqreturn_t s626_irq_handler(int irq, void *d PT_REGS_ARG);
-static lsampl_t s626_ai_reg_to_uint(int data);
-/* static lsampl_t s626_uint_to_reg(comedi_subdevice *s, int data); */
+static unsigned int s626_ai_reg_to_uint(int data);
+/* static unsigned int s626_uint_to_reg(struct comedi_subdevice *s, int data); */
-//end ioctl routines
+/* end ioctl routines */
-//internal routines
-static void s626_dio_init(comedi_device * dev);
-static void ResetADC(comedi_device * dev, uint8_t * ppl);
-static void LoadTrimDACs(comedi_device * dev);
-static void WriteTrimDAC(comedi_device * dev, uint8_t LogicalChan,
+/* internal routines */
+static void s626_dio_init(struct comedi_device *dev);
+static void ResetADC(struct comedi_device *dev, uint8_t *ppl);
+static void LoadTrimDACs(struct comedi_device *dev);
+static void WriteTrimDAC(struct comedi_device *dev, uint8_t LogicalChan,
uint8_t DacData);
-static uint8_t I2Cread(comedi_device * dev, uint8_t addr);
-static uint32_t I2Chandshake(comedi_device * dev, uint32_t val);
-static void SetDAC(comedi_device * dev, uint16_t chan, short dacdata);
-static void SendDAC(comedi_device * dev, uint32_t val);
-static void WriteMISC2(comedi_device * dev, uint16_t NewImage);
-static void DEBItransfer(comedi_device * dev);
-static uint16_t DEBIread(comedi_device * dev, uint16_t addr);
-static void DEBIwrite(comedi_device * dev, uint16_t addr, uint16_t wdata);
-static void DEBIreplace(comedi_device * dev, uint16_t addr, uint16_t mask,
+static uint8_t I2Cread(struct comedi_device *dev, uint8_t addr);
+static uint32_t I2Chandshake(struct comedi_device *dev, uint32_t val);
+static void SetDAC(struct comedi_device *dev, uint16_t chan, short dacdata);
+static void SendDAC(struct comedi_device *dev, uint32_t val);
+static void WriteMISC2(struct comedi_device *dev, uint16_t NewImage);
+static void DEBItransfer(struct comedi_device *dev);
+static uint16_t DEBIread(struct comedi_device *dev, uint16_t addr);
+static void DEBIwrite(struct comedi_device *dev, uint16_t addr, uint16_t wdata);
+static void DEBIreplace(struct comedi_device *dev, uint16_t addr, uint16_t mask,
uint16_t wdata);
-static void CloseDMAB(comedi_device * dev, DMABUF * pdma, size_t bsize);
-
-// COUNTER OBJECT ------------------------------------------------
-typedef struct enc_private_struct {
- // Pointers to functions that differ for A and B counters:
- uint16_t(*GetEnable) (comedi_device * dev, struct enc_private_struct *); //Return clock enable.
- uint16_t(*GetIntSrc) (comedi_device * dev, struct enc_private_struct *); //Return interrupt source.
- uint16_t(*GetLoadTrig) (comedi_device * dev, struct enc_private_struct *); //Return preload trigger source.
- uint16_t(*GetMode) (comedi_device * dev, struct enc_private_struct *); //Return standardized operating mode.
- void (*PulseIndex) (comedi_device * dev, struct enc_private_struct *); //Generate soft index strobe.
- void (*SetEnable) (comedi_device * dev, struct enc_private_struct *, uint16_t enab); //Program clock enable.
- void (*SetIntSrc) (comedi_device * dev, struct enc_private_struct *, uint16_t IntSource); //Program interrupt source.
- void (*SetLoadTrig) (comedi_device * dev, struct enc_private_struct *, uint16_t Trig); //Program preload trigger source.
- void (*SetMode) (comedi_device * dev, struct enc_private_struct *, uint16_t Setup, uint16_t DisableIntSrc); //Program standardized operating mode.
- void (*ResetCapFlags) (comedi_device * dev, struct enc_private_struct *); //Reset event capture flags.
-
- uint16_t MyCRA; // Address of CRA register.
- uint16_t MyCRB; // Address of CRB register.
- uint16_t MyLatchLsw; // Address of Latch least-significant-word
- // register.
- uint16_t MyEventBits[4]; // Bit translations for IntSrc -->RDMISC2.
-} enc_private; //counter object
-
-#define encpriv ((enc_private *)(dev->subdevices+5)->private)
-
-//counters routines
-static void s626_timer_load(comedi_device * dev, enc_private * k, int tick);
-static uint32_t ReadLatch(comedi_device * dev, enc_private * k);
-static void ResetCapFlags_A(comedi_device * dev, enc_private * k);
-static void ResetCapFlags_B(comedi_device * dev, enc_private * k);
-static uint16_t GetMode_A(comedi_device * dev, enc_private * k);
-static uint16_t GetMode_B(comedi_device * dev, enc_private * k);
-static void SetMode_A(comedi_device * dev, enc_private * k, uint16_t Setup,
+static void CloseDMAB(struct comedi_device *dev, struct bufferDMA *pdma, size_t bsize);
+
+/* COUNTER OBJECT ------------------------------------------------ */
+struct enc_private {
+ /* Pointers to functions that differ for A and B counters: */
+ uint16_t(*GetEnable) (struct comedi_device *dev, struct enc_private *); /* Return clock enable. */
+ uint16_t(*GetIntSrc) (struct comedi_device *dev, struct enc_private *); /* Return interrupt source. */
+ uint16_t(*GetLoadTrig) (struct comedi_device *dev, struct enc_private *); /* Return preload trigger source. */
+ uint16_t(*GetMode) (struct comedi_device *dev, struct enc_private *); /* Return standardized operating mode. */
+ void (*PulseIndex) (struct comedi_device *dev, struct enc_private *); /* Generate soft index strobe. */
+ void (*SetEnable) (struct comedi_device *dev, struct enc_private *, uint16_t enab); /* Program clock enable. */
+ void (*SetIntSrc) (struct comedi_device *dev, struct enc_private *, uint16_t IntSource); /* Program interrupt source. */
+ void (*SetLoadTrig) (struct comedi_device *dev, struct enc_private *, uint16_t Trig); /* Program preload trigger source. */
+ void (*SetMode) (struct comedi_device *dev, struct enc_private *, uint16_t Setup, uint16_t DisableIntSrc); /* Program standardized operating mode. */
+ void (*ResetCapFlags) (struct comedi_device *dev, struct enc_private *); /* Reset event capture flags. */
+
+ uint16_t MyCRA; /* Address of CRA register. */
+ uint16_t MyCRB; /* Address of CRB register. */
+ uint16_t MyLatchLsw; /* Address of Latch least-significant-word */
+ /* register. */
+ uint16_t MyEventBits[4]; /* Bit translations for IntSrc -->RDMISC2. */
+};
+
+#define encpriv ((struct enc_private *)(dev->subdevices+5)->private)
+
+/* counters routines */
+static void s626_timer_load(struct comedi_device *dev, struct enc_private *k, int tick);
+static uint32_t ReadLatch(struct comedi_device *dev, struct enc_private *k);
+static void ResetCapFlags_A(struct comedi_device *dev, struct enc_private *k);
+static void ResetCapFlags_B(struct comedi_device *dev, struct enc_private *k);
+static uint16_t GetMode_A(struct comedi_device *dev, struct enc_private *k);
+static uint16_t GetMode_B(struct comedi_device *dev, struct enc_private *k);
+static void SetMode_A(struct comedi_device *dev, struct enc_private *k, uint16_t Setup,
uint16_t DisableIntSrc);
-static void SetMode_B(comedi_device * dev, enc_private * k, uint16_t Setup,
+static void SetMode_B(struct comedi_device *dev, struct enc_private *k, uint16_t Setup,
uint16_t DisableIntSrc);
-static void SetEnable_A(comedi_device * dev, enc_private * k, uint16_t enab);
-static void SetEnable_B(comedi_device * dev, enc_private * k, uint16_t enab);
-static uint16_t GetEnable_A(comedi_device * dev, enc_private * k);
-static uint16_t GetEnable_B(comedi_device * dev, enc_private * k);
-static void SetLatchSource(comedi_device * dev, enc_private * k,
+static void SetEnable_A(struct comedi_device *dev, struct enc_private *k, uint16_t enab);
+static void SetEnable_B(struct comedi_device *dev, struct enc_private *k, uint16_t enab);
+static uint16_t GetEnable_A(struct comedi_device *dev, struct enc_private *k);
+static uint16_t GetEnable_B(struct comedi_device *dev, struct enc_private *k);
+static void SetLatchSource(struct comedi_device *dev, struct enc_private *k,
uint16_t value);
-/* static uint16_t GetLatchSource(comedi_device *dev, enc_private *k ); */
-static void SetLoadTrig_A(comedi_device * dev, enc_private * k, uint16_t Trig);
-static void SetLoadTrig_B(comedi_device * dev, enc_private * k, uint16_t Trig);
-static uint16_t GetLoadTrig_A(comedi_device * dev, enc_private * k);
-static uint16_t GetLoadTrig_B(comedi_device * dev, enc_private * k);
-static void SetIntSrc_B(comedi_device * dev, enc_private * k,
+/* static uint16_t GetLatchSource(struct comedi_device *dev, struct enc_private *k ); */
+static void SetLoadTrig_A(struct comedi_device *dev, struct enc_private *k, uint16_t Trig);
+static void SetLoadTrig_B(struct comedi_device *dev, struct enc_private *k, uint16_t Trig);
+static uint16_t GetLoadTrig_A(struct comedi_device *dev, struct enc_private *k);
+static uint16_t GetLoadTrig_B(struct comedi_device *dev, struct enc_private *k);
+static void SetIntSrc_B(struct comedi_device *dev, struct enc_private *k,
uint16_t IntSource);
-static void SetIntSrc_A(comedi_device * dev, enc_private * k,
+static void SetIntSrc_A(struct comedi_device *dev, struct enc_private *k,
uint16_t IntSource);
-static uint16_t GetIntSrc_A(comedi_device * dev, enc_private * k);
-static uint16_t GetIntSrc_B(comedi_device * dev, enc_private * k);
-/* static void SetClkMult(comedi_device *dev, enc_private *k, uint16_t value ) ; */
-/* static uint16_t GetClkMult(comedi_device *dev, enc_private *k ) ; */
-/* static void SetIndexPol(comedi_device *dev, enc_private *k, uint16_t value ); */
-/* static uint16_t GetClkPol(comedi_device *dev, enc_private *k ) ; */
-/* static void SetIndexSrc( comedi_device *dev,enc_private *k, uint16_t value ); */
-/* static uint16_t GetClkSrc( comedi_device *dev,enc_private *k ); */
-/* static void SetIndexSrc( comedi_device *dev,enc_private *k, uint16_t value ); */
-/* static uint16_t GetIndexSrc( comedi_device *dev,enc_private *k ); */
-static void PulseIndex_A(comedi_device * dev, enc_private * k);
-static void PulseIndex_B(comedi_device * dev, enc_private * k);
-static void Preload(comedi_device * dev, enc_private * k, uint32_t value);
-static void CountersInit(comedi_device * dev);
-//end internal routines
-
-/////////////////////////////////////////////////////////////////////////
-// Counter objects constructor.
-
-// Counter overflow/index event flag masks for RDMISC2.
-#define INDXMASK(C) ( 1 << ( ( (C) > 2 ) ? ( (C) * 2 - 1 ) : ( (C) * 2 + 4 ) ) )
-#define OVERMASK(C) ( 1 << ( ( (C) > 2 ) ? ( (C) * 2 + 5 ) : ( (C) * 2 + 10 ) ) )
+static uint16_t GetIntSrc_A(struct comedi_device *dev, struct enc_private *k);
+static uint16_t GetIntSrc_B(struct comedi_device *dev, struct enc_private *k);
+/* static void SetClkMult(struct comedi_device *dev, struct enc_private *k, uint16_t value ) ; */
+/* static uint16_t GetClkMult(struct comedi_device *dev, struct enc_private *k ) ; */
+/* static void SetIndexPol(struct comedi_device *dev, struct enc_private *k, uint16_t value ); */
+/* static uint16_t GetClkPol(struct comedi_device *dev, struct enc_private *k ) ; */
+/* static void SetIndexSrc( struct comedi_device *dev,struct enc_private *k, uint16_t value ); */
+/* static uint16_t GetClkSrc( struct comedi_device *dev,struct enc_private *k ); */
+/* static void SetIndexSrc( struct comedi_device *dev,struct enc_private *k, uint16_t value ); */
+/* static uint16_t GetIndexSrc( struct comedi_device *dev,struct enc_private *k ); */
+static void PulseIndex_A(struct comedi_device *dev, struct enc_private *k);
+static void PulseIndex_B(struct comedi_device *dev, struct enc_private *k);
+static void Preload(struct comedi_device *dev, struct enc_private *k, uint32_t value);
+static void CountersInit(struct comedi_device *dev);
+/* end internal routines */
+
+/* Counter objects constructor. */
+
+/* Counter overflow/index event flag masks for RDMISC2. */
+#define INDXMASK(C) (1 << (((C) > 2) ? ((C) * 2 - 1) : ((C) * 2 + 4)))
+#define OVERMASK(C) (1 << (((C) > 2) ? ((C) * 2 + 5) : ((C) * 2 + 10)))
#define EVBITS(C) { 0, OVERMASK(C), INDXMASK(C), OVERMASK(C) | INDXMASK(C) }
-// Translation table to map IntSrc into equivalent RDMISC2 event flag
-// bits.
-//static const uint16_t EventBits[][4] = { EVBITS(0), EVBITS(1), EVBITS(2), EVBITS(3), EVBITS(4), EVBITS(5) };
+/* Translation table to map IntSrc into equivalent RDMISC2 event flag bits. */
+/* static const uint16_t EventBits[][4] = { EVBITS(0), EVBITS(1), EVBITS(2), EVBITS(3), EVBITS(4), EVBITS(5) }; */
-/* enc_private; */
-static enc_private enc_private_data[] = {
+/* struct enc_private; */
+static struct enc_private enc_private_data[] = {
{
GetEnable:GetEnable_A,
- GetIntSrc:GetIntSrc_A,
- GetLoadTrig:GetLoadTrig_A,
- GetMode: GetMode_A,
- PulseIndex:PulseIndex_A,
- SetEnable:SetEnable_A,
- SetIntSrc:SetIntSrc_A,
- SetLoadTrig:SetLoadTrig_A,
- SetMode: SetMode_A,
- ResetCapFlags:ResetCapFlags_A,
- MyCRA: LP_CR0A,
- MyCRB: LP_CR0B,
- MyLatchLsw:LP_CNTR0ALSW,
- MyEventBits:EVBITS(0),
+ GetIntSrc : GetIntSrc_A,
+ GetLoadTrig : GetLoadTrig_A,
+ GetMode : GetMode_A,
+ PulseIndex : PulseIndex_A,
+ SetEnable : SetEnable_A,
+ SetIntSrc : SetIntSrc_A,
+ SetLoadTrig : SetLoadTrig_A,
+ SetMode : SetMode_A,
+ ResetCapFlags : ResetCapFlags_A,
+ MyCRA : LP_CR0A,
+ MyCRB : LP_CR0B,
+ MyLatchLsw : LP_CNTR0ALSW,
+ MyEventBits : EVBITS(0),
},
{
GetEnable:GetEnable_A,
- GetIntSrc:GetIntSrc_A,
- GetLoadTrig:GetLoadTrig_A,
- GetMode: GetMode_A,
- PulseIndex:PulseIndex_A,
- SetEnable:SetEnable_A,
- SetIntSrc:SetIntSrc_A,
- SetLoadTrig:SetLoadTrig_A,
- SetMode: SetMode_A,
- ResetCapFlags:ResetCapFlags_A,
- MyCRA: LP_CR1A,
- MyCRB: LP_CR1B,
- MyLatchLsw:LP_CNTR1ALSW,
- MyEventBits:EVBITS(1),
+ GetIntSrc : GetIntSrc_A,
+ GetLoadTrig : GetLoadTrig_A,
+ GetMode : GetMode_A,
+ PulseIndex : PulseIndex_A,
+ SetEnable : SetEnable_A,
+ SetIntSrc : SetIntSrc_A,
+ SetLoadTrig : SetLoadTrig_A,
+ SetMode : SetMode_A,
+ ResetCapFlags : ResetCapFlags_A,
+ MyCRA : LP_CR1A,
+ MyCRB : LP_CR1B,
+ MyLatchLsw : LP_CNTR1ALSW,
+ MyEventBits : EVBITS(1),
},
{
GetEnable:GetEnable_A,
- GetIntSrc:GetIntSrc_A,
- GetLoadTrig:GetLoadTrig_A,
- GetMode: GetMode_A,
- PulseIndex:PulseIndex_A,
- SetEnable:SetEnable_A,
- SetIntSrc:SetIntSrc_A,
- SetLoadTrig:SetLoadTrig_A,
- SetMode: SetMode_A,
- ResetCapFlags:ResetCapFlags_A,
- MyCRA: LP_CR2A,
- MyCRB: LP_CR2B,
- MyLatchLsw:LP_CNTR2ALSW,
- MyEventBits:EVBITS(2),
+ GetIntSrc : GetIntSrc_A,
+ GetLoadTrig : GetLoadTrig_A,
+ GetMode : GetMode_A,
+ PulseIndex : PulseIndex_A,
+ SetEnable : SetEnable_A,
+ SetIntSrc : SetIntSrc_A,
+ SetLoadTrig : SetLoadTrig_A,
+ SetMode : SetMode_A,
+ ResetCapFlags : ResetCapFlags_A,
+ MyCRA : LP_CR2A,
+ MyCRB : LP_CR2B,
+ MyLatchLsw : LP_CNTR2ALSW,
+ MyEventBits : EVBITS(2),
},
{
GetEnable:GetEnable_B,
- GetIntSrc:GetIntSrc_B,
- GetLoadTrig:GetLoadTrig_B,
- GetMode: GetMode_B,
- PulseIndex:PulseIndex_B,
- SetEnable:SetEnable_B,
- SetIntSrc:SetIntSrc_B,
- SetLoadTrig:SetLoadTrig_B,
- SetMode: SetMode_B,
- ResetCapFlags:ResetCapFlags_B,
- MyCRA: LP_CR0A,
- MyCRB: LP_CR0B,
- MyLatchLsw:LP_CNTR0BLSW,
- MyEventBits:EVBITS(3),
+ GetIntSrc : GetIntSrc_B,
+ GetLoadTrig : GetLoadTrig_B,
+ GetMode : GetMode_B,
+ PulseIndex : PulseIndex_B,
+ SetEnable : SetEnable_B,
+ SetIntSrc : SetIntSrc_B,
+ SetLoadTrig : SetLoadTrig_B,
+ SetMode : SetMode_B,
+ ResetCapFlags : ResetCapFlags_B,
+ MyCRA : LP_CR0A,
+ MyCRB : LP_CR0B,
+ MyLatchLsw : LP_CNTR0BLSW,
+ MyEventBits : EVBITS(3),
},
{
GetEnable:GetEnable_B,
- GetIntSrc:GetIntSrc_B,
- GetLoadTrig:GetLoadTrig_B,
- GetMode: GetMode_B,
- PulseIndex:PulseIndex_B,
- SetEnable:SetEnable_B,
- SetIntSrc:SetIntSrc_B,
- SetLoadTrig:SetLoadTrig_B,
- SetMode: SetMode_B,
- ResetCapFlags:ResetCapFlags_B,
- MyCRA: LP_CR1A,
- MyCRB: LP_CR1B,
- MyLatchLsw:LP_CNTR1BLSW,
- MyEventBits:EVBITS(4),
+ GetIntSrc : GetIntSrc_B,
+ GetLoadTrig : GetLoadTrig_B,
+ GetMode : GetMode_B,
+ PulseIndex : PulseIndex_B,
+ SetEnable : SetEnable_B,
+ SetIntSrc : SetIntSrc_B,
+ SetLoadTrig : SetLoadTrig_B,
+ SetMode : SetMode_B,
+ ResetCapFlags : ResetCapFlags_B,
+ MyCRA : LP_CR1A,
+ MyCRB : LP_CR1B,
+ MyLatchLsw : LP_CNTR1BLSW,
+ MyEventBits : EVBITS(4),
},
{
GetEnable:GetEnable_B,
- GetIntSrc:GetIntSrc_B,
- GetLoadTrig:GetLoadTrig_B,
- GetMode: GetMode_B,
- PulseIndex:PulseIndex_B,
- SetEnable:SetEnable_B,
- SetIntSrc:SetIntSrc_B,
- SetLoadTrig:SetLoadTrig_B,
- SetMode: SetMode_B,
- ResetCapFlags:ResetCapFlags_B,
- MyCRA: LP_CR2A,
- MyCRB: LP_CR2B,
- MyLatchLsw:LP_CNTR2BLSW,
- MyEventBits:EVBITS(5),
+ GetIntSrc : GetIntSrc_B,
+ GetLoadTrig : GetLoadTrig_B,
+ GetMode : GetMode_B,
+ PulseIndex : PulseIndex_B,
+ SetEnable : SetEnable_B,
+ SetIntSrc : SetIntSrc_B,
+ SetLoadTrig : SetLoadTrig_B,
+ SetMode : SetMode_B,
+ ResetCapFlags : ResetCapFlags_B,
+ MyCRA : LP_CR2A,
+ MyCRB : LP_CR2B,
+ MyLatchLsw : LP_CNTR2BLSW,
+ MyEventBits : EVBITS(5),
},
};
-// enab/disable a function or test status bit(s) that are accessed
-// through Main Control Registers 1 or 2.
-#define MC_ENABLE( REGADRS, CTRLWORD ) writel( ( (uint32_t)( CTRLWORD ) << 16 ) | (uint32_t)( CTRLWORD ),devpriv->base_addr+( REGADRS ) )
+/* enab/disable a function or test status bit(s) that are accessed */
+/* through Main Control Registers 1 or 2. */
+#define MC_ENABLE(REGADRS, CTRLWORD) writel(((uint32_t)(CTRLWORD) << 16) | (uint32_t)(CTRLWORD), devpriv->base_addr+(REGADRS))
-#define MC_DISABLE( REGADRS, CTRLWORD ) writel( (uint32_t)( CTRLWORD ) << 16 , devpriv->base_addr+( REGADRS ) )
+#define MC_DISABLE(REGADRS, CTRLWORD) writel((uint32_t)(CTRLWORD) << 16 , devpriv->base_addr+(REGADRS))
-#define MC_TEST( REGADRS, CTRLWORD ) ( ( readl(devpriv->base_addr+( REGADRS )) & CTRLWORD ) != 0 )
+#define MC_TEST(REGADRS, CTRLWORD) ((readl(devpriv->base_addr+(REGADRS)) & CTRLWORD) != 0)
/* #define WR7146(REGARDS,CTRLWORD)
writel(CTRLWORD,(uint32_t)(devpriv->base_addr+(REGARDS))) */
-#define WR7146(REGARDS,CTRLWORD) writel(CTRLWORD,devpriv->base_addr+(REGARDS))
+#define WR7146(REGARDS, CTRLWORD) writel(CTRLWORD, devpriv->base_addr+(REGARDS))
/* #define RR7146(REGARDS)
readl((uint32_t)(devpriv->base_addr+(REGARDS))) */
#define RR7146(REGARDS) readl(devpriv->base_addr+(REGARDS))
-#define BUGFIX_STREG(REGADRS) ( REGADRS - 4 )
+#define BUGFIX_STREG(REGADRS) (REGADRS - 4)
-// Write a time slot control record to TSL2.
-#define VECTPORT( VECTNUM ) (P_TSL2 + ( (VECTNUM) << 2 ))
-#define SETVECT( VECTNUM, VECTVAL ) WR7146(VECTPORT( VECTNUM ), (VECTVAL))
+/* Write a time slot control record to TSL2. */
+#define VECTPORT(VECTNUM) (P_TSL2 + ((VECTNUM) << 2))
+#define SETVECT(VECTNUM, VECTVAL) WR7146(VECTPORT(VECTNUM), (VECTVAL))
-// Code macros used for constructing I2C command bytes.
-#define I2C_B2(ATTR,VAL) ( ( (ATTR) << 6 ) | ( (VAL) << 24 ) )
-#define I2C_B1(ATTR,VAL) ( ( (ATTR) << 4 ) | ( (VAL) << 16 ) )
-#define I2C_B0(ATTR,VAL) ( ( (ATTR) << 2 ) | ( (VAL) << 8 ) )
+/* Code macros used for constructing I2C command bytes. */
+#define I2C_B2(ATTR, VAL) (((ATTR) << 6) | ((VAL) << 24))
+#define I2C_B1(ATTR, VAL) (((ATTR) << 4) | ((VAL) << 16))
+#define I2C_B0(ATTR, VAL) (((ATTR) << 2) | ((VAL) << 8))
-static const comedi_lrange s626_range_table = { 2, {
+static const struct comedi_lrange s626_range_table = { 2, {
RANGE(-5, 5),
RANGE(-10, 10),
}
};
-static int s626_attach(comedi_device * dev, comedi_devconfig * it)
+static int s626_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
/* uint8_t PollList; */
/* uint16_t AdcData; */
@@ -507,10 +497,10 @@ static int s626_attach(comedi_device * dev, comedi_devconfig * it)
int ret;
resource_size_t resourceStart;
dma_addr_t appdma;
- comedi_subdevice *s;
+ struct comedi_subdevice *s;
struct pci_dev *pdev;
- if (alloc_private(dev, sizeof(s626_private)) < 0)
+ if (alloc_private(dev, sizeof(struct s626_private)) < 0)
return -ENOMEM;
for (pdev = pci_get_device(PCI_VENDOR_ID_S626, PCI_DEVICE_ID_S626,
@@ -535,7 +525,8 @@ static int s626_attach(comedi_device * dev, comedi_devconfig * it)
return -ENODEV;
}
- if ((result = comedi_pci_enable(pdev, "s626")) < 0) {
+ result = comedi_pci_enable(pdev, "s626");
+ if (result < 0) {
printk("s626_attach: comedi_pci_enable fails\n");
return -ENODEV;
}
@@ -550,21 +541,22 @@ static int s626_attach(comedi_device * dev, comedi_devconfig * it)
}
if (devpriv->base_addr) {
- //disable master interrupt
+ /* disable master interrupt */
writel(0, devpriv->base_addr + P_IER);
- //soft reset
+ /* soft reset */
writel(MC1_SOFT_RESET, devpriv->base_addr + P_MC1);
- //DMA FIXME DMA//
+ /* DMA FIXME DMA// */
DEBUG("s626_attach: DMA ALLOCATION\n");
- //adc buffer allocation
+ /* adc buffer allocation */
devpriv->allocatedBuf = 0;
- if ((devpriv->ANABuf.LogicalBase =
- pci_alloc_consistent(devpriv->pdev, DMABUF_SIZE,
- &appdma)) == NULL) {
+ devpriv->ANABuf.LogicalBase =
+ pci_alloc_consistent(devpriv->pdev, DMABUF_SIZE, &appdma);
+
+ if (devpriv->ANABuf.LogicalBase == NULL) {
printk("s626_attach: DMA Memory mapping error\n");
return -ENOMEM;
}
@@ -575,9 +567,10 @@ static int s626_attach(comedi_device * dev, comedi_devconfig * it)
devpriv->allocatedBuf++;
- if ((devpriv->RPSBuf.LogicalBase =
- pci_alloc_consistent(devpriv->pdev, DMABUF_SIZE,
- &appdma)) == NULL) {
+ devpriv->RPSBuf.LogicalBase =
+ pci_alloc_consistent(devpriv->pdev, DMABUF_SIZE, &appdma);
+
+ if (devpriv->RPSBuf.LogicalBase == NULL) {
printk("s626_attach: DMA Memory mapping error\n");
return -ENOMEM;
}
@@ -599,12 +592,14 @@ static int s626_attach(comedi_device * dev, comedi_devconfig * it)
dev->iobase = (unsigned long)devpriv->base_addr;
dev->irq = devpriv->pdev->irq;
- //set up interrupt handler
+ /* set up interrupt handler */
if (dev->irq == 0) {
printk(" unknown irq (bad)\n");
} else {
- if ((ret = comedi_request_irq(dev->irq, s626_irq_handler,
- IRQF_SHARED, "s626", dev)) < 0) {
+ ret = comedi_request_irq(dev->irq, s626_irq_handler,
+ IRQF_SHARED, "s626", dev);
+
+ if (ret < 0) {
printk(" irq not available\n");
dev->irq = 0;
}
@@ -689,119 +684,121 @@ static int s626_attach(comedi_device * dev, comedi_devconfig * it)
s->maxdata = 0xffffff;
s->range_table = &range_unknown;
- //stop ai_command
+ /* stop ai_command */
devpriv->ai_cmd_running = 0;
if (devpriv->base_addr && (devpriv->allocatedBuf == 2)) {
dma_addr_t pPhysBuf;
uint16_t chan;
- // enab DEBI and audio pins, enable I2C interface.
+ /* enab DEBI and audio pins, enable I2C interface. */
MC_ENABLE(P_MC1, MC1_DEBI | MC1_AUDIO | MC1_I2C);
- // Configure DEBI operating mode.
- WR7146(P_DEBICFG, DEBI_CFG_SLAVE16 // Local bus is 16
- // bits wide.
- | (DEBI_TOUT << DEBI_CFG_TOUT_BIT) // Declare DEBI
- // transfer timeout
- // interval.
- | DEBI_SWAP // Set up byte lane
- // steering.
- | DEBI_CFG_INTEL); // Intel-compatible
- // local bus (DEBI
- // never times out).
+ /* Configure DEBI operating mode. */
+ WR7146(P_DEBICFG, DEBI_CFG_SLAVE16 /* Local bus is 16 */
+ /* bits wide. */
+ | (DEBI_TOUT << DEBI_CFG_TOUT_BIT) /* Declare DEBI */
+ /* transfer timeout */
+ /* interval. */
+ | DEBI_SWAP /* Set up byte lane */
+ /* steering. */
+ | DEBI_CFG_INTEL); /* Intel-compatible */
+ /* local bus (DEBI */
+ /* never times out). */
DEBUG("s626_attach: %d debi init -- %d\n",
DEBI_CFG_SLAVE16 | (DEBI_TOUT << DEBI_CFG_TOUT_BIT) |
DEBI_SWAP | DEBI_CFG_INTEL,
DEBI_CFG_INTEL | DEBI_CFG_TOQ | DEBI_CFG_INCQ |
DEBI_CFG_16Q);
- //DEBI INIT S626 WR7146( P_DEBICFG, DEBI_CFG_INTEL | DEBI_CFG_TOQ
- //| DEBI_CFG_INCQ| DEBI_CFG_16Q); //end
+ /* DEBI INIT S626 WR7146( P_DEBICFG, DEBI_CFG_INTEL | DEBI_CFG_TOQ */
+ /* | DEBI_CFG_INCQ| DEBI_CFG_16Q); //end */
- // Paging is disabled.
- WR7146(P_DEBIPAGE, DEBI_PAGE_DISABLE); // Disable MMU paging.
+ /* Paging is disabled. */
+ WR7146(P_DEBIPAGE, DEBI_PAGE_DISABLE); /* Disable MMU paging. */
- // Init GPIO so that ADC Start* is negated.
+ /* Init GPIO so that ADC Start* is negated. */
WR7146(P_GPIO, GPIO_BASE | GPIO1_HI);
- //IsBoardRevA is a boolean that indicates whether the board is
- //RevA.
-
- // VERSION 2.01 CHANGE: REV A & B BOARDS NOW SUPPORTED BY DYNAMIC
- // EEPROM ADDRESS SELECTION. Initialize the I2C interface, which
- // is used to access the onboard serial EEPROM. The EEPROM's I2C
- // DeviceAddress is hardwired to a value that is dependent on the
- // 626 board revision. On all board revisions, the EEPROM stores
- // TrimDAC calibration constants for analog I/O. On RevB and
- // higher boards, the DeviceAddress is hardwired to 0 to enable
- // the EEPROM to also store the PCI SubVendorID and SubDeviceID;
- // this is the address at which the SAA7146 expects a
- // configuration EEPROM to reside. On RevA boards, the EEPROM
- // device address, which is hardwired to 4, prevents the SAA7146
- // from retrieving PCI sub-IDs, so the SAA7146 uses its built-in
- // default values, instead.
-
- // devpriv->I2Cards= IsBoardRevA ? 0xA8 : 0xA0; // Set I2C EEPROM
- // DeviceType (0xA0)
- // and DeviceAddress<<1.
-
- devpriv->I2CAdrs = 0xA0; // I2C device address for onboard
- // eeprom(revb)
-
- // Issue an I2C ABORT command to halt any I2C operation in
- //progress and reset BUSY flag.
- WR7146(P_I2CSTAT, I2C_CLKSEL | I2C_ABORT); // Write I2C control:
- // abort any I2C
- // activity.
- MC_ENABLE(P_MC2, MC2_UPLD_IIC); // Invoke command
- // upload
- while ((RR7146(P_MC2) & MC2_UPLD_IIC) == 0) ; // and wait for
- // upload to
- // complete.
-
- // Per SAA7146 data sheet, write to STATUS reg twice to reset all
- // I2C error flags.
+ /* IsBoardRevA is a boolean that indicates whether the board is RevA.
+ *
+ * VERSION 2.01 CHANGE: REV A & B BOARDS NOW SUPPORTED BY DYNAMIC
+ * EEPROM ADDRESS SELECTION. Initialize the I2C interface, which
+ * is used to access the onboard serial EEPROM. The EEPROM's I2C
+ * DeviceAddress is hardwired to a value that is dependent on the
+ * 626 board revision. On all board revisions, the EEPROM stores
+ * TrimDAC calibration constants for analog I/O. On RevB and
+ * higher boards, the DeviceAddress is hardwired to 0 to enable
+ * the EEPROM to also store the PCI SubVendorID and SubDeviceID;
+ * this is the address at which the SAA7146 expects a
+ * configuration EEPROM to reside. On RevA boards, the EEPROM
+ * device address, which is hardwired to 4, prevents the SAA7146
+ * from retrieving PCI sub-IDs, so the SAA7146 uses its built-in
+ * default values, instead.
+ */
+
+ /* devpriv->I2Cards= IsBoardRevA ? 0xA8 : 0xA0; // Set I2C EEPROM */
+ /* DeviceType (0xA0) */
+ /* and DeviceAddress<<1. */
+
+ devpriv->I2CAdrs = 0xA0; /* I2C device address for onboard */
+ /* eeprom(revb) */
+
+ /* Issue an I2C ABORT command to halt any I2C operation in */
+ /* progress and reset BUSY flag. */
+ WR7146(P_I2CSTAT, I2C_CLKSEL | I2C_ABORT);
+ /* Write I2C control: abort any I2C activity. */
+ MC_ENABLE(P_MC2, MC2_UPLD_IIC);
+ /* Invoke command upload */
+ while ((RR7146(P_MC2) & MC2_UPLD_IIC) == 0)
+ ;
+ /* and wait for upload to complete. */
+
+ /* Per SAA7146 data sheet, write to STATUS reg twice to
+ * reset all I2C error flags. */
for (i = 0; i < 2; i++) {
- WR7146(P_I2CSTAT, I2C_CLKSEL); // Write I2C control: reset
- // error flags.
- MC_ENABLE(P_MC2, MC2_UPLD_IIC); // Invoke command upload
- while (!MC_TEST(P_MC2, MC2_UPLD_IIC)) ; // and wait for
- // upload to
- // complete.
+ WR7146(P_I2CSTAT, I2C_CLKSEL);
+ /* Write I2C control: reset error flags. */
+ MC_ENABLE(P_MC2, MC2_UPLD_IIC); /* Invoke command upload */
+ while (!MC_TEST(P_MC2, MC2_UPLD_IIC))
+ ;
+ /* and wait for upload to complete. */
}
- // Init audio interface functional attributes: set DAC/ADC serial
- // clock rates, invert DAC serial clock so that DAC data setup
- // times are satisfied, enable DAC serial clock out.
+ /* Init audio interface functional attributes: set DAC/ADC
+ * serial clock rates, invert DAC serial clock so that
+ * DAC data setup times are satisfied, enable DAC serial
+ * clock out.
+ */
+
WR7146(P_ACON2, ACON2_INIT);
- // Set up TSL1 slot list, which is used to control the
- // accumulation of ADC data: RSD1 = shift data in on SD1. SIB_A1
- // = store data uint8_t at next available location in FB BUFFER1
- // register.
- WR7146(P_TSL1, RSD1 | SIB_A1); // Fetch ADC high data
- // uint8_t.
- WR7146(P_TSL1 + 4, RSD1 | SIB_A1 | EOS); // Fetch ADC low data
- // uint8_t; end of
- // TSL1.
-
- // enab TSL1 slot list so that it executes all the time.
+ /* Set up TSL1 slot list, which is used to control the
+ * accumulation of ADC data: RSD1 = shift data in on SD1.
+ * SIB_A1 = store data uint8_t at next available location in
+ * FB BUFFER1 register. */
+ WR7146(P_TSL1, RSD1 | SIB_A1);
+ /* Fetch ADC high data uint8_t. */
+ WR7146(P_TSL1 + 4, RSD1 | SIB_A1 | EOS);
+ /* Fetch ADC low data uint8_t; end of TSL1. */
+
+ /* enab TSL1 slot list so that it executes all the time. */
WR7146(P_ACON1, ACON1_ADCSTART);
- // Initialize RPS registers used for ADC.
+ /* Initialize RPS registers used for ADC. */
- //Physical start of RPS program.
+ /* Physical start of RPS program. */
WR7146(P_RPSADDR1, (uint32_t) devpriv->RPSBuf.PhysicalBase);
- WR7146(P_RPSPAGE1, 0); // RPS program performs no
- // explicit mem writes.
- WR7146(P_RPS1_TOUT, 0); // Disable RPS timeouts.
+ WR7146(P_RPSPAGE1, 0);
+ /* RPS program performs no explicit mem writes. */
+ WR7146(P_RPS1_TOUT, 0); /* Disable RPS timeouts. */
- // SAA7146 BUG WORKAROUND. Initialize SAA7146 ADC interface to a
- // known state by invoking ADCs until FB BUFFER 1 register shows
- // that it is correctly receiving ADC data. This is necessary
- // because the SAA7146 ADC interface does not start up in a
- // defined state after a PCI reset.
+ /* SAA7146 BUG WORKAROUND. Initialize SAA7146 ADC interface
+ * to a known state by invoking ADCs until FB BUFFER 1
+ * register shows that it is correctly receiving ADC data.
+ * This is necessary because the SAA7146 ADC interface does
+ * not start up in a defined state after a PCI reset.
+ */
/* PollList = EOPL; // Create a simple polling */
/* // list for analog input */
@@ -829,115 +826,123 @@ static int s626_attach(comedi_device * dev, comedi_devconfig * it)
/* break; */
/* } */
- // end initADC
+ /* end initADC */
- // init the DAC interface
+ /* init the DAC interface */
- // Init Audio2's output DMAC attributes: burst length = 1 DWORD,
- // threshold = 1 DWORD.
+ /* Init Audio2's output DMAC attributes: burst length = 1
+ * DWORD, threshold = 1 DWORD.
+ */
WR7146(P_PCI_BT_A, 0);
- // Init Audio2's output DMA physical addresses. The protection
- // address is set to 1 DWORD past the base address so that a
- // single DWORD will be transferred each time a DMA transfer is
- // enabled.
+ /* Init Audio2's output DMA physical addresses. The protection
+ * address is set to 1 DWORD past the base address so that a
+ * single DWORD will be transferred each time a DMA transfer is
+ * enabled. */
pPhysBuf =
devpriv->ANABuf.PhysicalBase +
(DAC_WDMABUF_OS * sizeof(uint32_t));
- WR7146(P_BASEA2_OUT, (uint32_t) pPhysBuf); // Buffer base adrs.
- WR7146(P_PROTA2_OUT, (uint32_t) (pPhysBuf + sizeof(uint32_t))); // Protection address.
+ WR7146(P_BASEA2_OUT, (uint32_t) pPhysBuf); /* Buffer base adrs. */
+ WR7146(P_PROTA2_OUT, (uint32_t) (pPhysBuf + sizeof(uint32_t))); /* Protection address. */
- // Cache Audio2's output DMA buffer logical address. This is
- // where DAC data is buffered for A2 output DMA transfers.
+ /* Cache Audio2's output DMA buffer logical address. This is
+ * where DAC data is buffered for A2 output DMA transfers. */
devpriv->pDacWBuf =
(uint32_t *) devpriv->ANABuf.LogicalBase +
DAC_WDMABUF_OS;
- // Audio2's output channels does not use paging. The protection
- // violation handling bit is set so that the DMAC will
- // automatically halt and its PCI address pointer will be reset
- // when the protection address is reached.
+ /* Audio2's output channels does not use paging. The protection
+ * violation handling bit is set so that the DMAC will
+ * automatically halt and its PCI address pointer will be reset
+ * when the protection address is reached. */
+
WR7146(P_PAGEA2_OUT, 8);
- // Initialize time slot list 2 (TSL2), which is used to control
- // the clock generation for and serialization of data to be sent
- // to the DAC devices. Slot 0 is a NOP that is used to trap TSL
- // execution; this permits other slots to be safely modified
- // without first turning off the TSL sequencer (which is
- // apparently impossible to do). Also, SD3 (which is driven by a
- // pull-up resistor) is shifted in and stored to the MSB of
- // FB_BUFFER2 to be used as evidence that the slot sequence has
- // not yet finished executing.
- SETVECT(0, XSD2 | RSD3 | SIB_A2 | EOS); // Slot 0: Trap TSL
- // execution, shift 0xFF
- // into FB_BUFFER2.
-
- // Initialize slot 1, which is constant. Slot 1 causes a DWORD to
- // be transferred from audio channel 2's output FIFO to the FIFO's
- // output buffer so that it can be serialized and sent to the DAC
- // during subsequent slots. All remaining slots are dynamically
- // populated as required by the target DAC device.
- SETVECT(1, LF_A2); // Slot 1: Fetch DWORD from Audio2's
- // output FIFO.
-
- // Start DAC's audio interface (TSL2) running.
+ /* Initialize time slot list 2 (TSL2), which is used to control
+ * the clock generation for and serialization of data to be sent
+ * to the DAC devices. Slot 0 is a NOP that is used to trap TSL
+ * execution; this permits other slots to be safely modified
+ * without first turning off the TSL sequencer (which is
+ * apparently impossible to do). Also, SD3 (which is driven by a
+ * pull-up resistor) is shifted in and stored to the MSB of
+ * FB_BUFFER2 to be used as evidence that the slot sequence has
+ * not yet finished executing.
+ */
+
+ SETVECT(0, XSD2 | RSD3 | SIB_A2 | EOS);
+ /* Slot 0: Trap TSL execution, shift 0xFF into FB_BUFFER2. */
+
+ /* Initialize slot 1, which is constant. Slot 1 causes a
+ * DWORD to be transferred from audio channel 2's output FIFO
+ * to the FIFO's output buffer so that it can be serialized
+ * and sent to the DAC during subsequent slots. All remaining
+ * slots are dynamically populated as required by the target
+ * DAC device.
+ */
+ SETVECT(1, LF_A2);
+ /* Slot 1: Fetch DWORD from Audio2's output FIFO. */
+
+ /* Start DAC's audio interface (TSL2) running. */
WR7146(P_ACON1, ACON1_DACSTART);
- ////////////////////////////////////////////////////////
+ /* end init DAC interface */
- // end init DAC interface
-
- // Init Trim DACs to calibrated values. Do it twice because the
- // SAA7146 audio channel does not always reset properly and
- // sometimes causes the first few TrimDAC writes to malfunction.
+ /* Init Trim DACs to calibrated values. Do it twice because the
+ * SAA7146 audio channel does not always reset properly and
+ * sometimes causes the first few TrimDAC writes to malfunction.
+ */
LoadTrimDACs(dev);
- LoadTrimDACs(dev); // Insurance.
+ LoadTrimDACs(dev); /* Insurance. */
- //////////////////////////////////////////////////////////////////
- // Manually init all gate array hardware in case this is a soft
- // reset (we have no way of determining whether this is a warm or
- // cold start). This is necessary because the gate array will
- // reset only in response to a PCI hard reset; there is no soft
- // reset function.
+ /* Manually init all gate array hardware in case this is a soft
+ * reset (we have no way of determining whether this is a warm
+ * or cold start). This is necessary because the gate array will
+ * reset only in response to a PCI hard reset; there is no soft
+ * reset function. */
- // Init all DAC outputs to 0V and init all DAC setpoint and
- // polarity images.
+ /* Init all DAC outputs to 0V and init all DAC setpoint and
+ * polarity images.
+ */
for (chan = 0; chan < S626_DAC_CHANNELS; chan++)
SetDAC(dev, chan, 0);
- // Init image of WRMISC2 Battery Charger Enabled control bit.
- // This image is used when the state of the charger control bit,
- // which has no direct hardware readback mechanism, is queried.
+ /* Init image of WRMISC2 Battery Charger Enabled control bit.
+ * This image is used when the state of the charger control bit,
+ * which has no direct hardware readback mechanism, is queried.
+ */
devpriv->ChargeEnabled = 0;
- // Init image of watchdog timer interval in WRMISC2. This image
- // maintains the value of the control bits of MISC2 are
- // continuously reset to zero as long as the WD timer is disabled.
+ /* Init image of watchdog timer interval in WRMISC2. This image
+ * maintains the value of the control bits of MISC2 are
+ * continuously reset to zero as long as the WD timer is disabled.
+ */
devpriv->WDInterval = 0;
- // Init Counter Interrupt enab mask for RDMISC2. This mask is
- // applied against MISC2 when testing to determine which timer
- // events are requesting interrupt service.
+ /* Init Counter Interrupt enab mask for RDMISC2. This mask is
+ * applied against MISC2 when testing to determine which timer
+ * events are requesting interrupt service.
+ */
devpriv->CounterIntEnabs = 0;
- // Init counters.
+ /* Init counters. */
CountersInit(dev);
- // Without modifying the state of the Battery Backup enab, disable
- // the watchdog timer, set DIO channels 0-5 to operate in the
- // standard DIO (vs. counter overflow) mode, disable the battery
- // charger, and reset the watchdog interval selector to zero.
+ /* Without modifying the state of the Battery Backup enab, disable
+ * the watchdog timer, set DIO channels 0-5 to operate in the
+ * standard DIO (vs. counter overflow) mode, disable the battery
+ * charger, and reset the watchdog interval selector to zero.
+ */
WriteMISC2(dev, (uint16_t) (DEBIread(dev,
LP_RDMISC2) & MISC2_BATT_ENABLE));
- // Initialize the digital I/O subsystem.
+ /* Initialize the digital I/O subsystem. */
s626_dio_init(dev);
- //enable interrupt test
- // writel(IRQ_GPIO3 | IRQ_RPS1,devpriv->base_addr+P_IER);
+ /* enable interrupt test */
+ /* writel(IRQ_GPIO3 | IRQ_RPS1,devpriv->base_addr+P_IER); */
}
DEBUG("s626_attach: comedi%d s626 attached %04x\n", dev->minor,
@@ -946,9 +951,9 @@ static int s626_attach(comedi_device * dev, comedi_devconfig * it)
return 1;
}
-static lsampl_t s626_ai_reg_to_uint(int data)
+static unsigned int s626_ai_reg_to_uint(int data)
{
- lsampl_t tempdata;
+ unsigned int tempdata;
tempdata = (data >> 18);
if (tempdata & 0x2000)
@@ -959,21 +964,21 @@ static lsampl_t s626_ai_reg_to_uint(int data)
return tempdata;
}
-/* static lsampl_t s626_uint_to_reg(comedi_subdevice *s, int data){ */
+/* static unsigned int s626_uint_to_reg(struct comedi_subdevice *s, int data){ */
/* return 0; */
/* } */
static irqreturn_t s626_irq_handler(int irq, void *d PT_REGS_ARG)
{
- comedi_device *dev = d;
- comedi_subdevice *s;
- comedi_cmd *cmd;
- enc_private *k;
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s;
+ struct comedi_cmd *cmd;
+ struct enc_private *k;
unsigned long flags;
int32_t *readaddr;
uint32_t irqtype, irqstatus;
int i = 0;
- sampl_t tempdata;
+ short tempdata;
uint8_t group;
uint16_t irqbit;
@@ -981,47 +986,48 @@ static irqreturn_t s626_irq_handler(int irq, void *d PT_REGS_ARG)
if (dev->attached == 0)
return IRQ_NONE;
- // lock to avoid race with comedi_poll
+ /* lock to avoid race with comedi_poll */
comedi_spin_lock_irqsave(&dev->spinlock, flags);
- //save interrupt enable register state
+ /* save interrupt enable register state */
irqstatus = readl(devpriv->base_addr + P_IER);
- //read interrupt type
+ /* read interrupt type */
irqtype = readl(devpriv->base_addr + P_ISR);
- //disable master interrupt
+ /* disable master interrupt */
writel(0, devpriv->base_addr + P_IER);
- //clear interrupt
+ /* clear interrupt */
writel(irqtype, devpriv->base_addr + P_ISR);
- //do somethings
+ /* do somethings */
DEBUG("s626_irq_handler: interrupt type %d\n", irqtype);
switch (irqtype) {
- case IRQ_RPS1: // end_of_scan occurs
+ case IRQ_RPS1: /* end_of_scan occurs */
DEBUG("s626_irq_handler: RPS1 irq detected\n");
- // manage ai subdevice
+ /* manage ai subdevice */
s = dev->subdevices;
cmd = &(s->async->cmd);
- // Init ptr to DMA buffer that holds new ADC data. We skip the
- // first uint16_t in the buffer because it contains junk data from
- // the final ADC of the previous poll list scan.
+ /* Init ptr to DMA buffer that holds new ADC data. We skip the
+ * first uint16_t in the buffer because it contains junk data from
+ * the final ADC of the previous poll list scan.
+ */
readaddr = (int32_t *) devpriv->ANABuf.LogicalBase + 1;
- // get the data and hand it over to comedi
+ /* get the data and hand it over to comedi */
for (i = 0; i < (s->async->cmd.chanlist_len); i++) {
- // Convert ADC data to 16-bit integer values and copy to application
- // buffer.
+ /* Convert ADC data to 16-bit integer values and copy to application */
+ /* buffer. */
tempdata = s626_ai_reg_to_uint((int)*readaddr);
readaddr++;
- //put data into read buffer
- // comedi_buf_put(s->async, tempdata);
+ /* put data into read buffer */
+ /* comedi_buf_put(s->async, tempdata); */
if (cfc_write_to_buffer(s, tempdata) == 0)
printk("s626_irq_handler: cfc_write_to_buffer error!\n");
@@ -1029,7 +1035,7 @@ static irqreturn_t s626_irq_handler(int irq, void *d PT_REGS_ARG)
i, tempdata);
}
- //end of scan occurs
+ /* end of scan occurs */
s->async->events |= COMEDI_CB_EOS;
if (!(devpriv->ai_continous))
@@ -1037,13 +1043,13 @@ static irqreturn_t s626_irq_handler(int irq, void *d PT_REGS_ARG)
if (devpriv->ai_sample_count <= 0) {
devpriv->ai_cmd_running = 0;
- // Stop RPS program.
+ /* Stop RPS program. */
MC_DISABLE(P_MC1, MC1_ERPS1);
- //send end of acquisition
+ /* send end of acquisition */
s->async->events |= COMEDI_CB_EOA;
- //disable master interrupt
+ /* disable master interrupt */
irqstatus = 0;
}
@@ -1054,40 +1060,40 @@ static irqreturn_t s626_irq_handler(int irq, void *d PT_REGS_ARG)
DEBUG("s626_irq_handler: External trigger is set!!!\n");
}
- // tell comedi that data is there
+ /* tell comedi that data is there */
DEBUG("s626_irq_handler: events %d\n", s->async->events);
comedi_event(dev, s);
break;
- case IRQ_GPIO3: //check dio and conter interrupt
+ case IRQ_GPIO3: /* check dio and conter interrupt */
DEBUG("s626_irq_handler: GPIO3 irq detected\n");
- // manage ai subdevice
+ /* manage ai subdevice */
s = dev->subdevices;
cmd = &(s->async->cmd);
- //s626_dio_clear_irq(dev);
+ /* s626_dio_clear_irq(dev); */
for (group = 0; group < S626_DIO_BANKS; group++) {
irqbit = 0;
- //read interrupt type
+ /* read interrupt type */
irqbit = DEBIread(dev,
- ((dio_private *) (dev->subdevices + 2 +
+ ((struct dio_private *) (dev->subdevices + 2 +
group)->private)->RDCapFlg);
- //check if interrupt is generated from dio channels
+ /* check if interrupt is generated from dio channels */
if (irqbit) {
s626_dio_reset_irq(dev, group, irqbit);
DEBUG("s626_irq_handler: check interrupt on dio group %d %d\n", group, i);
if (devpriv->ai_cmd_running) {
- //check if interrupt is an ai acquisition start trigger
+ /* check if interrupt is an ai acquisition start trigger */
if ((irqbit >> (cmd->start_arg -
(16 * group)))
== 1
&& cmd->start_src == TRIG_EXT) {
DEBUG("s626_irq_handler: Edge capture interrupt recieved from channel %d\n", cmd->start_arg);
- // Start executing the RPS program.
+ /* Start executing the RPS program. */
MC_ENABLE(P_MC1, MC1_ERPS1);
DEBUG("s626_irq_handler: aquisition start triggered!!!\n");
@@ -1110,7 +1116,7 @@ static irqreturn_t s626_irq_handler(int irq, void *d PT_REGS_ARG)
TRIG_EXT) {
DEBUG("s626_irq_handler: Edge capture interrupt recieved from channel %d\n", cmd->scan_begin_arg);
- // Trigger ADC scan loop start by setting RPS Signal 0.
+ /* Trigger ADC scan loop start by setting RPS Signal 0. */
MC_ENABLE(P_MC2, MC2_ADC_RPS);
DEBUG("s626_irq_handler: scan triggered!!! %d\n", devpriv->ai_sample_count);
@@ -1151,7 +1157,7 @@ static irqreturn_t s626_irq_handler(int irq, void *d PT_REGS_ARG)
TRIG_EXT) {
DEBUG("s626_irq_handler: Edge capture interrupt recieved from channel %d\n", cmd->convert_arg);
- // Trigger ADC scan loop start by setting RPS Signal 0.
+ /* Trigger ADC scan loop start by setting RPS Signal 0. */
MC_ENABLE(P_MC2, MC2_ADC_RPS);
DEBUG("s626_irq_handler: adc convert triggered!!!\n");
@@ -1175,10 +1181,10 @@ static irqreturn_t s626_irq_handler(int irq, void *d PT_REGS_ARG)
}
}
- //read interrupt type
+ /* read interrupt type */
irqbit = DEBIread(dev, LP_RDMISC2);
- //check interrupt on counters
+ /* check interrupt on counters */
DEBUG("s626_irq_handler: check counters interrupt %d\n",
irqbit);
@@ -1186,35 +1192,35 @@ static irqreturn_t s626_irq_handler(int irq, void *d PT_REGS_ARG)
DEBUG("s626_irq_handler: interrupt on counter 1A overflow\n");
k = &encpriv[0];
- //clear interrupt capture flag
+ /* clear interrupt capture flag */
k->ResetCapFlags(dev, k);
}
if (irqbit & IRQ_COINT2A) {
DEBUG("s626_irq_handler: interrupt on counter 2A overflow\n");
k = &encpriv[1];
- //clear interrupt capture flag
+ /* clear interrupt capture flag */
k->ResetCapFlags(dev, k);
}
if (irqbit & IRQ_COINT3A) {
DEBUG("s626_irq_handler: interrupt on counter 3A overflow\n");
k = &encpriv[2];
- //clear interrupt capture flag
+ /* clear interrupt capture flag */
k->ResetCapFlags(dev, k);
}
if (irqbit & IRQ_COINT1B) {
DEBUG("s626_irq_handler: interrupt on counter 1B overflow\n");
k = &encpriv[3];
- //clear interrupt capture flag
+ /* clear interrupt capture flag */
k->ResetCapFlags(dev, k);
}
if (irqbit & IRQ_COINT2B) {
DEBUG("s626_irq_handler: interrupt on counter 2B overflow\n");
k = &encpriv[4];
- //clear interrupt capture flag
+ /* clear interrupt capture flag */
k->ResetCapFlags(dev, k);
if (devpriv->ai_convert_count > 0) {
@@ -1225,7 +1231,7 @@ static irqreturn_t s626_irq_handler(int irq, void *d PT_REGS_ARG)
if (cmd->convert_src == TRIG_TIMER) {
DEBUG("s626_irq_handler: conver timer trigger!!! %d\n", devpriv->ai_convert_count);
- // Trigger ADC scan loop start by setting RPS Signal 0.
+ /* Trigger ADC scan loop start by setting RPS Signal 0. */
MC_ENABLE(P_MC2, MC2_ADC_RPS);
}
}
@@ -1234,13 +1240,13 @@ static irqreturn_t s626_irq_handler(int irq, void *d PT_REGS_ARG)
DEBUG("s626_irq_handler: interrupt on counter 3B overflow\n");
k = &encpriv[5];
- //clear interrupt capture flag
+ /* clear interrupt capture flag */
k->ResetCapFlags(dev, k);
if (cmd->scan_begin_src == TRIG_TIMER) {
DEBUG("s626_irq_handler: scan timer trigger!!!\n");
- // Trigger ADC scan loop start by setting RPS Signal 0.
+ /* Trigger ADC scan loop start by setting RPS Signal 0. */
MC_ENABLE(P_MC2, MC2_ADC_RPS);
}
@@ -1253,7 +1259,7 @@ static irqreturn_t s626_irq_handler(int irq, void *d PT_REGS_ARG)
}
}
- //enable interrupt
+ /* enable interrupt */
writel(irqstatus, devpriv->base_addr + P_IER);
DEBUG("s626_irq_handler: exit interrupt service routine.\n");
@@ -1262,21 +1268,21 @@ static irqreturn_t s626_irq_handler(int irq, void *d PT_REGS_ARG)
return IRQ_HANDLED;
}
-static int s626_detach(comedi_device * dev)
+static int s626_detach(struct comedi_device *dev)
{
if (devpriv) {
- //stop ai_command
+ /* stop ai_command */
devpriv->ai_cmd_running = 0;
if (devpriv->base_addr) {
- //interrupt mask
- WR7146(P_IER, 0); // Disable master interrupt.
- WR7146(P_ISR, IRQ_GPIO3 | IRQ_RPS1); // Clear board's IRQ status flag.
+ /* interrupt mask */
+ WR7146(P_IER, 0); /* Disable master interrupt. */
+ WR7146(P_ISR, IRQ_GPIO3 | IRQ_RPS1); /* Clear board's IRQ status flag. */
- // Disable the watchdog timer and battery charger.
+ /* Disable the watchdog timer and battery charger. */
WriteMISC2(dev, 0);
- // Close all interfaces on 7146 device.
+ /* Close all interfaces on 7146 device. */
WR7146(P_MC1, MC1_SHUTDOWN);
WR7146(P_ACON1, ACON1_BASE);
@@ -1284,18 +1290,15 @@ static int s626_detach(comedi_device * dev)
CloseDMAB(dev, &devpriv->ANABuf, DMABUF_SIZE);
}
- if (dev->irq) {
+ if (dev->irq)
comedi_free_irq(dev->irq, dev);
- }
- if (devpriv->base_addr) {
+ if (devpriv->base_addr)
iounmap(devpriv->base_addr);
- }
if (devpriv->pdev) {
- if (devpriv->got_regions) {
+ if (devpriv->got_regions)
comedi_pci_disable(devpriv->pdev);
- }
pci_dev_put(devpriv->pdev);
}
}
@@ -1308,213 +1311,225 @@ static int s626_detach(comedi_device * dev)
/*
* this functions build the RPS program for hardware driven acquistion
*/
-void ResetADC(comedi_device * dev, uint8_t * ppl)
+void ResetADC(struct comedi_device *dev, uint8_t *ppl)
{
register uint32_t *pRPS;
uint32_t JmpAdrs;
uint16_t i;
uint16_t n;
uint32_t LocalPPL;
- comedi_cmd *cmd = &(dev->subdevices->async->cmd);
+ struct comedi_cmd *cmd = &(dev->subdevices->async->cmd);
- // Stop RPS program in case it is currently running.
+ /* Stop RPS program in case it is currently running. */
MC_DISABLE(P_MC1, MC1_ERPS1);
- // Set starting logical address to write RPS commands.
+ /* Set starting logical address to write RPS commands. */
pRPS = (uint32_t *) devpriv->RPSBuf.LogicalBase;
- // Initialize RPS instruction pointer.
+ /* Initialize RPS instruction pointer. */
WR7146(P_RPSADDR1, (uint32_t) devpriv->RPSBuf.PhysicalBase);
- // Construct RPS program in RPSBuf DMA buffer
+ /* Construct RPS program in RPSBuf DMA buffer */
if (cmd != NULL && cmd->scan_begin_src != TRIG_FOLLOW) {
DEBUG("ResetADC: scan_begin pause inserted\n");
- // Wait for Start trigger.
+ /* Wait for Start trigger. */
*pRPS++ = RPS_PAUSE | RPS_SIGADC;
*pRPS++ = RPS_CLRSIGNAL | RPS_SIGADC;
}
- // SAA7146 BUG WORKAROUND Do a dummy DEBI Write. This is necessary
- // because the first RPS DEBI Write following a non-RPS DEBI write
- // seems to always fail. If we don't do this dummy write, the ADC
- // gain might not be set to the value required for the first slot in
- // the poll list; the ADC gain would instead remain unchanged from
- // the previously programmed value.
- *pRPS++ = RPS_LDREG | (P_DEBICMD >> 2); // Write DEBI Write command
- // and address to shadow RAM.
+
+ /* SAA7146 BUG WORKAROUND Do a dummy DEBI Write. This is necessary
+ * because the first RPS DEBI Write following a non-RPS DEBI write
+ * seems to always fail. If we don't do this dummy write, the ADC
+ * gain might not be set to the value required for the first slot in
+ * the poll list; the ADC gain would instead remain unchanged from
+ * the previously programmed value.
+ */
+ *pRPS++ = RPS_LDREG | (P_DEBICMD >> 2);
+ /* Write DEBI Write command and address to shadow RAM. */
+
*pRPS++ = DEBI_CMD_WRWORD | LP_GSEL;
- *pRPS++ = RPS_LDREG | (P_DEBIAD >> 2); // Write DEBI immediate data
- // to shadow RAM:
- *pRPS++ = GSEL_BIPOLAR5V; // arbitrary immediate data
- // value.
- *pRPS++ = RPS_CLRSIGNAL | RPS_DEBI; // Reset "shadow RAM
- // uploaded" flag.
- *pRPS++ = RPS_UPLOAD | RPS_DEBI; // Invoke shadow RAM upload.
- *pRPS++ = RPS_PAUSE | RPS_DEBI; // Wait for shadow upload to finish.
-
- // Digitize all slots in the poll list. This is implemented as a
- // for loop to limit the slot count to 16 in case the application
- // forgot to set the EOPL flag in the final slot.
+ *pRPS++ = RPS_LDREG | (P_DEBIAD >> 2);
+ /* Write DEBI immediate data to shadow RAM: */
+
+ *pRPS++ = GSEL_BIPOLAR5V;
+ /* arbitrary immediate data value. */
+
+ *pRPS++ = RPS_CLRSIGNAL | RPS_DEBI;
+ /* Reset "shadow RAM uploaded" flag. */
+ *pRPS++ = RPS_UPLOAD | RPS_DEBI; /* Invoke shadow RAM upload. */
+ *pRPS++ = RPS_PAUSE | RPS_DEBI; /* Wait for shadow upload to finish. */
+
+ /* Digitize all slots in the poll list. This is implemented as a
+ * for loop to limit the slot count to 16 in case the application
+ * forgot to set the EOPL flag in the final slot.
+ */
for (devpriv->AdcItems = 0; devpriv->AdcItems < 16; devpriv->AdcItems++) {
- // Convert application's poll list item to private board class
- // format. Each app poll list item is an uint8_t with form
- // (EOPL,x,x,RANGE,CHAN<3:0>), where RANGE code indicates 0 =
- // +-10V, 1 = +-5V, and EOPL = End of Poll List marker.
+ /* Convert application's poll list item to private board class
+ * format. Each app poll list item is an uint8_t with form
+ * (EOPL,x,x,RANGE,CHAN<3:0>), where RANGE code indicates 0 =
+ * +-10V, 1 = +-5V, and EOPL = End of Poll List marker.
+ */
LocalPPL =
(*ppl << 8) | (*ppl & 0x10 ? GSEL_BIPOLAR5V :
GSEL_BIPOLAR10V);
- // Switch ADC analog gain.
- *pRPS++ = RPS_LDREG | (P_DEBICMD >> 2); // Write DEBI command
- // and address to
- // shadow RAM.
+ /* Switch ADC analog gain. */
+ *pRPS++ = RPS_LDREG | (P_DEBICMD >> 2); /* Write DEBI command */
+ /* and address to */
+ /* shadow RAM. */
*pRPS++ = DEBI_CMD_WRWORD | LP_GSEL;
- *pRPS++ = RPS_LDREG | (P_DEBIAD >> 2); // Write DEBI
- // immediate data to
- // shadow RAM.
+ *pRPS++ = RPS_LDREG | (P_DEBIAD >> 2); /* Write DEBI */
+ /* immediate data to */
+ /* shadow RAM. */
*pRPS++ = LocalPPL;
- *pRPS++ = RPS_CLRSIGNAL | RPS_DEBI; // Reset "shadow RAM uploaded"
- // flag.
- *pRPS++ = RPS_UPLOAD | RPS_DEBI; // Invoke shadow RAM upload.
- *pRPS++ = RPS_PAUSE | RPS_DEBI; // Wait for shadow upload to
- // finish.
-
- // Select ADC analog input channel.
- *pRPS++ = RPS_LDREG | (P_DEBICMD >> 2); // Write DEBI command
- // and address to
- // shadow RAM.
+ *pRPS++ = RPS_CLRSIGNAL | RPS_DEBI; /* Reset "shadow RAM uploaded" */
+ /* flag. */
+ *pRPS++ = RPS_UPLOAD | RPS_DEBI; /* Invoke shadow RAM upload. */
+ *pRPS++ = RPS_PAUSE | RPS_DEBI; /* Wait for shadow upload to */
+ /* finish. */
+
+ /* Select ADC analog input channel. */
+ *pRPS++ = RPS_LDREG | (P_DEBICMD >> 2);
+ /* Write DEBI command and address to shadow RAM. */
*pRPS++ = DEBI_CMD_WRWORD | LP_ISEL;
- *pRPS++ = RPS_LDREG | (P_DEBIAD >> 2); // Write DEBI
- // immediate data to
- // shadow RAM.
+ *pRPS++ = RPS_LDREG | (P_DEBIAD >> 2);
+ /* Write DEBI immediate data to shadow RAM. */
*pRPS++ = LocalPPL;
- *pRPS++ = RPS_CLRSIGNAL | RPS_DEBI; // Reset "shadow RAM uploaded"
- // flag.
- *pRPS++ = RPS_UPLOAD | RPS_DEBI; // Invoke shadow RAM upload.
- *pRPS++ = RPS_PAUSE | RPS_DEBI; // Wait for shadow upload to
- // finish.
-
- // Delay at least 10 microseconds for analog input settling.
- // Instead of padding with NOPs, we use RPS_JUMP instructions
- // here; this allows us to produce a longer delay than is
- // possible with NOPs because each RPS_JUMP flushes the RPS'
- // instruction prefetch pipeline.
+ *pRPS++ = RPS_CLRSIGNAL | RPS_DEBI;
+ /* Reset "shadow RAM uploaded" flag. */
+
+ *pRPS++ = RPS_UPLOAD | RPS_DEBI;
+ /* Invoke shadow RAM upload. */
+
+ *pRPS++ = RPS_PAUSE | RPS_DEBI;
+ /* Wait for shadow upload to finish. */
+
+ /* Delay at least 10 microseconds for analog input settling.
+ * Instead of padding with NOPs, we use RPS_JUMP instructions
+ * here; this allows us to produce a longer delay than is
+ * possible with NOPs because each RPS_JUMP flushes the RPS'
+ * instruction prefetch pipeline.
+ */
JmpAdrs =
(uint32_t) devpriv->RPSBuf.PhysicalBase +
(uint32_t) ((unsigned long)pRPS -
(unsigned long)devpriv->RPSBuf.LogicalBase);
for (i = 0; i < (10 * RPSCLK_PER_US / 2); i++) {
- JmpAdrs += 8; // Repeat to implement time delay:
- *pRPS++ = RPS_JUMP; // Jump to next RPS instruction.
+ JmpAdrs += 8; /* Repeat to implement time delay: */
+ *pRPS++ = RPS_JUMP; /* Jump to next RPS instruction. */
*pRPS++ = JmpAdrs;
}
if (cmd != NULL && cmd->convert_src != TRIG_NOW) {
DEBUG("ResetADC: convert pause inserted\n");
- // Wait for Start trigger.
+ /* Wait for Start trigger. */
*pRPS++ = RPS_PAUSE | RPS_SIGADC;
*pRPS++ = RPS_CLRSIGNAL | RPS_SIGADC;
}
- // Start ADC by pulsing GPIO1.
- *pRPS++ = RPS_LDREG | (P_GPIO >> 2); // Begin ADC Start pulse.
+ /* Start ADC by pulsing GPIO1. */
+ *pRPS++ = RPS_LDREG | (P_GPIO >> 2); /* Begin ADC Start pulse. */
*pRPS++ = GPIO_BASE | GPIO1_LO;
*pRPS++ = RPS_NOP;
- // VERSION 2.03 CHANGE: STRETCH OUT ADC START PULSE.
- *pRPS++ = RPS_LDREG | (P_GPIO >> 2); // End ADC Start pulse.
+ /* VERSION 2.03 CHANGE: STRETCH OUT ADC START PULSE. */
+ *pRPS++ = RPS_LDREG | (P_GPIO >> 2); /* End ADC Start pulse. */
*pRPS++ = GPIO_BASE | GPIO1_HI;
- // Wait for ADC to complete (GPIO2 is asserted high when ADC not
- // busy) and for data from previous conversion to shift into FB
- // BUFFER 1 register.
- *pRPS++ = RPS_PAUSE | RPS_GPIO2; // Wait for ADC done.
+ /* Wait for ADC to complete (GPIO2 is asserted high when ADC not
+ * busy) and for data from previous conversion to shift into FB
+ * BUFFER 1 register.
+ */
+ *pRPS++ = RPS_PAUSE | RPS_GPIO2; /* Wait for ADC done. */
- // Transfer ADC data from FB BUFFER 1 register to DMA buffer.
+ /* Transfer ADC data from FB BUFFER 1 register to DMA buffer. */
*pRPS++ = RPS_STREG | (BUGFIX_STREG(P_FB_BUFFER1) >> 2);
*pRPS++ =
(uint32_t) devpriv->ANABuf.PhysicalBase +
(devpriv->AdcItems << 2);
- // If this slot's EndOfPollList flag is set, all channels have
- // now been processed.
+ /* If this slot's EndOfPollList flag is set, all channels have */
+ /* now been processed. */
if (*ppl++ & EOPL) {
- devpriv->AdcItems++; // Adjust poll list item count.
- break; // Exit poll list processing loop.
+ devpriv->AdcItems++; /* Adjust poll list item count. */
+ break; /* Exit poll list processing loop. */
}
}
DEBUG("ResetADC: ADC items %d \n", devpriv->AdcItems);
- // VERSION 2.01 CHANGE: DELAY CHANGED FROM 250NS to 2US. Allow the
- // ADC to stabilize for 2 microseconds before starting the final
- // (dummy) conversion. This delay is necessary to allow sufficient
- // time between last conversion finished and the start of the dummy
- // conversion. Without this delay, the last conversion's data value
- // is sometimes set to the previous conversion's data value.
+ /* VERSION 2.01 CHANGE: DELAY CHANGED FROM 250NS to 2US. Allow the
+ * ADC to stabilize for 2 microseconds before starting the final
+ * (dummy) conversion. This delay is necessary to allow sufficient
+ * time between last conversion finished and the start of the dummy
+ * conversion. Without this delay, the last conversion's data value
+ * is sometimes set to the previous conversion's data value.
+ */
for (n = 0; n < (2 * RPSCLK_PER_US); n++)
*pRPS++ = RPS_NOP;
- // Start a dummy conversion to cause the data from the last
- // conversion of interest to be shifted in.
- *pRPS++ = RPS_LDREG | (P_GPIO >> 2); // Begin ADC Start pulse.
+ /* Start a dummy conversion to cause the data from the last
+ * conversion of interest to be shifted in.
+ */
+ *pRPS++ = RPS_LDREG | (P_GPIO >> 2); /* Begin ADC Start pulse. */
*pRPS++ = GPIO_BASE | GPIO1_LO;
*pRPS++ = RPS_NOP;
- // VERSION 2.03 CHANGE: STRETCH OUT ADC START PULSE.
- *pRPS++ = RPS_LDREG | (P_GPIO >> 2); // End ADC Start pulse.
+ /* VERSION 2.03 CHANGE: STRETCH OUT ADC START PULSE. */
+ *pRPS++ = RPS_LDREG | (P_GPIO >> 2); /* End ADC Start pulse. */
*pRPS++ = GPIO_BASE | GPIO1_HI;
- // Wait for the data from the last conversion of interest to arrive
- // in FB BUFFER 1 register.
- *pRPS++ = RPS_PAUSE | RPS_GPIO2; // Wait for ADC done.
+ /* Wait for the data from the last conversion of interest to arrive
+ * in FB BUFFER 1 register.
+ */
+ *pRPS++ = RPS_PAUSE | RPS_GPIO2; /* Wait for ADC done. */
- // Transfer final ADC data from FB BUFFER 1 register to DMA buffer.
- *pRPS++ = RPS_STREG | (BUGFIX_STREG(P_FB_BUFFER1) >> 2); //
+ /* Transfer final ADC data from FB BUFFER 1 register to DMA buffer. */
+ *pRPS++ = RPS_STREG | (BUGFIX_STREG(P_FB_BUFFER1) >> 2); /* */
*pRPS++ =
(uint32_t) devpriv->ANABuf.PhysicalBase +
(devpriv->AdcItems << 2);
- // Indicate ADC scan loop is finished.
- // *pRPS++= RPS_CLRSIGNAL | RPS_SIGADC ; // Signal ReadADC() that scan is done.
+ /* Indicate ADC scan loop is finished. */
+ /* *pRPS++= RPS_CLRSIGNAL | RPS_SIGADC ; // Signal ReadADC() that scan is done. */
- //invoke interrupt
+ /* invoke interrupt */
if (devpriv->ai_cmd_running == 1) {
DEBUG("ResetADC: insert irq in ADC RPS task\n");
*pRPS++ = RPS_IRQ;
}
- // Restart RPS program at its beginning.
- *pRPS++ = RPS_JUMP; // Branch to start of RPS program.
+ /* Restart RPS program at its beginning. */
+ *pRPS++ = RPS_JUMP; /* Branch to start of RPS program. */
*pRPS++ = (uint32_t) devpriv->RPSBuf.PhysicalBase;
- // End of RPS program build
- // ------------------------------------------------------------
+ /* End of RPS program build */
}
/* TO COMPLETE, IF NECESSARY */
-static int s626_ai_insn_config(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int s626_ai_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
return -EINVAL;
}
-/* static int s626_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) */
+/* static int s626_ai_rinsn(struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) */
/* { */
/* register uint8_t i; */
/* register int32_t *readaddr; */
/* DEBUG("as626_ai_rinsn: ai_rinsn enter \n"); */
-/* // Trigger ADC scan loop start by setting RPS Signal 0. */
+/* Trigger ADC scan loop start by setting RPS Signal 0. */
/* MC_ENABLE( P_MC2, MC2_ADC_RPS ); */
-/* // Wait until ADC scan loop is finished (RPS Signal 0 reset). */
+/* Wait until ADC scan loop is finished (RPS Signal 0 reset). */
/* while ( MC_TEST( P_MC2, MC2_ADC_RPS ) ); */
-/* // Init ptr to DMA buffer that holds new ADC data. We skip the */
-/* // first uint16_t in the buffer because it contains junk data from */
-/* // the final ADC of the previous poll list scan. */
+/* Init ptr to DMA buffer that holds new ADC data. We skip the
+ * first uint16_t in the buffer because it contains junk data from
+ * the final ADC of the previous poll list scan.
+ */
/* readaddr = (uint32_t *)devpriv->ANABuf.LogicalBase + 1; */
-/* // Convert ADC data to 16-bit integer values and copy to application */
-/* // buffer. */
+/* Convert ADC data to 16-bit integer values and copy to application buffer. */
/* for ( i = 0; i < devpriv->AdcItems; i++ ) { */
/* *data = s626_ai_reg_to_uint( *readaddr++ ); */
/* DEBUG("s626_ai_rinsn: data %d \n",*data); */
@@ -1525,8 +1540,8 @@ static int s626_ai_insn_config(comedi_device * dev, comedi_subdevice * s,
/* return i; */
/* } */
-static int s626_ai_insn_read(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int s626_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
uint16_t chan = CR_CHAN(insn->chanspec);
uint16_t range = CR_RANGE(insn->chanspec);
@@ -1534,86 +1549,87 @@ static int s626_ai_insn_read(comedi_device * dev, comedi_subdevice * s,
uint32_t GpioImage;
int n;
-/* //interrupt call test */
-/* writel(IRQ_GPIO3,devpriv->base_addr+P_PSR); //Writing a logical 1 */
-/* //into any of the RPS_PSR */
-/* //bits causes the */
-/* //corresponding interrupt */
-/* //to be generated if */
-/* //enabled */
+ /* interrupt call test */
+/* writel(IRQ_GPIO3,devpriv->base_addr+P_PSR); */
+ /* Writing a logical 1 into any of the RPS_PSR bits causes the
+ * corresponding interrupt to be generated if enabled
+ */
DEBUG("s626_ai_insn_read: entering\n");
- // Convert application's ADC specification into form
- // appropriate for register programming.
+ /* Convert application's ADC specification into form
+ * appropriate for register programming.
+ */
if (range == 0)
AdcSpec = (chan << 8) | (GSEL_BIPOLAR5V);
else
AdcSpec = (chan << 8) | (GSEL_BIPOLAR10V);
- // Switch ADC analog gain.
- DEBIwrite(dev, LP_GSEL, AdcSpec); // Set gain.
+ /* Switch ADC analog gain. */
+ DEBIwrite(dev, LP_GSEL, AdcSpec); /* Set gain. */
- // Select ADC analog input channel.
- DEBIwrite(dev, LP_ISEL, AdcSpec); // Select channel.
+ /* Select ADC analog input channel. */
+ DEBIwrite(dev, LP_ISEL, AdcSpec); /* Select channel. */
for (n = 0; n < insn->n; n++) {
- // Delay 10 microseconds for analog input settling.
+ /* Delay 10 microseconds for analog input settling. */
comedi_udelay(10);
- // Start ADC by pulsing GPIO1 low.
+ /* Start ADC by pulsing GPIO1 low. */
GpioImage = RR7146(P_GPIO);
- // Assert ADC Start command
+ /* Assert ADC Start command */
WR7146(P_GPIO, GpioImage & ~GPIO1_HI);
- // and stretch it out.
+ /* and stretch it out. */
WR7146(P_GPIO, GpioImage & ~GPIO1_HI);
WR7146(P_GPIO, GpioImage & ~GPIO1_HI);
- // Negate ADC Start command.
+ /* Negate ADC Start command. */
WR7146(P_GPIO, GpioImage | GPIO1_HI);
- // Wait for ADC to complete (GPIO2 is asserted high when
- // ADC not busy) and for data from previous conversion to
- // shift into FB BUFFER 1 register.
+ /* Wait for ADC to complete (GPIO2 is asserted high when */
+ /* ADC not busy) and for data from previous conversion to */
+ /* shift into FB BUFFER 1 register. */
- // Wait for ADC done.
- while (!(RR7146(P_PSR) & PSR_GPIO2)) ;
+ /* Wait for ADC done. */
+ while (!(RR7146(P_PSR) & PSR_GPIO2))
+ ;
- // Fetch ADC data.
+ /* Fetch ADC data. */
if (n != 0)
data[n - 1] = s626_ai_reg_to_uint(RR7146(P_FB_BUFFER1));
- // Allow the ADC to stabilize for 4 microseconds before
- // starting the next (final) conversion. This delay is
- // necessary to allow sufficient time between last
- // conversion finished and the start of the next
- // conversion. Without this delay, the last conversion's
- // data value is sometimes set to the previous
- // conversion's data value.
+ /* Allow the ADC to stabilize for 4 microseconds before
+ * starting the next (final) conversion. This delay is
+ * necessary to allow sufficient time between last
+ * conversion finished and the start of the next
+ * conversion. Without this delay, the last conversion's
+ * data value is sometimes set to the previous
+ * conversion's data value.
+ */
comedi_udelay(4);
}
- // Start a dummy conversion to cause the data from the
- // previous conversion to be shifted in.
+ /* Start a dummy conversion to cause the data from the
+ * previous conversion to be shifted in. */
GpioImage = RR7146(P_GPIO);
- //Assert ADC Start command
+ /* Assert ADC Start command */
WR7146(P_GPIO, GpioImage & ~GPIO1_HI);
- // and stretch it out.
+ /* and stretch it out. */
WR7146(P_GPIO, GpioImage & ~GPIO1_HI);
WR7146(P_GPIO, GpioImage & ~GPIO1_HI);
- // Negate ADC Start command.
+ /* Negate ADC Start command. */
WR7146(P_GPIO, GpioImage | GPIO1_HI);
- // Wait for the data to arrive in FB BUFFER 1 register.
+ /* Wait for the data to arrive in FB BUFFER 1 register. */
- // Wait for ADC done.
- while (!(RR7146(P_PSR) & PSR_GPIO2)) ;
+ /* Wait for ADC done. */
+ while (!(RR7146(P_PSR) & PSR_GPIO2))
+ ;
- // Fetch ADC data from audio interface's input shift
- // register.
+ /* Fetch ADC data from audio interface's input shift register. */
- // Fetch ADC data.
+ /* Fetch ADC data. */
if (n != 0)
data[n - 1] = s626_ai_reg_to_uint(RR7146(P_FB_BUFFER1));
@@ -1622,7 +1638,7 @@ static int s626_ai_insn_read(comedi_device * dev, comedi_subdevice * s,
return n;
}
-static int s626_ai_load_polllist(uint8_t * ppl, comedi_cmd * cmd)
+static int s626_ai_load_polllist(uint8_t *ppl, struct comedi_cmd *cmd)
{
int n;
@@ -1638,7 +1654,7 @@ static int s626_ai_load_polllist(uint8_t * ppl, comedi_cmd * cmd)
return n;
}
-static int s626_ai_inttrig(comedi_device * dev, comedi_subdevice * s,
+static int s626_ai_inttrig(struct comedi_device *dev, struct comedi_subdevice *s,
unsigned int trignum)
{
if (trignum != 0)
@@ -1646,7 +1662,7 @@ static int s626_ai_inttrig(comedi_device * dev, comedi_subdevice * s,
DEBUG("s626_ai_inttrig: trigger adc start...");
- // Start executing the RPS program.
+ /* Start executing the RPS program. */
MC_ENABLE(P_MC1, MC1_ERPS1);
s->async->inttrig = NULL;
@@ -1657,12 +1673,12 @@ static int s626_ai_inttrig(comedi_device * dev, comedi_subdevice * s,
}
/* TO COMPLETE */
-static int s626_ai_cmd(comedi_device * dev, comedi_subdevice * s)
+static int s626_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
uint8_t ppl[16];
- comedi_cmd *cmd = &s->async->cmd;
- enc_private *k;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ struct enc_private *k;
int tick;
DEBUG("s626_ai_cmd: entering command function\n");
@@ -1672,20 +1688,20 @@ static int s626_ai_cmd(comedi_device * dev, comedi_subdevice * s)
dev->minor);
return -EBUSY;
}
- //disable interrupt
+ /* disable interrupt */
writel(0, devpriv->base_addr + P_IER);
- //clear interrupt request
+ /* clear interrupt request */
writel(IRQ_RPS1 | IRQ_GPIO3, devpriv->base_addr + P_ISR);
- //clear any pending interrupt
+ /* clear any pending interrupt */
s626_dio_clear_irq(dev);
- // s626_enc_clear_irq(dev);
+ /* s626_enc_clear_irq(dev); */
- //reset ai_cmd_running flag
+ /* reset ai_cmd_running flag */
devpriv->ai_cmd_running = 0;
- // test if cmd is valid
+ /* test if cmd is valid */
if (cmd == NULL) {
DEBUG("s626_ai_cmd: NULL command\n");
return -EINVAL;
@@ -1707,12 +1723,12 @@ static int s626_ai_cmd(comedi_device * dev, comedi_subdevice * s)
case TRIG_FOLLOW:
break;
case TRIG_TIMER:
- // set a conter to generate adc trigger at scan_begin_arg interval
+ /* set a conter to generate adc trigger at scan_begin_arg interval */
k = &encpriv[5];
tick = s626_ns_to_timer((int *)&cmd->scan_begin_arg,
cmd->flags & TRIG_ROUND_MASK);
- //load timer value and enable interrupt
+ /* load timer value and enable interrupt */
s626_timer_load(dev, k, tick);
k->SetEnable(dev, k, CLKENAB_ALWAYS);
@@ -1721,7 +1737,7 @@ static int s626_ai_cmd(comedi_device * dev, comedi_subdevice * s)
break;
case TRIG_EXT:
- // set the digital line and interrupt for scan trigger
+ /* set the digital line and interrupt for scan trigger */
if (cmd->start_src != TRIG_EXT)
s626_dio_set_irq(dev, cmd->scan_begin_arg);
@@ -1734,19 +1750,19 @@ static int s626_ai_cmd(comedi_device * dev, comedi_subdevice * s)
case TRIG_NOW:
break;
case TRIG_TIMER:
- // set a conter to generate adc trigger at convert_arg interval
+ /* set a conter to generate adc trigger at convert_arg interval */
k = &encpriv[4];
tick = s626_ns_to_timer((int *)&cmd->convert_arg,
cmd->flags & TRIG_ROUND_MASK);
- //load timer value and enable interrupt
+ /* load timer value and enable interrupt */
s626_timer_load(dev, k, tick);
k->SetEnable(dev, k, CLKENAB_INDEX);
DEBUG("s626_ai_cmd: convert trigger timer is set with value %d\n", tick);
break;
case TRIG_EXT:
- // set the digital line and interrupt for convert trigger
+ /* set the digital line and interrupt for convert trigger */
if (cmd->scan_begin_src != TRIG_EXT
&& cmd->start_src == TRIG_EXT)
s626_dio_set_irq(dev, cmd->convert_arg);
@@ -1758,12 +1774,12 @@ static int s626_ai_cmd(comedi_device * dev, comedi_subdevice * s)
switch (cmd->stop_src) {
case TRIG_COUNT:
- // data arrives as one packet
+ /* data arrives as one packet */
devpriv->ai_sample_count = cmd->stop_arg;
devpriv->ai_continous = 0;
break;
case TRIG_NONE:
- // continous aquisition
+ /* continous aquisition */
devpriv->ai_continous = 1;
devpriv->ai_sample_count = 0;
break;
@@ -1773,17 +1789,17 @@ static int s626_ai_cmd(comedi_device * dev, comedi_subdevice * s)
switch (cmd->start_src) {
case TRIG_NOW:
- // Trigger ADC scan loop start by setting RPS Signal 0.
- // MC_ENABLE( P_MC2, MC2_ADC_RPS );
+ /* Trigger ADC scan loop start by setting RPS Signal 0. */
+ /* MC_ENABLE( P_MC2, MC2_ADC_RPS ); */
- // Start executing the RPS program.
+ /* Start executing the RPS program. */
MC_ENABLE(P_MC1, MC1_ERPS1);
DEBUG("s626_ai_cmd: ADC triggered\n");
s->async->inttrig = NULL;
break;
case TRIG_EXT:
- //configure DIO channel for acquisition trigger
+ /* configure DIO channel for acquisition trigger */
s626_dio_set_irq(dev, cmd->start_arg);
DEBUG("s626_ai_cmd: External start trigger is set!!!\n");
@@ -1795,7 +1811,7 @@ static int s626_ai_cmd(comedi_device * dev, comedi_subdevice * s)
break;
}
- //enable interrupt
+ /* enable interrupt */
writel(IRQ_GPIO3 | IRQ_RPS1, devpriv->base_addr + P_IER);
DEBUG("s626_ai_cmd: command function terminated\n");
@@ -1803,8 +1819,8 @@ static int s626_ai_cmd(comedi_device * dev, comedi_subdevice * s)
return 0;
}
-static int s626_ai_cmdtest(comedi_device * dev, comedi_subdevice * s,
- comedi_cmd * cmd)
+static int s626_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
{
int err = 0;
int tmp;
@@ -1988,12 +2004,12 @@ static int s626_ai_cmdtest(comedi_device * dev, comedi_subdevice * s,
return 0;
}
-static int s626_ai_cancel(comedi_device * dev, comedi_subdevice * s)
+static int s626_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
- // Stop RPS program in case it is currently running.
+ /* Stop RPS program in case it is currently running. */
MC_DISABLE(P_MC1, MC1_ERPS1);
- //disable master interrupt
+ /* disable master interrupt */
writel(0, devpriv->base_addr + P_IER);
devpriv->ai_cmd_running = 0;
@@ -2010,7 +2026,7 @@ static int s626_ns_to_timer(int *nanosec, int round_mode)
{
int divider, base;
- base = 500; //2MHz internal clock
+ base = 500; /* 2MHz internal clock */
switch (round_mode) {
case TRIG_ROUND_NEAREST:
@@ -2029,8 +2045,8 @@ static int s626_ns_to_timer(int *nanosec, int round_mode)
return divider - 1;
}
-static int s626_ao_winsn(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int s626_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int i;
@@ -2048,45 +2064,42 @@ static int s626_ao_winsn(comedi_device * dev, comedi_subdevice * s,
return i;
}
-static int s626_ao_rinsn(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int s626_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int i;
- for (i = 0; i < insn->n; i++) {
+ for (i = 0; i < insn->n; i++)
data[i] = devpriv->ao_readback[CR_CHAN(insn->chanspec)];
- }
return i;
}
-/////////////////////////////////////////////////////////////////////
-/////////////// DIGITAL I/O FUNCTIONS /////////////////////////////
-/////////////////////////////////////////////////////////////////////
-// All DIO functions address a group of DIO channels by means of
-// "group" argument. group may be 0, 1 or 2, which correspond to DIO
-// ports A, B and C, respectively.
-/////////////////////////////////////////////////////////////////////
+/* *************** DIGITAL I/O FUNCTIONS ***************
+ * All DIO functions address a group of DIO channels by means of
+ * "group" argument. group may be 0, 1 or 2, which correspond to DIO
+ * ports A, B and C, respectively.
+ */
-static void s626_dio_init(comedi_device * dev)
+static void s626_dio_init(struct comedi_device *dev)
{
uint16_t group;
- comedi_subdevice *s;
+ struct comedi_subdevice *s;
- // Prepare to treat writes to WRCapSel as capture disables.
+ /* Prepare to treat writes to WRCapSel as capture disables. */
DEBIwrite(dev, LP_MISC1, MISC1_NOEDCAP);
- // For each group of sixteen channels ...
+ /* For each group of sixteen channels ... */
for (group = 0; group < S626_DIO_BANKS; group++) {
s = dev->subdevices + 2 + group;
- DEBIwrite(dev, diopriv->WRIntSel, 0); // Disable all interrupts.
- DEBIwrite(dev, diopriv->WRCapSel, 0xFFFF); // Disable all event
- // captures.
- DEBIwrite(dev, diopriv->WREdgSel, 0); // Init all DIOs to
- // default edge
- // polarity.
- DEBIwrite(dev, diopriv->WRDOut, 0); // Program all outputs
- // to inactive state.
+ DEBIwrite(dev, diopriv->WRIntSel, 0); /* Disable all interrupts. */
+ DEBIwrite(dev, diopriv->WRCapSel, 0xFFFF); /* Disable all event */
+ /* captures. */
+ DEBIwrite(dev, diopriv->WREdgSel, 0); /* Init all DIOs to */
+ /* default edge */
+ /* polarity. */
+ DEBIwrite(dev, diopriv->WRDOut, 0); /* Program all outputs */
+ /* to inactive state. */
}
DEBUG("s626_dio_init: DIO initialized \n");
}
@@ -2097,14 +2110,14 @@ static void s626_dio_init(comedi_device * dev)
* This allows packed reading/writing of the DIO channels. The comedi
* core can convert between insn_bits and insn_read/write */
-static int s626_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int s626_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
/* Length of data must be 2 (mask and new data, see below) */
- if (insn->n == 0) {
+ if (insn->n == 0)
return 0;
- }
+
if (insn->n != 2) {
printk("comedi%d: s626: s626_dio_insn_bits(): Invalid instruction length\n", dev->minor);
return -EINVAL;
@@ -2133,8 +2146,8 @@ static int s626_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
return 2;
}
-static int s626_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int s626_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
switch (data[0]) {
@@ -2160,75 +2173,75 @@ static int s626_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
return 1;
}
-static int s626_dio_set_irq(comedi_device * dev, unsigned int chan)
+static int s626_dio_set_irq(struct comedi_device *dev, unsigned int chan)
{
unsigned int group;
unsigned int bitmask;
unsigned int status;
- //select dio bank
+ /* select dio bank */
group = chan / 16;
bitmask = 1 << (chan - (16 * group));
DEBUG("s626_dio_set_irq: enable interrupt on dio channel %d group %d\n",
chan - (16 * group), group);
- //set channel to capture positive edge
+ /* set channel to capture positive edge */
status = DEBIread(dev,
- ((dio_private *) (dev->subdevices + 2 +
+ ((struct dio_private *) (dev->subdevices + 2 +
group)->private)->RDEdgSel);
DEBIwrite(dev,
- ((dio_private *) (dev->subdevices + 2 +
+ ((struct dio_private *) (dev->subdevices + 2 +
group)->private)->WREdgSel, bitmask | status);
- //enable interrupt on selected channel
+ /* enable interrupt on selected channel */
status = DEBIread(dev,
- ((dio_private *) (dev->subdevices + 2 +
+ ((struct dio_private *) (dev->subdevices + 2 +
group)->private)->RDIntSel);
DEBIwrite(dev,
- ((dio_private *) (dev->subdevices + 2 +
+ ((struct dio_private *) (dev->subdevices + 2 +
group)->private)->WRIntSel, bitmask | status);
- //enable edge capture write command
+ /* enable edge capture write command */
DEBIwrite(dev, LP_MISC1, MISC1_EDCAP);
- //enable edge capture on selected channel
+ /* enable edge capture on selected channel */
status = DEBIread(dev,
- ((dio_private *) (dev->subdevices + 2 +
+ ((struct dio_private *) (dev->subdevices + 2 +
group)->private)->RDCapSel);
DEBIwrite(dev,
- ((dio_private *) (dev->subdevices + 2 +
+ ((struct dio_private *) (dev->subdevices + 2 +
group)->private)->WRCapSel, bitmask | status);
return 0;
}
-static int s626_dio_reset_irq(comedi_device * dev, unsigned int group,
+static int s626_dio_reset_irq(struct comedi_device *dev, unsigned int group,
unsigned int mask)
{
DEBUG("s626_dio_reset_irq: disable interrupt on dio channel %d group %d\n", mask, group);
- //disable edge capture write command
+ /* disable edge capture write command */
DEBIwrite(dev, LP_MISC1, MISC1_NOEDCAP);
- //enable edge capture on selected channel
+ /* enable edge capture on selected channel */
DEBIwrite(dev,
- ((dio_private *) (dev->subdevices + 2 +
+ ((struct dio_private *) (dev->subdevices + 2 +
group)->private)->WRCapSel, mask);
return 0;
}
-static int s626_dio_clear_irq(comedi_device * dev)
+static int s626_dio_clear_irq(struct comedi_device *dev)
{
unsigned int group;
- //disable edge capture write command
+ /* disable edge capture write command */
DEBIwrite(dev, LP_MISC1, MISC1_NOEDCAP);
for (group = 0; group < S626_DIO_BANKS; group++) {
- //clear pending events and interrupt
+ /* clear pending events and interrupt */
DEBIwrite(dev,
- ((dio_private *) (dev->subdevices + 2 +
+ ((struct dio_private *) (dev->subdevices + 2 +
group)->private)->WRCapSel, 0xffff);
}
@@ -2238,26 +2251,26 @@ static int s626_dio_clear_irq(comedi_device * dev)
/* Now this function initializes the value of the counter (data[0])
and set the subdevice. To complete with trigger and interrupt
configuration */
-static int s626_enc_insn_config(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int s626_enc_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
- uint16_t Setup = (LOADSRC_INDX << BF_LOADSRC) | // Preload upon
- // index.
- (INDXSRC_SOFT << BF_INDXSRC) | // Disable hardware index.
- (CLKSRC_COUNTER << BF_CLKSRC) | // Operating mode is Counter.
- (CLKPOL_POS << BF_CLKPOL) | // Active high clock.
- //( CNTDIR_UP << BF_CLKPOL ) | // Count direction is Down.
- (CLKMULT_1X << BF_CLKMULT) | // Clock multiplier is 1x.
+ uint16_t Setup = (LOADSRC_INDX << BF_LOADSRC) | /* Preload upon */
+ /* index. */
+ (INDXSRC_SOFT << BF_INDXSRC) | /* Disable hardware index. */
+ (CLKSRC_COUNTER << BF_CLKSRC) | /* Operating mode is Counter. */
+ (CLKPOL_POS << BF_CLKPOL) | /* Active high clock. */
+ /* ( CNTDIR_UP << BF_CLKPOL ) | // Count direction is Down. */
+ (CLKMULT_1X << BF_CLKMULT) | /* Clock multiplier is 1x. */
(CLKENAB_INDEX << BF_CLKENAB);
/* uint16_t DisableIntSrc=TRUE; */
- // uint32_t Preloadvalue; //Counter initial value
+ /* uint32_t Preloadvalue; //Counter initial value */
uint16_t valueSrclatch = LATCHSRC_AB_READ;
uint16_t enab = CLKENAB_ALWAYS;
- enc_private *k = &encpriv[CR_CHAN(insn->chanspec)];
+ struct enc_private *k = &encpriv[CR_CHAN(insn->chanspec)];
DEBUG("s626_enc_insn_config: encoder config\n");
- // (data==NULL) ? (Preloadvalue=0) : (Preloadvalue=data[0]);
+ /* (data==NULL) ? (Preloadvalue=0) : (Preloadvalue=data[0]); */
k->SetMode(dev, k, Setup, TRUE);
Preload(dev, k, *(insn->data));
@@ -2268,12 +2281,12 @@ static int s626_enc_insn_config(comedi_device * dev, comedi_subdevice * s,
return insn->n;
}
-static int s626_enc_insn_read(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int s626_enc_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int n;
- enc_private *k = &encpriv[CR_CHAN(insn->chanspec)];
+ struct enc_private *k = &encpriv[CR_CHAN(insn->chanspec)];
DEBUG("s626_enc_insn_read: encoder read channel %d \n",
CR_CHAN(insn->chanspec));
@@ -2286,20 +2299,20 @@ static int s626_enc_insn_read(comedi_device * dev, comedi_subdevice * s,
return n;
}
-static int s626_enc_insn_write(comedi_device * dev, comedi_subdevice * s,
- comedi_insn * insn, lsampl_t * data)
+static int s626_enc_insn_write(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
- enc_private *k = &encpriv[CR_CHAN(insn->chanspec)];
+ struct enc_private *k = &encpriv[CR_CHAN(insn->chanspec)];
DEBUG("s626_enc_insn_write: encoder write channel %d \n",
CR_CHAN(insn->chanspec));
- // Set the preload register
+ /* Set the preload register */
Preload(dev, k, data[0]);
- // Software index pulse forces the preload register to load
- // into the counter
+ /* Software index pulse forces the preload register to load */
+ /* into the counter */
k->SetLoadTrig(dev, k, 0);
k->PulseIndex(dev, k);
k->SetLoadTrig(dev, k, 2);
@@ -2309,175 +2322,165 @@ static int s626_enc_insn_write(comedi_device * dev, comedi_subdevice * s,
return 1;
}
-static void s626_timer_load(comedi_device * dev, enc_private * k, int tick)
+static void s626_timer_load(struct comedi_device *dev, struct enc_private *k, int tick)
{
- uint16_t Setup = (LOADSRC_INDX << BF_LOADSRC) | // Preload upon
- // index.
- (INDXSRC_SOFT << BF_INDXSRC) | // Disable hardware index.
- (CLKSRC_TIMER << BF_CLKSRC) | // Operating mode is Timer.
- (CLKPOL_POS << BF_CLKPOL) | // Active high clock.
- (CNTDIR_DOWN << BF_CLKPOL) | // Count direction is Down.
- (CLKMULT_1X << BF_CLKMULT) | // Clock multiplier is 1x.
+ uint16_t Setup = (LOADSRC_INDX << BF_LOADSRC) | /* Preload upon */
+ /* index. */
+ (INDXSRC_SOFT << BF_INDXSRC) | /* Disable hardware index. */
+ (CLKSRC_TIMER << BF_CLKSRC) | /* Operating mode is Timer. */
+ (CLKPOL_POS << BF_CLKPOL) | /* Active high clock. */
+ (CNTDIR_DOWN << BF_CLKPOL) | /* Count direction is Down. */
+ (CLKMULT_1X << BF_CLKMULT) | /* Clock multiplier is 1x. */
(CLKENAB_INDEX << BF_CLKENAB);
uint16_t valueSrclatch = LATCHSRC_A_INDXA;
- // uint16_t enab=CLKENAB_ALWAYS;
+ /* uint16_t enab=CLKENAB_ALWAYS; */
k->SetMode(dev, k, Setup, FALSE);
- // Set the preload register
+ /* Set the preload register */
Preload(dev, k, tick);
- // Software index pulse forces the preload register to load
- // into the counter
+ /* Software index pulse forces the preload register to load */
+ /* into the counter */
k->SetLoadTrig(dev, k, 0);
k->PulseIndex(dev, k);
- //set reload on counter overflow
+ /* set reload on counter overflow */
k->SetLoadTrig(dev, k, 1);
- //set interrupt on overflow
+ /* set interrupt on overflow */
k->SetIntSrc(dev, k, INTSRC_OVER);
SetLatchSource(dev, k, valueSrclatch);
- // k->SetEnable(dev,k,(uint16_t)(enab != 0));
+ /* k->SetEnable(dev,k,(uint16_t)(enab != 0)); */
}
-///////////////////////////////////////////////////////////////////////
-///////////////////// DAC FUNCTIONS /////////////////////////////////
-///////////////////////////////////////////////////////////////////////
+/* *********** DAC FUNCTIONS *********** */
-// Slot 0 base settings.
-#define VECT0 ( XSD2 | RSD3 | SIB_A2 ) // Slot 0 always shifts in
- // 0xFF and store it to
- // FB_BUFFER2.
+/* Slot 0 base settings. */
+#define VECT0 (XSD2 | RSD3 | SIB_A2)
+/* Slot 0 always shifts in 0xFF and store it to FB_BUFFER2. */
-// TrimDac LogicalChan-to-PhysicalChan mapping table.
+/* TrimDac LogicalChan-to-PhysicalChan mapping table. */
static uint8_t trimchan[] = { 10, 9, 8, 3, 2, 7, 6, 1, 0, 5, 4 };
-// TrimDac LogicalChan-to-EepromAdrs mapping table.
+/* TrimDac LogicalChan-to-EepromAdrs mapping table. */
static uint8_t trimadrs[] =
{ 0x40, 0x41, 0x42, 0x50, 0x51, 0x52, 0x53, 0x60, 0x61, 0x62, 0x63 };
-static void LoadTrimDACs(comedi_device * dev)
+static void LoadTrimDACs(struct comedi_device *dev)
{
register uint8_t i;
- // Copy TrimDac setpoint values from EEPROM to TrimDacs.
+ /* Copy TrimDac setpoint values from EEPROM to TrimDacs. */
for (i = 0; i < (sizeof(trimchan) / sizeof(trimchan[0])); i++)
WriteTrimDAC(dev, i, I2Cread(dev, trimadrs[i]));
}
-static void WriteTrimDAC(comedi_device * dev, uint8_t LogicalChan,
+static void WriteTrimDAC(struct comedi_device *dev, uint8_t LogicalChan,
uint8_t DacData)
{
uint32_t chan;
- // Save the new setpoint in case the application needs to read it back later.
+ /* Save the new setpoint in case the application needs to read it back later. */
devpriv->TrimSetpoint[LogicalChan] = (uint8_t) DacData;
- // Map logical channel number to physical channel number.
+ /* Map logical channel number to physical channel number. */
chan = (uint32_t) trimchan[LogicalChan];
- // Set up TSL2 records for TrimDac write operation. All slots shift
- // 0xFF in from pulled-up SD3 so that the end of the slot sequence
- // can be detected.
- SETVECT(2, XSD2 | XFIFO_1 | WS3); // Slot 2: Send high uint8_t
- // to target TrimDac.
- SETVECT(3, XSD2 | XFIFO_0 | WS3); // Slot 3: Send low uint8_t to
- // target TrimDac.
- SETVECT(4, XSD2 | XFIFO_3 | WS1); // Slot 4: Send NOP high
- // uint8_t to DAC0 to keep
- // clock running.
- SETVECT(5, XSD2 | XFIFO_2 | WS1 | EOS); // Slot 5: Send NOP low
- // uint8_t to DAC0.
-
- // Construct and transmit target DAC's serial packet: ( 0000 AAAA
- // ),( DDDD DDDD ),( 0x00 ),( 0x00 ) where A<3:0> is the DAC
- // channel's address, and D<7:0> is the DAC setpoint. Append a WORD
- // value (that writes a channel 0 NOP command to a non-existent main
- // DAC channel) that serves to keep the clock running after the
- // packet has been sent to the target DAC.
-
- SendDAC(dev, ((uint32_t) chan << 8) // Address the DAC channel
- // within the trimdac device.
- | (uint32_t) DacData); // Include DAC setpoint data.
-}
+ /* Set up TSL2 records for TrimDac write operation. All slots shift
+ * 0xFF in from pulled-up SD3 so that the end of the slot sequence
+ * can be detected.
+ */
-/////////////////////////////////////////////////////////////////////////
-//////////////// EEPROM ACCESS FUNCTIONS //////////////////////////////
-/////////////////////////////////////////////////////////////////////////
+ SETVECT(2, XSD2 | XFIFO_1 | WS3);
+ /* Slot 2: Send high uint8_t to target TrimDac. */
+ SETVECT(3, XSD2 | XFIFO_0 | WS3);
+ /* Slot 3: Send low uint8_t to target TrimDac. */
+ SETVECT(4, XSD2 | XFIFO_3 | WS1);
+ /* Slot 4: Send NOP high uint8_t to DAC0 to keep clock running. */
+ SETVECT(5, XSD2 | XFIFO_2 | WS1 | EOS);
+ /* Slot 5: Send NOP low uint8_t to DAC0. */
+
+ /* Construct and transmit target DAC's serial packet:
+ * ( 0000 AAAA ), ( DDDD DDDD ),( 0x00 ),( 0x00 ) where A<3:0> is the
+ * DAC channel's address, and D<7:0> is the DAC setpoint. Append a
+ * WORD value (that writes a channel 0 NOP command to a non-existent
+ * main DAC channel) that serves to keep the clock running after the
+ * packet has been sent to the target DAC.
+ */
+
+ /* Address the DAC channel within the trimdac device. */
+ SendDAC(dev, ((uint32_t) chan << 8)
+ | (uint32_t) DacData); /* Include DAC setpoint data. */
+}
-///////////////////////////////////////////
-// Read uint8_t from EEPROM.
+/* ************** EEPROM ACCESS FUNCTIONS ************** */
+/* Read uint8_t from EEPROM. */
-static uint8_t I2Cread(comedi_device * dev, uint8_t addr)
+static uint8_t I2Cread(struct comedi_device *dev, uint8_t addr)
{
uint8_t rtnval;
- // Send EEPROM target address.
- if (I2Chandshake(dev, I2C_B2(I2C_ATTRSTART, I2CW) // Byte2 = I2C
- // command:
- // write to
- // I2C EEPROM
- // device.
- | I2C_B1(I2C_ATTRSTOP, addr) // Byte1 = EEPROM
- // internal target
- // address.
- | I2C_B0(I2C_ATTRNOP, 0))) // Byte0 = Not
- // sent.
- {
- // Abort function and declare error if handshake failed.
+ /* Send EEPROM target address. */
+ if (I2Chandshake(dev, I2C_B2(I2C_ATTRSTART, I2CW)
+ /* Byte2 = I2C command: write to I2C EEPROM device. */
+ | I2C_B1(I2C_ATTRSTOP, addr)
+ /* Byte1 = EEPROM internal target address. */
+ | I2C_B0(I2C_ATTRNOP, 0))) { /* Byte0 = Not sent. */
+ /* Abort function and declare error if handshake failed. */
DEBUG("I2Cread: error handshake I2Cread a\n");
return 0;
}
- // Execute EEPROM read.
- if (I2Chandshake(dev, I2C_B2(I2C_ATTRSTART, I2CR) // Byte2 = I2C
- // command: read
- // from I2C EEPROM
- // device.
- | I2C_B1(I2C_ATTRSTOP, 0) // Byte1 receives
- // uint8_t from
- // EEPROM.
- | I2C_B0(I2C_ATTRNOP, 0))) // Byte0 = Not
- // sent.
- {
- // Abort function and declare error if handshake failed.
+ /* Execute EEPROM read. */
+ if (I2Chandshake(dev, I2C_B2(I2C_ATTRSTART, I2CR) /* Byte2 = I2C */
+ /* command: read */
+ /* from I2C EEPROM */
+ /* device. */
+ | I2C_B1(I2C_ATTRSTOP, 0) /* Byte1 receives */
+ /* uint8_t from */
+ /* EEPROM. */
+ | I2C_B0(I2C_ATTRNOP, 0))) { /* Byte0 = Not sent. */
+
+ /* Abort function and declare error if handshake failed. */
DEBUG("I2Cread: error handshake I2Cread b\n");
return 0;
}
- // Return copy of EEPROM value.
+ /* Return copy of EEPROM value. */
rtnval = (uint8_t) (RR7146(P_I2CCTRL) >> 16);
return rtnval;
}
-static uint32_t I2Chandshake(comedi_device * dev, uint32_t val)
+static uint32_t I2Chandshake(struct comedi_device *dev, uint32_t val)
{
- // Write I2C command to I2C Transfer Control shadow register.
+ /* Write I2C command to I2C Transfer Control shadow register. */
WR7146(P_I2CCTRL, val);
- // Upload I2C shadow registers into working registers and wait for
- // upload confirmation.
+ /* Upload I2C shadow registers into working registers and wait for */
+ /* upload confirmation. */
MC_ENABLE(P_MC2, MC2_UPLD_IIC);
- while (!MC_TEST(P_MC2, MC2_UPLD_IIC)) ;
+ while (!MC_TEST(P_MC2, MC2_UPLD_IIC))
+ ;
- // Wait until I2C bus transfer is finished or an error occurs.
- while ((RR7146(P_I2CCTRL) & (I2C_BUSY | I2C_ERR)) == I2C_BUSY) ;
+ /* Wait until I2C bus transfer is finished or an error occurs. */
+ while ((RR7146(P_I2CCTRL) & (I2C_BUSY | I2C_ERR)) == I2C_BUSY)
+ ;
- // Return non-zero if I2C error occured.
+ /* Return non-zero if I2C error occured. */
return RR7146(P_I2CCTRL) & I2C_ERR;
}
-// Private helper function: Write setpoint to an application DAC channel.
+/* Private helper function: Write setpoint to an application DAC channel. */
-static void SetDAC(comedi_device * dev, uint16_t chan, short dacdata)
+static void SetDAC(struct comedi_device *dev, uint16_t chan, short dacdata)
{
register uint16_t signmask;
register uint32_t WSImage;
- // Adjust DAC data polarity and set up Polarity Control Register
- // image.
+ /* Adjust DAC data polarity and set up Polarity Control Register */
+ /* image. */
signmask = 1 << chan;
if (dacdata < 0) {
dacdata = -dacdata;
@@ -2485,237 +2488,252 @@ static void SetDAC(comedi_device * dev, uint16_t chan, short dacdata)
} else
devpriv->Dacpol &= ~signmask;
- // Limit DAC setpoint value to valid range.
+ /* Limit DAC setpoint value to valid range. */
if ((uint16_t) dacdata > 0x1FFF)
dacdata = 0x1FFF;
- // Set up TSL2 records (aka "vectors") for DAC update. Vectors V2
- // and V3 transmit the setpoint to the target DAC. V4 and V5 send
- // data to a non-existent TrimDac channel just to keep the clock
- // running after sending data to the target DAC. This is necessary
- // to eliminate the clock glitch that would otherwise occur at the
- // end of the target DAC's serial data stream. When the sequence
- // restarts at V0 (after executing V5), the gate array automatically
- // disables gating for the DAC clock and all DAC chip selects.
- WSImage = (chan & 2) ? WS1 : WS2; // Choose DAC chip select to
- // be asserted.
- SETVECT(2, XSD2 | XFIFO_1 | WSImage); // Slot 2: Transmit high
- // data byte to target DAC.
- SETVECT(3, XSD2 | XFIFO_0 | WSImage); // Slot 3: Transmit low data
- // byte to target DAC.
- SETVECT(4, XSD2 | XFIFO_3 | WS3); // Slot 4: Transmit to
- // non-existent TrimDac
- // channel to keep clock
- SETVECT(5, XSD2 | XFIFO_2 | WS3 | EOS); // Slot 5: running after
- // writing target DAC's
- // low data byte.
-
- // Construct and transmit target DAC's serial packet: ( A10D DDDD
- // ),( DDDD DDDD ),( 0x0F ),( 0x00 ) where A is chan<0>, and D<12:0>
- // is the DAC setpoint. Append a WORD value (that writes to a
- // non-existent TrimDac channel) that serves to keep the clock
- // running after the packet has been sent to the target DAC.
- SendDAC(dev, 0x0F000000 //Continue clock after target DAC
- //data (write to non-existent
- //trimdac).
- | 0x00004000 // Address the two main dual-DAC
- // devices (TSL's chip select enables
- // target device).
- | ((uint32_t) (chan & 1) << 15) // Address the DAC
- // channel within the
- // device.
- | (uint32_t) dacdata); // Include DAC setpoint data.
+ /* Set up TSL2 records (aka "vectors") for DAC update. Vectors V2
+ * and V3 transmit the setpoint to the target DAC. V4 and V5 send
+ * data to a non-existent TrimDac channel just to keep the clock
+ * running after sending data to the target DAC. This is necessary
+ * to eliminate the clock glitch that would otherwise occur at the
+ * end of the target DAC's serial data stream. When the sequence
+ * restarts at V0 (after executing V5), the gate array automatically
+ * disables gating for the DAC clock and all DAC chip selects.
+ */
+
+ WSImage = (chan & 2) ? WS1 : WS2;
+ /* Choose DAC chip select to be asserted. */
+ SETVECT(2, XSD2 | XFIFO_1 | WSImage);
+ /* Slot 2: Transmit high data byte to target DAC. */
+ SETVECT(3, XSD2 | XFIFO_0 | WSImage);
+ /* Slot 3: Transmit low data byte to target DAC. */
+ SETVECT(4, XSD2 | XFIFO_3 | WS3);
+ /* Slot 4: Transmit to non-existent TrimDac channel to keep clock */
+ SETVECT(5, XSD2 | XFIFO_2 | WS3 | EOS);
+ /* Slot 5: running after writing target DAC's low data byte. */
+
+ /* Construct and transmit target DAC's serial packet:
+ * ( A10D DDDD ),( DDDD DDDD ),( 0x0F ),( 0x00 ) where A is chan<0>,
+ * and D<12:0> is the DAC setpoint. Append a WORD value (that writes
+ * to a non-existent TrimDac channel) that serves to keep the clock
+ * running after the packet has been sent to the target DAC.
+ */
+ SendDAC(dev, 0x0F000000
+ /* Continue clock after target DAC data (write to non-existent trimdac). */
+ | 0x00004000
+ /* Address the two main dual-DAC devices (TSL's chip select enables
+ * target device). */
+ | ((uint32_t) (chan & 1) << 15)
+ /* Address the DAC channel within the device. */
+ | (uint32_t) dacdata); /* Include DAC setpoint data. */
}
-////////////////////////////////////////////////////////
-// Private helper function: Transmit serial data to DAC via Audio
-// channel 2. Assumes: (1) TSL2 slot records initialized, and (2)
-// Dacpol contains valid target image.
+/* Private helper function: Transmit serial data to DAC via Audio
+ * channel 2. Assumes: (1) TSL2 slot records initialized, and (2)
+ * Dacpol contains valid target image.
+ */
-static void SendDAC(comedi_device * dev, uint32_t val)
+static void SendDAC(struct comedi_device *dev, uint32_t val)
{
- // START THE SERIAL CLOCK RUNNING -------------
+ /* START THE SERIAL CLOCK RUNNING ------------- */
- // Assert DAC polarity control and enable gating of DAC serial clock
- // and audio bit stream signals. At this point in time we must be
- // assured of being in time slot 0. If we are not in slot 0, the
- // serial clock and audio stream signals will be disabled; this is
- // because the following DEBIwrite statement (which enables signals
- // to be passed through the gate array) would execute before the
- // trailing edge of WS1/WS3 (which turns off the signals), thus
- // causing the signals to be inactive during the DAC write.
+ /* Assert DAC polarity control and enable gating of DAC serial clock
+ * and audio bit stream signals. At this point in time we must be
+ * assured of being in time slot 0. If we are not in slot 0, the
+ * serial clock and audio stream signals will be disabled; this is
+ * because the following DEBIwrite statement (which enables signals
+ * to be passed through the gate array) would execute before the
+ * trailing edge of WS1/WS3 (which turns off the signals), thus
+ * causing the signals to be inactive during the DAC write.
+ */
DEBIwrite(dev, LP_DACPOL, devpriv->Dacpol);
- // TRANSFER OUTPUT DWORD VALUE INTO A2'S OUTPUT FIFO ----------------
+ /* TRANSFER OUTPUT DWORD VALUE INTO A2'S OUTPUT FIFO ---------------- */
- // Copy DAC setpoint value to DAC's output DMA buffer.
+ /* Copy DAC setpoint value to DAC's output DMA buffer. */
- //WR7146( (uint32_t)devpriv->pDacWBuf, val );
+ /* WR7146( (uint32_t)devpriv->pDacWBuf, val ); */
*devpriv->pDacWBuf = val;
- // enab the output DMA transfer. This will cause the DMAC to copy
- // the DAC's data value to A2's output FIFO. The DMA transfer will
- // then immediately terminate because the protection address is
- // reached upon transfer of the first DWORD value.
+ /* enab the output DMA transfer. This will cause the DMAC to copy
+ * the DAC's data value to A2's output FIFO. The DMA transfer will
+ * then immediately terminate because the protection address is
+ * reached upon transfer of the first DWORD value.
+ */
MC_ENABLE(P_MC1, MC1_A2OUT);
- // While the DMA transfer is executing ...
+ /* While the DMA transfer is executing ... */
- // Reset Audio2 output FIFO's underflow flag (along with any other
- // FIFO underflow/overflow flags). When set, this flag will
- // indicate that we have emerged from slot 0.
+ /* Reset Audio2 output FIFO's underflow flag (along with any other
+ * FIFO underflow/overflow flags). When set, this flag will
+ * indicate that we have emerged from slot 0.
+ */
WR7146(P_ISR, ISR_AFOU);
- // Wait for the DMA transfer to finish so that there will be data
- // available in the FIFO when time slot 1 tries to transfer a DWORD
- // from the FIFO to the output buffer register. We test for DMA
- // Done by polling the DMAC enable flag; this flag is automatically
- // cleared when the transfer has finished.
- while ((RR7146(P_MC1) & MC1_A2OUT) != 0) ;
+ /* Wait for the DMA transfer to finish so that there will be data
+ * available in the FIFO when time slot 1 tries to transfer a DWORD
+ * from the FIFO to the output buffer register. We test for DMA
+ * Done by polling the DMAC enable flag; this flag is automatically
+ * cleared when the transfer has finished.
+ */
+ while ((RR7146(P_MC1) & MC1_A2OUT) != 0)
+ ;
- // START THE OUTPUT STREAM TO THE TARGET DAC --------------------
+ /* START THE OUTPUT STREAM TO THE TARGET DAC -------------------- */
- // FIFO data is now available, so we enable execution of time slots
- // 1 and higher by clearing the EOS flag in slot 0. Note that SD3
- // will be shifted in and stored in FB_BUFFER2 for end-of-slot-list
- // detection.
+ /* FIFO data is now available, so we enable execution of time slots
+ * 1 and higher by clearing the EOS flag in slot 0. Note that SD3
+ * will be shifted in and stored in FB_BUFFER2 for end-of-slot-list
+ * detection.
+ */
SETVECT(0, XSD2 | RSD3 | SIB_A2);
- // Wait for slot 1 to execute to ensure that the Packet will be
- // transmitted. This is detected by polling the Audio2 output FIFO
- // underflow flag, which will be set when slot 1 execution has
- // finished transferring the DAC's data DWORD from the output FIFO
- // to the output buffer register.
- while ((RR7146(P_SSR) & SSR_AF2_OUT) == 0) ;
-
- // Set up to trap execution at slot 0 when the TSL sequencer cycles
- // back to slot 0 after executing the EOS in slot 5. Also,
- // simultaneously shift out and in the 0x00 that is ALWAYS the value
- // stored in the last byte to be shifted out of the FIFO's DWORD
- // buffer register.
+ /* Wait for slot 1 to execute to ensure that the Packet will be
+ * transmitted. This is detected by polling the Audio2 output FIFO
+ * underflow flag, which will be set when slot 1 execution has
+ * finished transferring the DAC's data DWORD from the output FIFO
+ * to the output buffer register.
+ */
+ while ((RR7146(P_SSR) & SSR_AF2_OUT) == 0)
+ ;
+
+ /* Set up to trap execution at slot 0 when the TSL sequencer cycles
+ * back to slot 0 after executing the EOS in slot 5. Also,
+ * simultaneously shift out and in the 0x00 that is ALWAYS the value
+ * stored in the last byte to be shifted out of the FIFO's DWORD
+ * buffer register.
+ */
SETVECT(0, XSD2 | XFIFO_2 | RSD2 | SIB_A2 | EOS);
- // WAIT FOR THE TRANSACTION TO FINISH -----------------------
-
- // Wait for the TSL to finish executing all time slots before
- // exiting this function. We must do this so that the next DAC
- // write doesn't start, thereby enabling clock/chip select signals:
- // 1. Before the TSL sequence cycles back to slot 0, which disables
- // the clock/cs signal gating and traps slot // list execution. If
- // we have not yet finished slot 5 then the clock/cs signals are
- // still gated and we have // not finished transmitting the stream.
- // 2. While slots 2-5 are executing due to a late slot 0 trap. In
- // this case, the slot sequence is currently // repeating, but with
- // clock/cs signals disabled. We must wait for slot 0 to trap
- // execution before setting // up the next DAC setpoint DMA transfer
- // and enabling the clock/cs signals. To detect the end of slot 5,
- // we test for the FB_BUFFER2 MSB contents to be equal to 0xFF. If
- // the TSL has not yet finished executing slot 5 ...
+ /* WAIT FOR THE TRANSACTION TO FINISH ----------------------- */
+
+ /* Wait for the TSL to finish executing all time slots before
+ * exiting this function. We must do this so that the next DAC
+ * write doesn't start, thereby enabling clock/chip select signals:
+ *
+ * 1. Before the TSL sequence cycles back to slot 0, which disables
+ * the clock/cs signal gating and traps slot // list execution.
+ * we have not yet finished slot 5 then the clock/cs signals are
+ * still gated and we have not finished transmitting the stream.
+ *
+ * 2. While slots 2-5 are executing due to a late slot 0 trap. In
+ * this case, the slot sequence is currently repeating, but with
+ * clock/cs signals disabled. We must wait for slot 0 to trap
+ * execution before setting up the next DAC setpoint DMA transfer
+ * and enabling the clock/cs signals. To detect the end of slot 5,
+ * we test for the FB_BUFFER2 MSB contents to be equal to 0xFF. If
+ * the TSL has not yet finished executing slot 5 ...
+ */
if ((RR7146(P_FB_BUFFER2) & 0xFF000000) != 0) {
- // The trap was set on time and we are still executing somewhere
- // in slots 2-5, so we now wait for slot 0 to execute and trap
- // TSL execution. This is detected when FB_BUFFER2 MSB changes
- // from 0xFF to 0x00, which slot 0 causes to happen by shifting
- // out/in on SD2 the 0x00 that is always referenced by slot 5.
- while ((RR7146(P_FB_BUFFER2) & 0xFF000000) != 0) ;
+ /* The trap was set on time and we are still executing somewhere
+ * in slots 2-5, so we now wait for slot 0 to execute and trap
+ * TSL execution. This is detected when FB_BUFFER2 MSB changes
+ * from 0xFF to 0x00, which slot 0 causes to happen by shifting
+ * out/in on SD2 the 0x00 that is always referenced by slot 5.
+ */
+ while ((RR7146(P_FB_BUFFER2) & 0xFF000000) != 0)
+ ;
}
- // Either (1) we were too late setting the slot 0 trap; the TSL
- // sequencer restarted slot 0 before we could set the EOS trap flag,
- // or (2) we were not late and execution is now trapped at slot 0.
- // In either case, we must now change slot 0 so that it will store
- // value 0xFF (instead of 0x00) to FB_BUFFER2 next time it executes.
- // In order to do this, we reprogram slot 0 so that it will shift in
- // SD3, which is driven only by a pull-up resistor.
+ /* Either (1) we were too late setting the slot 0 trap; the TSL
+ * sequencer restarted slot 0 before we could set the EOS trap flag,
+ * or (2) we were not late and execution is now trapped at slot 0.
+ * In either case, we must now change slot 0 so that it will store
+ * value 0xFF (instead of 0x00) to FB_BUFFER2 next time it executes.
+ * In order to do this, we reprogram slot 0 so that it will shift in
+ * SD3, which is driven only by a pull-up resistor.
+ */
SETVECT(0, RSD3 | SIB_A2 | EOS);
- // Wait for slot 0 to execute, at which time the TSL is setup for
- // the next DAC write. This is detected when FB_BUFFER2 MSB changes
- // from 0x00 to 0xFF.
- while ((RR7146(P_FB_BUFFER2) & 0xFF000000) == 0) ;
+ /* Wait for slot 0 to execute, at which time the TSL is setup for
+ * the next DAC write. This is detected when FB_BUFFER2 MSB changes
+ * from 0x00 to 0xFF.
+ */
+ while ((RR7146(P_FB_BUFFER2) & 0xFF000000) == 0)
+ ;
}
-static void WriteMISC2(comedi_device * dev, uint16_t NewImage)
+static void WriteMISC2(struct comedi_device *dev, uint16_t NewImage)
{
- DEBIwrite(dev, LP_MISC1, MISC1_WENABLE); // enab writes to
- // MISC2 register.
- DEBIwrite(dev, LP_WRMISC2, NewImage); // Write new image to MISC2.
- DEBIwrite(dev, LP_MISC1, MISC1_WDISABLE); // Disable writes to MISC2.
+ DEBIwrite(dev, LP_MISC1, MISC1_WENABLE); /* enab writes to */
+ /* MISC2 register. */
+ DEBIwrite(dev, LP_WRMISC2, NewImage); /* Write new image to MISC2. */
+ DEBIwrite(dev, LP_MISC1, MISC1_WDISABLE); /* Disable writes to MISC2. */
}
-/////////////////////////////////////////////////////////////////////
-// Initialize the DEBI interface for all transfers.
+/* Initialize the DEBI interface for all transfers. */
-static uint16_t DEBIread(comedi_device * dev, uint16_t addr)
+static uint16_t DEBIread(struct comedi_device *dev, uint16_t addr)
{
uint16_t retval;
- // Set up DEBI control register value in shadow RAM.
+ /* Set up DEBI control register value in shadow RAM. */
WR7146(P_DEBICMD, DEBI_CMD_RDWORD | addr);
- // Execute the DEBI transfer.
+ /* Execute the DEBI transfer. */
DEBItransfer(dev);
- // Fetch target register value.
+ /* Fetch target register value. */
retval = (uint16_t) RR7146(P_DEBIAD);
- // Return register value.
+ /* Return register value. */
return retval;
}
-// Execute a DEBI transfer. This must be called from within a
-// critical section.
-static void DEBItransfer(comedi_device * dev)
+/* Execute a DEBI transfer. This must be called from within a */
+/* critical section. */
+static void DEBItransfer(struct comedi_device *dev)
{
- // Initiate upload of shadow RAM to DEBI control register.
+ /* Initiate upload of shadow RAM to DEBI control register. */
MC_ENABLE(P_MC2, MC2_UPLD_DEBI);
- // Wait for completion of upload from shadow RAM to DEBI control
- // register.
- while (!MC_TEST(P_MC2, MC2_UPLD_DEBI)) ;
+ /* Wait for completion of upload from shadow RAM to DEBI control */
+ /* register. */
+ while (!MC_TEST(P_MC2, MC2_UPLD_DEBI))
+ ;
- // Wait until DEBI transfer is done.
- while (RR7146(P_PSR) & PSR_DEBI_S) ;
+ /* Wait until DEBI transfer is done. */
+ while (RR7146(P_PSR) & PSR_DEBI_S)
+ ;
}
-// Write a value to a gate array register.
-static void DEBIwrite(comedi_device * dev, uint16_t addr, uint16_t wdata)
+/* Write a value to a gate array register. */
+static void DEBIwrite(struct comedi_device *dev, uint16_t addr, uint16_t wdata)
{
- // Set up DEBI control register value in shadow RAM.
+ /* Set up DEBI control register value in shadow RAM. */
WR7146(P_DEBICMD, DEBI_CMD_WRWORD | addr);
WR7146(P_DEBIAD, wdata);
- // Execute the DEBI transfer.
+ /* Execute the DEBI transfer. */
DEBItransfer(dev);
}
-/////////////////////////////////////////////////////////////////////////////
-// Replace the specified bits in a gate array register. Imports: mask
-// specifies bits that are to be preserved, wdata is new value to be
-// or'd with the masked original.
-static void DEBIreplace(comedi_device * dev, uint16_t addr, uint16_t mask,
+/* Replace the specified bits in a gate array register. Imports: mask
+ * specifies bits that are to be preserved, wdata is new value to be
+ * or'd with the masked original.
+ */
+static void DEBIreplace(struct comedi_device *dev, uint16_t addr, uint16_t mask,
uint16_t wdata)
{
- // Copy target gate array register into P_DEBIAD register.
- WR7146(P_DEBICMD, DEBI_CMD_RDWORD | addr); // Set up DEBI control
- // reg value in shadow
- // RAM.
- DEBItransfer(dev); // Execute the DEBI
- // Read transfer.
+ /* Copy target gate array register into P_DEBIAD register. */
+ WR7146(P_DEBICMD, DEBI_CMD_RDWORD | addr);
+ /* Set up DEBI control reg value in shadow RAM. */
+ DEBItransfer(dev); /* Execute the DEBI Read transfer. */
- // Write back the modified image.
- WR7146(P_DEBICMD, DEBI_CMD_WRWORD | addr); // Set up DEBI control
- // reg value in shadow
- // RAM.
+ /* Write back the modified image. */
+ WR7146(P_DEBICMD, DEBI_CMD_WRWORD | addr);
+ /* Set up DEBI control reg value in shadow RAM. */
- WR7146(P_DEBIAD, wdata | ((uint16_t) RR7146(P_DEBIAD) & mask)); // Modify the register image.
- DEBItransfer(dev); // Execute the DEBI Write transfer.
+ WR7146(P_DEBIAD, wdata | ((uint16_t) RR7146(P_DEBIAD) & mask));
+ /* Modify the register image. */
+ DEBItransfer(dev); /* Execute the DEBI Write transfer. */
}
-static void CloseDMAB(comedi_device * dev, DMABUF * pdma, size_t bsize)
+static void CloseDMAB(struct comedi_device *dev, struct bufferDMA *pdma, size_t bsize)
{
void *vbptr;
dma_addr_t vpptr;
@@ -2723,7 +2741,7 @@ static void CloseDMAB(comedi_device * dev, DMABUF * pdma, size_t bsize)
DEBUG("CloseDMAB: Entering S626DRV_CloseDMAB():\n");
if (pdma == NULL)
return;
- //find the matching allocation from the board struct
+ /* find the matching allocation from the board struct */
vbptr = pdma->LogicalBase;
vpptr = pdma->PhysicalBase;
@@ -2737,268 +2755,260 @@ static void CloseDMAB(comedi_device * dev, DMABUF * pdma, size_t bsize)
}
}
-////////////////////////////////////////////////////////////////////////
-///////////////// COUNTER FUNCTIONS //////////////////////////////////
-////////////////////////////////////////////////////////////////////////
-// All counter functions address a specific counter by means of the
-// "Counter" argument, which is a logical counter number. The Counter
-// argument may have any of the following legal values: 0=0A, 1=1A,
-// 2=2A, 3=0B, 4=1B, 5=2B.
-////////////////////////////////////////////////////////////////////////
+/* ****** COUNTER FUNCTIONS ******* */
+/* All counter functions address a specific counter by means of the
+ * "Counter" argument, which is a logical counter number. The Counter
+ * argument may have any of the following legal values: 0=0A, 1=1A,
+ * 2=2A, 3=0B, 4=1B, 5=2B.
+ */
-// Forward declarations for functions that are common to both A and B
-// counters:
+/* Forward declarations for functions that are common to both A and B counters: */
-/////////////////////////////////////////////////////////////////////
-//////////////////// PRIVATE COUNTER FUNCTIONS /////////////////////
-/////////////////////////////////////////////////////////////////////
+/* ****** PRIVATE COUNTER FUNCTIONS ****** */
-/////////////////////////////////////////////////////////////////
-// Read a counter's output latch.
+/* Read a counter's output latch. */
-static uint32_t ReadLatch(comedi_device * dev, enc_private * k)
+static uint32_t ReadLatch(struct comedi_device *dev, struct enc_private *k)
{
register uint32_t value;
- //DEBUG FIXME DEBUG("ReadLatch: Read Latch enter\n");
+ /* DEBUG FIXME DEBUG("ReadLatch: Read Latch enter\n"); */
- // Latch counts and fetch LSW of latched counts value.
+ /* Latch counts and fetch LSW of latched counts value. */
value = (uint32_t) DEBIread(dev, k->MyLatchLsw);
- // Fetch MSW of latched counts and combine with LSW.
+ /* Fetch MSW of latched counts and combine with LSW. */
value |= ((uint32_t) DEBIread(dev, k->MyLatchLsw + 2) << 16);
- // DEBUG FIXME DEBUG("ReadLatch: Read Latch exit\n");
+ /* DEBUG FIXME DEBUG("ReadLatch: Read Latch exit\n"); */
- // Return latched counts.
+ /* Return latched counts. */
return value;
}
-///////////////////////////////////////////////////////////////////
-// Reset a counter's index and overflow event capture flags.
+/* Reset a counter's index and overflow event capture flags. */
-static void ResetCapFlags_A(comedi_device * dev, enc_private * k)
+static void ResetCapFlags_A(struct comedi_device *dev, struct enc_private *k)
{
DEBIreplace(dev, k->MyCRB, (uint16_t) (~CRBMSK_INTCTRL),
CRBMSK_INTRESETCMD | CRBMSK_INTRESET_A);
}
-static void ResetCapFlags_B(comedi_device * dev, enc_private * k)
+static void ResetCapFlags_B(struct comedi_device *dev, struct enc_private *k)
{
DEBIreplace(dev, k->MyCRB, (uint16_t) (~CRBMSK_INTCTRL),
CRBMSK_INTRESETCMD | CRBMSK_INTRESET_B);
}
-/////////////////////////////////////////////////////////////////////////
-// Return counter setup in a format (COUNTER_SETUP) that is consistent
-// for both A and B counters.
+/* Return counter setup in a format (COUNTER_SETUP) that is consistent */
+/* for both A and B counters. */
-static uint16_t GetMode_A(comedi_device * dev, enc_private * k)
+static uint16_t GetMode_A(struct comedi_device *dev, struct enc_private *k)
{
register uint16_t cra;
register uint16_t crb;
register uint16_t setup;
- // Fetch CRA and CRB register images.
+ /* Fetch CRA and CRB register images. */
cra = DEBIread(dev, k->MyCRA);
crb = DEBIread(dev, k->MyCRB);
- // Populate the standardized counter setup bit fields. Note:
- // IndexSrc is restricted to ENC_X or IndxPol.
- setup = ((cra & STDMSK_LOADSRC) // LoadSrc = LoadSrcA.
- | ((crb << (STDBIT_LATCHSRC - CRBBIT_LATCHSRC)) & STDMSK_LATCHSRC) // LatchSrc = LatchSrcA.
- | ((cra << (STDBIT_INTSRC - CRABIT_INTSRC_A)) & STDMSK_INTSRC) // IntSrc = IntSrcA.
- | ((cra << (STDBIT_INDXSRC - (CRABIT_INDXSRC_A + 1))) & STDMSK_INDXSRC) // IndxSrc = IndxSrcA<1>.
- | ((cra >> (CRABIT_INDXPOL_A - STDBIT_INDXPOL)) & STDMSK_INDXPOL) // IndxPol = IndxPolA.
- | ((crb >> (CRBBIT_CLKENAB_A - STDBIT_CLKENAB)) & STDMSK_CLKENAB)); // ClkEnab = ClkEnabA.
-
- // Adjust mode-dependent parameters.
- if (cra & (2 << CRABIT_CLKSRC_A)) // If Timer mode (ClkSrcA<1> == 1):
- setup |= ((CLKSRC_TIMER << STDBIT_CLKSRC) // Indicate Timer mode.
- | ((cra << (STDBIT_CLKPOL - CRABIT_CLKSRC_A)) & STDMSK_CLKPOL) // Set ClkPol to indicate count direction (ClkSrcA<0>).
- | (MULT_X1 << STDBIT_CLKMULT)); // ClkMult must be 1x in Timer mode.
-
- else // If Counter mode (ClkSrcA<1> == 0):
- setup |= ((CLKSRC_COUNTER << STDBIT_CLKSRC) // Indicate Counter mode.
- | ((cra >> (CRABIT_CLKPOL_A - STDBIT_CLKPOL)) & STDMSK_CLKPOL) // Pass through ClkPol.
- | (((cra & CRAMSK_CLKMULT_A) == (MULT_X0 << CRABIT_CLKMULT_A)) ? // Force ClkMult to 1x if not legal, else pass through.
+ /* Populate the standardized counter setup bit fields. Note: */
+ /* IndexSrc is restricted to ENC_X or IndxPol. */
+ setup = ((cra & STDMSK_LOADSRC) /* LoadSrc = LoadSrcA. */
+ | ((crb << (STDBIT_LATCHSRC - CRBBIT_LATCHSRC)) & STDMSK_LATCHSRC) /* LatchSrc = LatchSrcA. */
+ | ((cra << (STDBIT_INTSRC - CRABIT_INTSRC_A)) & STDMSK_INTSRC) /* IntSrc = IntSrcA. */
+ | ((cra << (STDBIT_INDXSRC - (CRABIT_INDXSRC_A + 1))) & STDMSK_INDXSRC) /* IndxSrc = IndxSrcA<1>. */
+ | ((cra >> (CRABIT_INDXPOL_A - STDBIT_INDXPOL)) & STDMSK_INDXPOL) /* IndxPol = IndxPolA. */
+ | ((crb >> (CRBBIT_CLKENAB_A - STDBIT_CLKENAB)) & STDMSK_CLKENAB)); /* ClkEnab = ClkEnabA. */
+
+ /* Adjust mode-dependent parameters. */
+ if (cra & (2 << CRABIT_CLKSRC_A)) /* If Timer mode (ClkSrcA<1> == 1): */
+ setup |= ((CLKSRC_TIMER << STDBIT_CLKSRC) /* Indicate Timer mode. */
+ | ((cra << (STDBIT_CLKPOL - CRABIT_CLKSRC_A)) & STDMSK_CLKPOL) /* Set ClkPol to indicate count direction (ClkSrcA<0>). */
+ | (MULT_X1 << STDBIT_CLKMULT)); /* ClkMult must be 1x in Timer mode. */
+
+ else /* If Counter mode (ClkSrcA<1> == 0): */
+ setup |= ((CLKSRC_COUNTER << STDBIT_CLKSRC) /* Indicate Counter mode. */
+ | ((cra >> (CRABIT_CLKPOL_A - STDBIT_CLKPOL)) & STDMSK_CLKPOL) /* Pass through ClkPol. */
+ | (((cra & CRAMSK_CLKMULT_A) == (MULT_X0 << CRABIT_CLKMULT_A)) ? /* Force ClkMult to 1x if not legal, else pass through. */
(MULT_X1 << STDBIT_CLKMULT) :
((cra >> (CRABIT_CLKMULT_A -
STDBIT_CLKMULT)) &
STDMSK_CLKMULT)));
- // Return adjusted counter setup.
+ /* Return adjusted counter setup. */
return setup;
}
-static uint16_t GetMode_B(comedi_device * dev, enc_private * k)
+static uint16_t GetMode_B(struct comedi_device *dev, struct enc_private *k)
{
register uint16_t cra;
register uint16_t crb;
register uint16_t setup;
- // Fetch CRA and CRB register images.
+ /* Fetch CRA and CRB register images. */
cra = DEBIread(dev, k->MyCRA);
crb = DEBIread(dev, k->MyCRB);
- // Populate the standardized counter setup bit fields. Note:
- // IndexSrc is restricted to ENC_X or IndxPol.
- setup = (((crb << (STDBIT_INTSRC - CRBBIT_INTSRC_B)) & STDMSK_INTSRC) // IntSrc = IntSrcB.
- | ((crb << (STDBIT_LATCHSRC - CRBBIT_LATCHSRC)) & STDMSK_LATCHSRC) // LatchSrc = LatchSrcB.
- | ((crb << (STDBIT_LOADSRC - CRBBIT_LOADSRC_B)) & STDMSK_LOADSRC) // LoadSrc = LoadSrcB.
- | ((crb << (STDBIT_INDXPOL - CRBBIT_INDXPOL_B)) & STDMSK_INDXPOL) // IndxPol = IndxPolB.
- | ((crb >> (CRBBIT_CLKENAB_B - STDBIT_CLKENAB)) & STDMSK_CLKENAB) // ClkEnab = ClkEnabB.
- | ((cra >> ((CRABIT_INDXSRC_B + 1) - STDBIT_INDXSRC)) & STDMSK_INDXSRC)); // IndxSrc = IndxSrcB<1>.
-
- // Adjust mode-dependent parameters.
- if ((crb & CRBMSK_CLKMULT_B) == (MULT_X0 << CRBBIT_CLKMULT_B)) // If Extender mode (ClkMultB == MULT_X0):
- setup |= ((CLKSRC_EXTENDER << STDBIT_CLKSRC) // Indicate Extender mode.
- | (MULT_X1 << STDBIT_CLKMULT) // Indicate multiplier is 1x.
- | ((cra >> (CRABIT_CLKSRC_B - STDBIT_CLKPOL)) & STDMSK_CLKPOL)); // Set ClkPol equal to Timer count direction (ClkSrcB<0>).
-
- else if (cra & (2 << CRABIT_CLKSRC_B)) // If Timer mode (ClkSrcB<1> == 1):
- setup |= ((CLKSRC_TIMER << STDBIT_CLKSRC) // Indicate Timer mode.
- | (MULT_X1 << STDBIT_CLKMULT) // Indicate multiplier is 1x.
- | ((cra >> (CRABIT_CLKSRC_B - STDBIT_CLKPOL)) & STDMSK_CLKPOL)); // Set ClkPol equal to Timer count direction (ClkSrcB<0>).
-
- else // If Counter mode (ClkSrcB<1> == 0):
- setup |= ((CLKSRC_COUNTER << STDBIT_CLKSRC) // Indicate Timer mode.
- | ((crb >> (CRBBIT_CLKMULT_B - STDBIT_CLKMULT)) & STDMSK_CLKMULT) // Clock multiplier is passed through.
- | ((crb << (STDBIT_CLKPOL - CRBBIT_CLKPOL_B)) & STDMSK_CLKPOL)); // Clock polarity is passed through.
-
- // Return adjusted counter setup.
+ /* Populate the standardized counter setup bit fields. Note: */
+ /* IndexSrc is restricted to ENC_X or IndxPol. */
+ setup = (((crb << (STDBIT_INTSRC - CRBBIT_INTSRC_B)) & STDMSK_INTSRC) /* IntSrc = IntSrcB. */
+ | ((crb << (STDBIT_LATCHSRC - CRBBIT_LATCHSRC)) & STDMSK_LATCHSRC) /* LatchSrc = LatchSrcB. */
+ | ((crb << (STDBIT_LOADSRC - CRBBIT_LOADSRC_B)) & STDMSK_LOADSRC) /* LoadSrc = LoadSrcB. */
+ | ((crb << (STDBIT_INDXPOL - CRBBIT_INDXPOL_B)) & STDMSK_INDXPOL) /* IndxPol = IndxPolB. */
+ | ((crb >> (CRBBIT_CLKENAB_B - STDBIT_CLKENAB)) & STDMSK_CLKENAB) /* ClkEnab = ClkEnabB. */
+ | ((cra >> ((CRABIT_INDXSRC_B + 1) - STDBIT_INDXSRC)) & STDMSK_INDXSRC)); /* IndxSrc = IndxSrcB<1>. */
+
+ /* Adjust mode-dependent parameters. */
+ if ((crb & CRBMSK_CLKMULT_B) == (MULT_X0 << CRBBIT_CLKMULT_B)) /* If Extender mode (ClkMultB == MULT_X0): */
+ setup |= ((CLKSRC_EXTENDER << STDBIT_CLKSRC) /* Indicate Extender mode. */
+ | (MULT_X1 << STDBIT_CLKMULT) /* Indicate multiplier is 1x. */
+ | ((cra >> (CRABIT_CLKSRC_B - STDBIT_CLKPOL)) & STDMSK_CLKPOL)); /* Set ClkPol equal to Timer count direction (ClkSrcB<0>). */
+
+ else if (cra & (2 << CRABIT_CLKSRC_B)) /* If Timer mode (ClkSrcB<1> == 1): */
+ setup |= ((CLKSRC_TIMER << STDBIT_CLKSRC) /* Indicate Timer mode. */
+ | (MULT_X1 << STDBIT_CLKMULT) /* Indicate multiplier is 1x. */
+ | ((cra >> (CRABIT_CLKSRC_B - STDBIT_CLKPOL)) & STDMSK_CLKPOL)); /* Set ClkPol equal to Timer count direction (ClkSrcB<0>). */
+
+ else /* If Counter mode (ClkSrcB<1> == 0): */
+ setup |= ((CLKSRC_COUNTER << STDBIT_CLKSRC) /* Indicate Timer mode. */
+ | ((crb >> (CRBBIT_CLKMULT_B - STDBIT_CLKMULT)) & STDMSK_CLKMULT) /* Clock multiplier is passed through. */
+ | ((crb << (STDBIT_CLKPOL - CRBBIT_CLKPOL_B)) & STDMSK_CLKPOL)); /* Clock polarity is passed through. */
+
+ /* Return adjusted counter setup. */
return setup;
}
-/////////////////////////////////////////////////////////////////////////////////////////////
-// Set the operating mode for the specified counter. The setup
-// parameter is treated as a COUNTER_SETUP data type. The following
-// parameters are programmable (all other parms are ignored): ClkMult,
-// ClkPol, ClkEnab, IndexSrc, IndexPol, LoadSrc.
+/*
+ * Set the operating mode for the specified counter. The setup
+ * parameter is treated as a COUNTER_SETUP data type. The following
+ * parameters are programmable (all other parms are ignored): ClkMult,
+ * ClkPol, ClkEnab, IndexSrc, IndexPol, LoadSrc.
+ */
-static void SetMode_A(comedi_device * dev, enc_private * k, uint16_t Setup,
+static void SetMode_A(struct comedi_device *dev, struct enc_private *k, uint16_t Setup,
uint16_t DisableIntSrc)
{
register uint16_t cra;
register uint16_t crb;
- register uint16_t setup = Setup; // Cache the Standard Setup.
+ register uint16_t setup = Setup; /* Cache the Standard Setup. */
- // Initialize CRA and CRB images.
- cra = ((setup & CRAMSK_LOADSRC_A) // Preload trigger is passed through.
- | ((setup & STDMSK_INDXSRC) >> (STDBIT_INDXSRC - (CRABIT_INDXSRC_A + 1)))); // IndexSrc is restricted to ENC_X or IndxPol.
+ /* Initialize CRA and CRB images. */
+ cra = ((setup & CRAMSK_LOADSRC_A) /* Preload trigger is passed through. */
+ | ((setup & STDMSK_INDXSRC) >> (STDBIT_INDXSRC - (CRABIT_INDXSRC_A + 1)))); /* IndexSrc is restricted to ENC_X or IndxPol. */
- crb = (CRBMSK_INTRESETCMD | CRBMSK_INTRESET_A // Reset any pending CounterA event captures.
- | ((setup & STDMSK_CLKENAB) << (CRBBIT_CLKENAB_A - STDBIT_CLKENAB))); // Clock enable is passed through.
+ crb = (CRBMSK_INTRESETCMD | CRBMSK_INTRESET_A /* Reset any pending CounterA event captures. */
+ | ((setup & STDMSK_CLKENAB) << (CRBBIT_CLKENAB_A - STDBIT_CLKENAB))); /* Clock enable is passed through. */
- // Force IntSrc to Disabled if DisableIntSrc is asserted.
+ /* Force IntSrc to Disabled if DisableIntSrc is asserted. */
if (!DisableIntSrc)
cra |= ((setup & STDMSK_INTSRC) >> (STDBIT_INTSRC -
CRABIT_INTSRC_A));
- // Populate all mode-dependent attributes of CRA & CRB images.
+ /* Populate all mode-dependent attributes of CRA & CRB images. */
switch ((setup & STDMSK_CLKSRC) >> STDBIT_CLKSRC) {
- case CLKSRC_EXTENDER: // Extender Mode: Force to Timer mode
- // (Extender valid only for B counters).
-
- case CLKSRC_TIMER: // Timer Mode:
- cra |= ((2 << CRABIT_CLKSRC_A) // ClkSrcA<1> selects system clock
- | ((setup & STDMSK_CLKPOL) >> (STDBIT_CLKPOL - CRABIT_CLKSRC_A)) // with count direction (ClkSrcA<0>) obtained from ClkPol.
- | (1 << CRABIT_CLKPOL_A) // ClkPolA behaves as always-on clock enable.
- | (MULT_X1 << CRABIT_CLKMULT_A)); // ClkMult must be 1x.
+ case CLKSRC_EXTENDER: /* Extender Mode: Force to Timer mode */
+ /* (Extender valid only for B counters). */
+
+ case CLKSRC_TIMER: /* Timer Mode: */
+ cra |= ((2 << CRABIT_CLKSRC_A) /* ClkSrcA<1> selects system clock */
+ | ((setup & STDMSK_CLKPOL) >> (STDBIT_CLKPOL - CRABIT_CLKSRC_A)) /* with count direction (ClkSrcA<0>) obtained from ClkPol. */
+ | (1 << CRABIT_CLKPOL_A) /* ClkPolA behaves as always-on clock enable. */
+ | (MULT_X1 << CRABIT_CLKMULT_A)); /* ClkMult must be 1x. */
break;
- default: // Counter Mode:
- cra |= (CLKSRC_COUNTER // Select ENC_C and ENC_D as clock/direction inputs.
- | ((setup & STDMSK_CLKPOL) << (CRABIT_CLKPOL_A - STDBIT_CLKPOL)) // Clock polarity is passed through.
- | (((setup & STDMSK_CLKMULT) == (MULT_X0 << STDBIT_CLKMULT)) ? // Force multiplier to x1 if not legal, otherwise pass through.
+ default: /* Counter Mode: */
+ cra |= (CLKSRC_COUNTER /* Select ENC_C and ENC_D as clock/direction inputs. */
+ | ((setup & STDMSK_CLKPOL) << (CRABIT_CLKPOL_A - STDBIT_CLKPOL)) /* Clock polarity is passed through. */
+ | (((setup & STDMSK_CLKMULT) == (MULT_X0 << STDBIT_CLKMULT)) ? /* Force multiplier to x1 if not legal, otherwise pass through. */
(MULT_X1 << CRABIT_CLKMULT_A) :
((setup & STDMSK_CLKMULT) << (CRABIT_CLKMULT_A -
STDBIT_CLKMULT))));
}
- // Force positive index polarity if IndxSrc is software-driven only,
- // otherwise pass it through.
+ /* Force positive index polarity if IndxSrc is software-driven only, */
+ /* otherwise pass it through. */
if (~setup & STDMSK_INDXSRC)
cra |= ((setup & STDMSK_INDXPOL) << (CRABIT_INDXPOL_A -
STDBIT_INDXPOL));
- // If IntSrc has been forced to Disabled, update the MISC2 interrupt
- // enable mask to indicate the counter interrupt is disabled.
+ /* If IntSrc has been forced to Disabled, update the MISC2 interrupt */
+ /* enable mask to indicate the counter interrupt is disabled. */
if (DisableIntSrc)
devpriv->CounterIntEnabs &= ~k->MyEventBits[3];
- // While retaining CounterB and LatchSrc configurations, program the
- // new counter operating mode.
+ /* While retaining CounterB and LatchSrc configurations, program the */
+ /* new counter operating mode. */
DEBIreplace(dev, k->MyCRA, CRAMSK_INDXSRC_B | CRAMSK_CLKSRC_B, cra);
DEBIreplace(dev, k->MyCRB,
(uint16_t) (~(CRBMSK_INTCTRL | CRBMSK_CLKENAB_A)), crb);
}
-static void SetMode_B(comedi_device * dev, enc_private * k, uint16_t Setup,
+static void SetMode_B(struct comedi_device *dev, struct enc_private *k, uint16_t Setup,
uint16_t DisableIntSrc)
{
register uint16_t cra;
register uint16_t crb;
- register uint16_t setup = Setup; // Cache the Standard Setup.
+ register uint16_t setup = Setup; /* Cache the Standard Setup. */
- // Initialize CRA and CRB images.
- cra = ((setup & STDMSK_INDXSRC) << ((CRABIT_INDXSRC_B + 1) - STDBIT_INDXSRC)); // IndexSrc field is restricted to ENC_X or IndxPol.
+ /* Initialize CRA and CRB images. */
+ cra = ((setup & STDMSK_INDXSRC) << ((CRABIT_INDXSRC_B + 1) - STDBIT_INDXSRC)); /* IndexSrc field is restricted to ENC_X or IndxPol. */
- crb = (CRBMSK_INTRESETCMD | CRBMSK_INTRESET_B // Reset event captures and disable interrupts.
- | ((setup & STDMSK_CLKENAB) << (CRBBIT_CLKENAB_B - STDBIT_CLKENAB)) // Clock enable is passed through.
- | ((setup & STDMSK_LOADSRC) >> (STDBIT_LOADSRC - CRBBIT_LOADSRC_B))); // Preload trigger source is passed through.
+ crb = (CRBMSK_INTRESETCMD | CRBMSK_INTRESET_B /* Reset event captures and disable interrupts. */
+ | ((setup & STDMSK_CLKENAB) << (CRBBIT_CLKENAB_B - STDBIT_CLKENAB)) /* Clock enable is passed through. */
+ | ((setup & STDMSK_LOADSRC) >> (STDBIT_LOADSRC - CRBBIT_LOADSRC_B))); /* Preload trigger source is passed through. */
- // Force IntSrc to Disabled if DisableIntSrc is asserted.
+ /* Force IntSrc to Disabled if DisableIntSrc is asserted. */
if (!DisableIntSrc)
crb |= ((setup & STDMSK_INTSRC) >> (STDBIT_INTSRC -
CRBBIT_INTSRC_B));
- // Populate all mode-dependent attributes of CRA & CRB images.
+ /* Populate all mode-dependent attributes of CRA & CRB images. */
switch ((setup & STDMSK_CLKSRC) >> STDBIT_CLKSRC) {
- case CLKSRC_TIMER: // Timer Mode:
- cra |= ((2 << CRABIT_CLKSRC_B) // ClkSrcB<1> selects system clock
- | ((setup & STDMSK_CLKPOL) << (CRABIT_CLKSRC_B - STDBIT_CLKPOL))); // with direction (ClkSrcB<0>) obtained from ClkPol.
- crb |= ((1 << CRBBIT_CLKPOL_B) // ClkPolB behaves as always-on clock enable.
- | (MULT_X1 << CRBBIT_CLKMULT_B)); // ClkMultB must be 1x.
+ case CLKSRC_TIMER: /* Timer Mode: */
+ cra |= ((2 << CRABIT_CLKSRC_B) /* ClkSrcB<1> selects system clock */
+ | ((setup & STDMSK_CLKPOL) << (CRABIT_CLKSRC_B - STDBIT_CLKPOL))); /* with direction (ClkSrcB<0>) obtained from ClkPol. */
+ crb |= ((1 << CRBBIT_CLKPOL_B) /* ClkPolB behaves as always-on clock enable. */
+ | (MULT_X1 << CRBBIT_CLKMULT_B)); /* ClkMultB must be 1x. */
break;
- case CLKSRC_EXTENDER: // Extender Mode:
- cra |= ((2 << CRABIT_CLKSRC_B) // ClkSrcB source is OverflowA (same as "timer")
- | ((setup & STDMSK_CLKPOL) << (CRABIT_CLKSRC_B - STDBIT_CLKPOL))); // with direction obtained from ClkPol.
- crb |= ((1 << CRBBIT_CLKPOL_B) // ClkPolB controls IndexB -- always set to active.
- | (MULT_X0 << CRBBIT_CLKMULT_B)); // ClkMultB selects OverflowA as the clock source.
+ case CLKSRC_EXTENDER: /* Extender Mode: */
+ cra |= ((2 << CRABIT_CLKSRC_B) /* ClkSrcB source is OverflowA (same as "timer") */
+ | ((setup & STDMSK_CLKPOL) << (CRABIT_CLKSRC_B - STDBIT_CLKPOL))); /* with direction obtained from ClkPol. */
+ crb |= ((1 << CRBBIT_CLKPOL_B) /* ClkPolB controls IndexB -- always set to active. */
+ | (MULT_X0 << CRBBIT_CLKMULT_B)); /* ClkMultB selects OverflowA as the clock source. */
break;
- default: // Counter Mode:
- cra |= (CLKSRC_COUNTER << CRABIT_CLKSRC_B); // Select ENC_C and ENC_D as clock/direction inputs.
- crb |= (((setup & STDMSK_CLKPOL) >> (STDBIT_CLKPOL - CRBBIT_CLKPOL_B)) // ClkPol is passed through.
- | (((setup & STDMSK_CLKMULT) == (MULT_X0 << STDBIT_CLKMULT)) ? // Force ClkMult to x1 if not legal, otherwise pass through.
+ default: /* Counter Mode: */
+ cra |= (CLKSRC_COUNTER << CRABIT_CLKSRC_B); /* Select ENC_C and ENC_D as clock/direction inputs. */
+ crb |= (((setup & STDMSK_CLKPOL) >> (STDBIT_CLKPOL - CRBBIT_CLKPOL_B)) /* ClkPol is passed through. */
+ | (((setup & STDMSK_CLKMULT) == (MULT_X0 << STDBIT_CLKMULT)) ? /* Force ClkMult to x1 if not legal, otherwise pass through. */
(MULT_X1 << CRBBIT_CLKMULT_B) :
((setup & STDMSK_CLKMULT) << (CRBBIT_CLKMULT_B -
STDBIT_CLKMULT))));
}
- // Force positive index polarity if IndxSrc is software-driven only,
- // otherwise pass it through.
+ /* Force positive index polarity if IndxSrc is software-driven only, */
+ /* otherwise pass it through. */
if (~setup & STDMSK_INDXSRC)
crb |= ((setup & STDMSK_INDXPOL) >> (STDBIT_INDXPOL -
CRBBIT_INDXPOL_B));
- // If IntSrc has been forced to Disabled, update the MISC2 interrupt
- // enable mask to indicate the counter interrupt is disabled.
+ /* If IntSrc has been forced to Disabled, update the MISC2 interrupt */
+ /* enable mask to indicate the counter interrupt is disabled. */
if (DisableIntSrc)
devpriv->CounterIntEnabs &= ~k->MyEventBits[3];
- // While retaining CounterA and LatchSrc configurations, program the
- // new counter operating mode.
+ /* While retaining CounterA and LatchSrc configurations, program the */
+ /* new counter operating mode. */
DEBIreplace(dev, k->MyCRA,
(uint16_t) (~(CRAMSK_INDXSRC_B | CRAMSK_CLKSRC_B)), cra);
DEBIreplace(dev, k->MyCRB, CRBMSK_CLKENAB_A | CRBMSK_LATCHSRC, crb);
}
-////////////////////////////////////////////////////////////////////////
-// Return/set a counter's enable. enab: 0=always enabled, 1=enabled by index.
+/* Return/set a counter's enable. enab: 0=always enabled, 1=enabled by index. */
-static void SetEnable_A(comedi_device * dev, enc_private * k, uint16_t enab)
+static void SetEnable_A(struct comedi_device *dev, struct enc_private *k, uint16_t enab)
{
DEBUG("SetEnable_A: SetEnable_A enter 3541\n");
DEBIreplace(dev, k->MyCRB,
@@ -3006,29 +3016,29 @@ static void SetEnable_A(comedi_device * dev, enc_private * k, uint16_t enab)
(uint16_t) (enab << CRBBIT_CLKENAB_A));
}
-static void SetEnable_B(comedi_device * dev, enc_private * k, uint16_t enab)
+static void SetEnable_B(struct comedi_device *dev, struct enc_private *k, uint16_t enab)
{
DEBIreplace(dev, k->MyCRB,
(uint16_t) (~(CRBMSK_INTCTRL | CRBMSK_CLKENAB_B)),
(uint16_t) (enab << CRBBIT_CLKENAB_B));
}
-static uint16_t GetEnable_A(comedi_device * dev, enc_private * k)
+static uint16_t GetEnable_A(struct comedi_device *dev, struct enc_private *k)
{
return (DEBIread(dev, k->MyCRB) >> CRBBIT_CLKENAB_A) & 1;
}
-static uint16_t GetEnable_B(comedi_device * dev, enc_private * k)
+static uint16_t GetEnable_B(struct comedi_device *dev, struct enc_private *k)
{
return (DEBIread(dev, k->MyCRB) >> CRBBIT_CLKENAB_B) & 1;
}
-////////////////////////////////////////////////////////////////////////
-// Return/set a counter pair's latch trigger source. 0: On read
-// access, 1: A index latches A, 2: B index latches B, 3: A overflow
-// latches B.
+/* Return/set a counter pair's latch trigger source. 0: On read
+ * access, 1: A index latches A, 2: B index latches B, 3: A overflow
+ * latches B.
+ */
-static void SetLatchSource(comedi_device * dev, enc_private * k, uint16_t value)
+static void SetLatchSource(struct comedi_device *dev, struct enc_private *k, uint16_t value)
{
DEBUG("SetLatchSource: SetLatchSource enter 3550 \n");
DEBIreplace(dev, k->MyCRB,
@@ -3038,210 +3048,206 @@ static void SetLatchSource(comedi_device * dev, enc_private * k, uint16_t value)
DEBUG("SetLatchSource: SetLatchSource exit \n");
}
-/* static uint16_t GetLatchSource(comedi_device *dev, enc_private *k ) */
-/* { */
-/* return ( DEBIread( dev, k->MyCRB) >> CRBBIT_LATCHSRC ) & 3; */
-/* } */
+/*
+ * static uint16_t GetLatchSource(struct comedi_device *dev, struct enc_private *k )
+ * {
+ * return ( DEBIread( dev, k->MyCRB) >> CRBBIT_LATCHSRC ) & 3;
+ * }
+ */
-/////////////////////////////////////////////////////////////////////////
-// Return/set the event that will trigger transfer of the preload
-// register into the counter. 0=ThisCntr_Index, 1=ThisCntr_Overflow,
-// 2=OverflowA (B counters only), 3=disabled.
+/*
+ * Return/set the event that will trigger transfer of the preload
+ * register into the counter. 0=ThisCntr_Index, 1=ThisCntr_Overflow,
+ * 2=OverflowA (B counters only), 3=disabled.
+ */
-static void SetLoadTrig_A(comedi_device * dev, enc_private * k, uint16_t Trig)
+static void SetLoadTrig_A(struct comedi_device *dev, struct enc_private *k, uint16_t Trig)
{
DEBIreplace(dev, k->MyCRA, (uint16_t) (~CRAMSK_LOADSRC_A),
(uint16_t) (Trig << CRABIT_LOADSRC_A));
}
-static void SetLoadTrig_B(comedi_device * dev, enc_private * k, uint16_t Trig)
+static void SetLoadTrig_B(struct comedi_device *dev, struct enc_private *k, uint16_t Trig)
{
DEBIreplace(dev, k->MyCRB,
(uint16_t) (~(CRBMSK_LOADSRC_B | CRBMSK_INTCTRL)),
(uint16_t) (Trig << CRBBIT_LOADSRC_B));
}
-static uint16_t GetLoadTrig_A(comedi_device * dev, enc_private * k)
+static uint16_t GetLoadTrig_A(struct comedi_device *dev, struct enc_private *k)
{
return (DEBIread(dev, k->MyCRA) >> CRABIT_LOADSRC_A) & 3;
}
-static uint16_t GetLoadTrig_B(comedi_device * dev, enc_private * k)
+static uint16_t GetLoadTrig_B(struct comedi_device *dev, struct enc_private *k)
{
return (DEBIread(dev, k->MyCRB) >> CRBBIT_LOADSRC_B) & 3;
}
-////////////////////
-// Return/set counter interrupt source and clear any captured
-// index/overflow events. IntSource: 0=Disabled, 1=OverflowOnly,
-// 2=IndexOnly, 3=IndexAndOverflow.
+/* Return/set counter interrupt source and clear any captured
+ * index/overflow events. IntSource: 0=Disabled, 1=OverflowOnly,
+ * 2=IndexOnly, 3=IndexAndOverflow.
+ */
-static void SetIntSrc_A(comedi_device * dev, enc_private * k,
+static void SetIntSrc_A(struct comedi_device *dev, struct enc_private *k,
uint16_t IntSource)
{
- // Reset any pending counter overflow or index captures.
+ /* Reset any pending counter overflow or index captures. */
DEBIreplace(dev, k->MyCRB, (uint16_t) (~CRBMSK_INTCTRL),
CRBMSK_INTRESETCMD | CRBMSK_INTRESET_A);
- // Program counter interrupt source.
+ /* Program counter interrupt source. */
DEBIreplace(dev, k->MyCRA, ~CRAMSK_INTSRC_A,
(uint16_t) (IntSource << CRABIT_INTSRC_A));
- // Update MISC2 interrupt enable mask.
+ /* Update MISC2 interrupt enable mask. */
devpriv->CounterIntEnabs =
(devpriv->CounterIntEnabs & ~k->MyEventBits[3]) | k->
MyEventBits[IntSource];
}
-static void SetIntSrc_B(comedi_device * dev, enc_private * k,
+static void SetIntSrc_B(struct comedi_device *dev, struct enc_private *k,
uint16_t IntSource)
{
uint16_t crb;
- // Cache writeable CRB register image.
+ /* Cache writeable CRB register image. */
crb = DEBIread(dev, k->MyCRB) & ~CRBMSK_INTCTRL;
- // Reset any pending counter overflow or index captures.
+ /* Reset any pending counter overflow or index captures. */
DEBIwrite(dev, k->MyCRB,
(uint16_t) (crb | CRBMSK_INTRESETCMD | CRBMSK_INTRESET_B));
- // Program counter interrupt source.
+ /* Program counter interrupt source. */
DEBIwrite(dev, k->MyCRB,
(uint16_t) ((crb & ~CRBMSK_INTSRC_B) | (IntSource <<
CRBBIT_INTSRC_B)));
- // Update MISC2 interrupt enable mask.
+ /* Update MISC2 interrupt enable mask. */
devpriv->CounterIntEnabs =
(devpriv->CounterIntEnabs & ~k->MyEventBits[3]) | k->
MyEventBits[IntSource];
}
-static uint16_t GetIntSrc_A(comedi_device * dev, enc_private * k)
+static uint16_t GetIntSrc_A(struct comedi_device *dev, struct enc_private *k)
{
return (DEBIread(dev, k->MyCRA) >> CRABIT_INTSRC_A) & 3;
}
-static uint16_t GetIntSrc_B(comedi_device * dev, enc_private * k)
+static uint16_t GetIntSrc_B(struct comedi_device *dev, struct enc_private *k)
{
return (DEBIread(dev, k->MyCRB) >> CRBBIT_INTSRC_B) & 3;
}
-/////////////////////////////////////////////////////////////////////////
-// Return/set the clock multiplier.
+/* Return/set the clock multiplier. */
-/* static void SetClkMult(comedi_device *dev, enc_private *k, uint16_t value ) */
+/* static void SetClkMult(struct comedi_device *dev, struct enc_private *k, uint16_t value ) */
/* { */
/* k->SetMode(dev, k, (uint16_t)( ( k->GetMode(dev, k ) & ~STDMSK_CLKMULT ) | ( value << STDBIT_CLKMULT ) ), FALSE ); */
/* } */
-/* static uint16_t GetClkMult(comedi_device *dev, enc_private *k ) */
+/* static uint16_t GetClkMult(struct comedi_device *dev, struct enc_private *k ) */
/* { */
/* return ( k->GetMode(dev, k ) >> STDBIT_CLKMULT ) & 3; */
/* } */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* // Return/set the clock polarity. */
+/* Return/set the clock polarity. */
-/* static void SetClkPol( comedi_device *dev,enc_private *k, uint16_t value ) */
+/* static void SetClkPol( struct comedi_device *dev,struct enc_private *k, uint16_t value ) */
/* { */
/* k->SetMode(dev, k, (uint16_t)( ( k->GetMode(dev, k ) & ~STDMSK_CLKPOL ) | ( value << STDBIT_CLKPOL ) ), FALSE ); */
/* } */
-/* static uint16_t GetClkPol(comedi_device *dev, enc_private *k ) */
+/* static uint16_t GetClkPol(struct comedi_device *dev, struct enc_private *k ) */
/* { */
/* return ( k->GetMode(dev, k ) >> STDBIT_CLKPOL ) & 1; */
/* } */
-/* /////////////////////////////////////////////////////////////////////// */
-/* // Return/set the clock source. */
+/* Return/set the clock source. */
-/* static void SetClkSrc( comedi_device *dev,enc_private *k, uint16_t value ) */
+/* static void SetClkSrc( struct comedi_device *dev,struct enc_private *k, uint16_t value ) */
/* { */
/* k->SetMode(dev, k, (uint16_t)( ( k->GetMode(dev, k ) & ~STDMSK_CLKSRC ) | ( value << STDBIT_CLKSRC ) ), FALSE ); */
/* } */
-/* static uint16_t GetClkSrc( comedi_device *dev,enc_private *k ) */
+/* static uint16_t GetClkSrc( struct comedi_device *dev,struct enc_private *k ) */
/* { */
/* return ( k->GetMode(dev, k ) >> STDBIT_CLKSRC ) & 3; */
/* } */
-/* //////////////////////////////////////////////////////////////////////// */
-/* // Return/set the index polarity. */
+/* Return/set the index polarity. */
-/* static void SetIndexPol(comedi_device *dev, enc_private *k, uint16_t value ) */
+/* static void SetIndexPol(struct comedi_device *dev, struct enc_private *k, uint16_t value ) */
/* { */
/* k->SetMode(dev, k, (uint16_t)( ( k->GetMode(dev, k ) & ~STDMSK_INDXPOL ) | ( (value != 0) << STDBIT_INDXPOL ) ), FALSE ); */
/* } */
-/* static uint16_t GetIndexPol(comedi_device *dev, enc_private *k ) */
+/* static uint16_t GetIndexPol(struct comedi_device *dev, struct enc_private *k ) */
/* { */
/* return ( k->GetMode(dev, k ) >> STDBIT_INDXPOL ) & 1; */
/* } */
-/* //////////////////////////////////////////////////////////////////////// */
-/* // Return/set the index source. */
+/* Return/set the index source. */
-/* static void SetIndexSrc(comedi_device *dev, enc_private *k, uint16_t value ) */
+/* static void SetIndexSrc(struct comedi_device *dev, struct enc_private *k, uint16_t value ) */
/* { */
/* DEBUG("SetIndexSrc: set index src enter 3700\n"); */
/* k->SetMode(dev, k, (uint16_t)( ( k->GetMode(dev, k ) & ~STDMSK_INDXSRC ) | ( (value != 0) << STDBIT_INDXSRC ) ), FALSE ); */
/* } */
-/* static uint16_t GetIndexSrc(comedi_device *dev, enc_private *k ) */
+/* static uint16_t GetIndexSrc(struct comedi_device *dev, struct enc_private *k ) */
/* { */
/* return ( k->GetMode(dev, k ) >> STDBIT_INDXSRC ) & 1; */
/* } */
-///////////////////////////////////////////////////////////////////
-// Generate an index pulse.
+/* Generate an index pulse. */
-static void PulseIndex_A(comedi_device * dev, enc_private * k)
+static void PulseIndex_A(struct comedi_device *dev, struct enc_private *k)
{
register uint16_t cra;
DEBUG("PulseIndex_A: pulse index enter\n");
- cra = DEBIread(dev, k->MyCRA); // Pulse index.
+ cra = DEBIread(dev, k->MyCRA); /* Pulse index. */
DEBIwrite(dev, k->MyCRA, (uint16_t) (cra ^ CRAMSK_INDXPOL_A));
DEBUG("PulseIndex_A: pulse index step1\n");
DEBIwrite(dev, k->MyCRA, cra);
}
-static void PulseIndex_B(comedi_device * dev, enc_private * k)
+static void PulseIndex_B(struct comedi_device *dev, struct enc_private *k)
{
register uint16_t crb;
- crb = DEBIread(dev, k->MyCRB) & ~CRBMSK_INTCTRL; // Pulse index.
+ crb = DEBIread(dev, k->MyCRB) & ~CRBMSK_INTCTRL; /* Pulse index. */
DEBIwrite(dev, k->MyCRB, (uint16_t) (crb ^ CRBMSK_INDXPOL_B));
DEBIwrite(dev, k->MyCRB, crb);
}
-/////////////////////////////////////////////////////////
-// Write value into counter preload register.
+/* Write value into counter preload register. */
-static void Preload(comedi_device * dev, enc_private * k, uint32_t value)
+static void Preload(struct comedi_device *dev, struct enc_private *k, uint32_t value)
{
DEBUG("Preload: preload enter\n");
- DEBIwrite(dev, (uint16_t) (k->MyLatchLsw), (uint16_t) value); // Write value to preload register.
+ DEBIwrite(dev, (uint16_t) (k->MyLatchLsw), (uint16_t) value); /* Write value to preload register. */
DEBUG("Preload: preload step 1\n");
DEBIwrite(dev, (uint16_t) (k->MyLatchLsw + 2),
(uint16_t) (value >> 16));
}
-static void CountersInit(comedi_device * dev)
+static void CountersInit(struct comedi_device *dev)
{
int chan;
- enc_private *k;
- uint16_t Setup = (LOADSRC_INDX << BF_LOADSRC) | // Preload upon
- // index.
- (INDXSRC_SOFT << BF_INDXSRC) | // Disable hardware index.
- (CLKSRC_COUNTER << BF_CLKSRC) | // Operating mode is counter.
- (CLKPOL_POS << BF_CLKPOL) | // Active high clock.
- (CNTDIR_UP << BF_CLKPOL) | // Count direction is up.
- (CLKMULT_1X << BF_CLKMULT) | // Clock multiplier is 1x.
- (CLKENAB_INDEX << BF_CLKENAB); // Enabled by index
-
- // Disable all counter interrupts and clear any captured counter events.
+ struct enc_private *k;
+ uint16_t Setup = (LOADSRC_INDX << BF_LOADSRC) | /* Preload upon */
+ /* index. */
+ (INDXSRC_SOFT << BF_INDXSRC) | /* Disable hardware index. */
+ (CLKSRC_COUNTER << BF_CLKSRC) | /* Operating mode is counter. */
+ (CLKPOL_POS << BF_CLKPOL) | /* Active high clock. */
+ (CNTDIR_UP << BF_CLKPOL) | /* Count direction is up. */
+ (CLKMULT_1X << BF_CLKMULT) | /* Clock multiplier is 1x. */
+ (CLKENAB_INDEX << BF_CLKENAB); /* Enabled by index */
+
+ /* Disable all counter interrupts and clear any captured counter events. */
for (chan = 0; chan < S626_ENCODER_CHANNELS; chan++) {
k = &encpriv[chan];
k->SetMode(dev, k, Setup, TRUE);
diff --git a/drivers/staging/comedi/drivers/s626.h b/drivers/staging/comedi/drivers/s626.h
index 11d8b1ceb0b8..891126f25099 100644
--- a/drivers/staging/comedi/drivers/s626.h
+++ b/drivers/staging/comedi/drivers/s626.h
@@ -51,15 +51,15 @@
Example code
- insn.insn=INSN_CONFIG; //configuration instruction
- insn.n=1; //number of operation (must be 1)
- insn.data=&initialvalue; //initial value loaded into encoder
- //during configuration
- insn.subdev=5; //encoder subdevice
- insn.chanspec=CR_PACK(encoder_channel,0,AREF_OTHER); //encoder_channel
- //to configure
-
- comedi_do_insn(cf,&insn); //executing configuration
+ insn.insn=INSN_CONFIG; // configuration instruction
+ insn.n=1; // number of operation (must be 1)
+ insn.data=&initialvalue; // initial value loaded into encoder
+ // during configuration
+ insn.subdev=5; // encoder subdevice
+ insn.chanspec=CR_PACK(encoder_channel,0,AREF_OTHER); // encoder_channel
+ // to configure
+
+ comedi_do_insn(cf,&insn); // executing configuration
*/
#ifdef _DEBUG_
@@ -88,147 +88,136 @@
#define INLINE static __inline
#endif
-/////////////////////////////////////////////////////
#include<linux/slab.h>
#define S626_SIZE 0x0200
#define SIZEOF_ADDRESS_SPACE 0x0200
-#define DMABUF_SIZE 4096 // 4k pages
+#define DMABUF_SIZE 4096 /* 4k pages */
#define S626_ADC_CHANNELS 16
#define S626_DAC_CHANNELS 4
#define S626_ENCODER_CHANNELS 6
#define S626_DIO_CHANNELS 48
-#define S626_DIO_BANKS 3 // Number of DIO groups.
-#define S626_DIO_EXTCHANS 40 // Number of
- // extended-capability
- // DIO channels.
+#define S626_DIO_BANKS 3 /* Number of DIO groups. */
+#define S626_DIO_EXTCHANS 40 /* Number of */
+ /* extended-capability */
+ /* DIO channels. */
-#define NUM_TRIMDACS 12 // Number of valid TrimDAC channels.
+#define NUM_TRIMDACS 12 /* Number of valid TrimDAC channels. */
-// PCI bus interface types.
-#define INTEL 1 // Intel bus type.
-#define MOTOROLA 2 // Motorola bus type.
+/* PCI bus interface types. */
+#define INTEL 1 /* Intel bus type. */
+#define MOTOROLA 2 /* Motorola bus type. */
-//////////////////////////////////////////////////////////
+#define PLATFORM INTEL /* *** SELECT PLATFORM TYPE *** */
-//////////////////////////////////////////////////////////
-#define PLATFORM INTEL // *** SELECT PLATFORM TYPE ***
-//////////////////////////////////////////////////////////
+#define RANGE_5V 0x10 /* +/-5V range */
+#define RANGE_10V 0x00 /* +/-10V range */
-#define RANGE_5V 0x10 // +/-5V range
-#define RANGE_10V 0x00 // +/-10V range
+#define EOPL 0x80 /* End of ADC poll list marker. */
+#define GSEL_BIPOLAR5V 0x00F0 /* LP_GSEL setting for 5V bipolar range. */
+#define GSEL_BIPOLAR10V 0x00A0 /* LP_GSEL setting for 10V bipolar range. */
-#define EOPL 0x80 // End of ADC poll list marker.
-#define GSEL_BIPOLAR5V 0x00F0 // LP_GSEL setting for 5V bipolar range.
-#define GSEL_BIPOLAR10V 0x00A0 // LP_GSEL setting for 10V bipolar range.
+/* Error codes that must be visible to this base class. */
+#define ERR_ILLEGAL_PARM 0x00010000 /* Illegal function parameter value was specified. */
+#define ERR_I2C 0x00020000 /* I2C error. */
+#define ERR_COUNTERSETUP 0x00200000 /* Illegal setup specified for counter channel. */
+#define ERR_DEBI_TIMEOUT 0x00400000 /* DEBI transfer timed out. */
-// Error codes that must be visible to this base class.
-#define ERR_ILLEGAL_PARM 0x00010000 // Illegal function parameter value was specified.
-#define ERR_I2C 0x00020000 // I2C error.
-#define ERR_COUNTERSETUP 0x00200000 // Illegal setup specified for counter channel.
-#define ERR_DEBI_TIMEOUT 0x00400000 // DEBI transfer timed out.
+/* Organization (physical order) and size (in DWORDs) of logical DMA buffers contained by ANA_DMABUF. */
+#define ADC_DMABUF_DWORDS 40 /* ADC DMA buffer must hold 16 samples, plus pre/post garbage samples. */
+#define DAC_WDMABUF_DWORDS 1 /* DAC output DMA buffer holds a single sample. */
-// Organization (physical order) and size (in DWORDs) of logical DMA buffers contained by ANA_DMABUF.
-#define ADC_DMABUF_DWORDS 40 // ADC DMA buffer must hold 16 samples, plus pre/post garbage samples.
-#define DAC_WDMABUF_DWORDS 1 // DAC output DMA buffer holds a single sample.
+/* All remaining space in 4KB DMA buffer is available for the RPS1 program. */
-// All remaining space in 4KB DMA buffer is available for the RPS1 program.
-
-// Address offsets, in DWORDS, from base of DMA buffer.
+/* Address offsets, in DWORDS, from base of DMA buffer. */
#define DAC_WDMABUF_OS ADC_DMABUF_DWORDS
-// Interrupt enab bit in ISR and IER.
-#define IRQ_GPIO3 0x00000040 // IRQ enable for GPIO3.
+/* Interrupt enab bit in ISR and IER. */
+#define IRQ_GPIO3 0x00000040 /* IRQ enable for GPIO3. */
#define IRQ_RPS1 0x10000000
-#define ISR_AFOU 0x00000800 // Audio fifo
- // under/overflow
- // detected.
-#define IRQ_COINT1A 0x0400 // conter 1A overflow
- // interrupt mask
-#define IRQ_COINT1B 0x0800 // conter 1B overflow
- // interrupt mask
-#define IRQ_COINT2A 0x1000 // conter 2A overflow
- // interrupt mask
-#define IRQ_COINT2B 0x2000 // conter 2B overflow
- // interrupt mask
-#define IRQ_COINT3A 0x4000 // conter 3A overflow
- // interrupt mask
-#define IRQ_COINT3B 0x8000 // conter 3B overflow
- // interrupt mask
-
-// RPS command codes.
-#define RPS_CLRSIGNAL 0x00000000 // CLEAR SIGNAL
-#define RPS_SETSIGNAL 0x10000000 // SET SIGNAL
-#define RPS_NOP 0x00000000 // NOP
-#define RPS_PAUSE 0x20000000 // PAUSE
-#define RPS_UPLOAD 0x40000000 // UPLOAD
-#define RPS_JUMP 0x80000000 // JUMP
-#define RPS_LDREG 0x90000100 // LDREG (1 uint32_t only)
-#define RPS_STREG 0xA0000100 // STREG (1 uint32_t only)
-#define RPS_STOP 0x50000000 // STOP
-#define RPS_IRQ 0x60000000 // IRQ
-
-#define RPS_LOGICAL_OR 0x08000000 // Logical OR conditionals.
-#define RPS_INVERT 0x04000000 // Test for negated semaphores.
-#define RPS_DEBI 0x00000002 // DEBI done
-
-#define RPS_SIG0 0x00200000 // RPS semaphore 0 (used by ADC).
-#define RPS_SIG1 0x00400000 // RPS semaphore 1 (used by DAC).
-#define RPS_SIG2 0x00800000 // RPS semaphore 2 (not used).
-#define RPS_GPIO2 0x00080000 // RPS GPIO2
-#define RPS_GPIO3 0x00100000 // RPS GPIO3
-
-#define RPS_SIGADC RPS_SIG0 // Trigger/status for ADC's RPS program.
-#define RPS_SIGDAC RPS_SIG1 // Trigger/status for DAC's RPS program.
-
-// RPS clock parameters.
-#define RPSCLK_SCALAR 8 // This is apparent ratio of PCI/RPS clks (undocumented!!).
-#define RPSCLK_PER_US ( 33 / RPSCLK_SCALAR ) // Number of RPS clocks in one microsecond.
-
-// Event counter source addresses.
-#define SBA_RPS_A0 0x27 // Time of RPS0 busy, in PCI clocks.
-
-// GPIO constants.
-#define GPIO_BASE 0x10004000 // GPIO 0,2,3 = inputs, GPIO3 = IRQ; GPIO1 = out.
-#define GPIO1_LO 0x00000000 // GPIO1 set to LOW.
-#define GPIO1_HI 0x00001000 // GPIO1 set to HIGH.
-
-// Primary Status Register (PSR) constants.
-#define PSR_DEBI_E 0x00040000 // DEBI event flag.
-#define PSR_DEBI_S 0x00080000 // DEBI status flag.
-#define PSR_A2_IN 0x00008000 // Audio output DMA2 protection address reached.
-#define PSR_AFOU 0x00000800 // Audio FIFO under/overflow detected.
-#define PSR_GPIO2 0x00000020 // GPIO2 input pin: 0=AdcBusy, 1=AdcIdle.
-#define PSR_EC0S 0x00000001 // Event counter 0 threshold reached.
-
-// Secondary Status Register (SSR) constants.
-#define SSR_AF2_OUT 0x00000200 // Audio 2 output FIFO under/overflow detected.
-
-// Master Control Register 1 (MC1) constants.
-#define MC1_SOFT_RESET 0x80000000 // Invoke 7146 soft reset.
-#define MC1_SHUTDOWN 0x3FFF0000 // Shut down all MC1-controlled enables.
-
-#define MC1_ERPS1 0x2000 // enab/disable RPS task 1.
-#define MC1_ERPS0 0x1000 // enab/disable RPS task 0.
-#define MC1_DEBI 0x0800 // enab/disable DEBI pins.
-#define MC1_AUDIO 0x0200 // enab/disable audio port pins.
-#define MC1_I2C 0x0100 // enab/disable I2C interface.
-#define MC1_A2OUT 0x0008 // enab/disable transfer on A2 out.
-#define MC1_A2IN 0x0004 // enab/disable transfer on A2 in.
-#define MC1_A1IN 0x0001 // enab/disable transfer on A1 in.
-
-// Master Control Register 2 (MC2) constants.
-#define MC2_UPLD_DEBIq 0x00020002 // Upload DEBI registers.
-#define MC2_UPLD_IICq 0x00010001 // Upload I2C registers.
-#define MC2_RPSSIG2_ONq 0x20002000 // Assert RPS_SIG2.
-#define MC2_RPSSIG1_ONq 0x10001000 // Assert RPS_SIG1.
-#define MC2_RPSSIG0_ONq 0x08000800 // Assert RPS_SIG0.
-#define MC2_UPLD_DEBI_MASKq 0x00000002 // Upload DEBI mask.
-#define MC2_UPLD_IIC_MASKq 0x00000001 // Upload I2C mask.
-#define MC2_RPSSIG2_MASKq 0x00002000 // RPS_SIG2 bit mask.
-#define MC2_RPSSIG1_MASKq 0x00001000 // RPS_SIG1 bit mask.
-#define MC2_RPSSIG0_MASKq 0x00000800 // RPS_SIG0 bit mask.
+#define ISR_AFOU 0x00000800
+/* Audio fifo under/overflow detected. */
+
+#define IRQ_COINT1A 0x0400 /* conter 1A overflow interrupt mask */
+#define IRQ_COINT1B 0x0800 /* conter 1B overflow interrupt mask */
+#define IRQ_COINT2A 0x1000 /* conter 2A overflow interrupt mask */
+#define IRQ_COINT2B 0x2000 /* conter 2B overflow interrupt mask */
+#define IRQ_COINT3A 0x4000 /* conter 3A overflow interrupt mask */
+#define IRQ_COINT3B 0x8000 /* conter 3B overflow interrupt mask */
+
+/* RPS command codes. */
+#define RPS_CLRSIGNAL 0x00000000 /* CLEAR SIGNAL */
+#define RPS_SETSIGNAL 0x10000000 /* SET SIGNAL */
+#define RPS_NOP 0x00000000 /* NOP */
+#define RPS_PAUSE 0x20000000 /* PAUSE */
+#define RPS_UPLOAD 0x40000000 /* UPLOAD */
+#define RPS_JUMP 0x80000000 /* JUMP */
+#define RPS_LDREG 0x90000100 /* LDREG (1 uint32_t only) */
+#define RPS_STREG 0xA0000100 /* STREG (1 uint32_t only) */
+#define RPS_STOP 0x50000000 /* STOP */
+#define RPS_IRQ 0x60000000 /* IRQ */
+
+#define RPS_LOGICAL_OR 0x08000000 /* Logical OR conditionals. */
+#define RPS_INVERT 0x04000000 /* Test for negated semaphores. */
+#define RPS_DEBI 0x00000002 /* DEBI done */
+
+#define RPS_SIG0 0x00200000 /* RPS semaphore 0 (used by ADC). */
+#define RPS_SIG1 0x00400000 /* RPS semaphore 1 (used by DAC). */
+#define RPS_SIG2 0x00800000 /* RPS semaphore 2 (not used). */
+#define RPS_GPIO2 0x00080000 /* RPS GPIO2 */
+#define RPS_GPIO3 0x00100000 /* RPS GPIO3 */
+
+#define RPS_SIGADC RPS_SIG0 /* Trigger/status for ADC's RPS program. */
+#define RPS_SIGDAC RPS_SIG1 /* Trigger/status for DAC's RPS program. */
+
+/* RPS clock parameters. */
+#define RPSCLK_SCALAR 8 /* This is apparent ratio of PCI/RPS clks (undocumented!!). */
+#define RPSCLK_PER_US (33 / RPSCLK_SCALAR) /* Number of RPS clocks in one microsecond. */
+
+/* Event counter source addresses. */
+#define SBA_RPS_A0 0x27 /* Time of RPS0 busy, in PCI clocks. */
+
+/* GPIO constants. */
+#define GPIO_BASE 0x10004000 /* GPIO 0,2,3 = inputs, GPIO3 = IRQ; GPIO1 = out. */
+#define GPIO1_LO 0x00000000 /* GPIO1 set to LOW. */
+#define GPIO1_HI 0x00001000 /* GPIO1 set to HIGH. */
+
+/* Primary Status Register (PSR) constants. */
+#define PSR_DEBI_E 0x00040000 /* DEBI event flag. */
+#define PSR_DEBI_S 0x00080000 /* DEBI status flag. */
+#define PSR_A2_IN 0x00008000 /* Audio output DMA2 protection address reached. */
+#define PSR_AFOU 0x00000800 /* Audio FIFO under/overflow detected. */
+#define PSR_GPIO2 0x00000020 /* GPIO2 input pin: 0=AdcBusy, 1=AdcIdle. */
+#define PSR_EC0S 0x00000001 /* Event counter 0 threshold reached. */
+
+/* Secondary Status Register (SSR) constants. */
+#define SSR_AF2_OUT 0x00000200 /* Audio 2 output FIFO under/overflow detected. */
+
+/* Master Control Register 1 (MC1) constants. */
+#define MC1_SOFT_RESET 0x80000000 /* Invoke 7146 soft reset. */
+#define MC1_SHUTDOWN 0x3FFF0000 /* Shut down all MC1-controlled enables. */
+
+#define MC1_ERPS1 0x2000 /* enab/disable RPS task 1. */
+#define MC1_ERPS0 0x1000 /* enab/disable RPS task 0. */
+#define MC1_DEBI 0x0800 /* enab/disable DEBI pins. */
+#define MC1_AUDIO 0x0200 /* enab/disable audio port pins. */
+#define MC1_I2C 0x0100 /* enab/disable I2C interface. */
+#define MC1_A2OUT 0x0008 /* enab/disable transfer on A2 out. */
+#define MC1_A2IN 0x0004 /* enab/disable transfer on A2 in. */
+#define MC1_A1IN 0x0001 /* enab/disable transfer on A1 in. */
+
+/* Master Control Register 2 (MC2) constants. */
+#define MC2_UPLD_DEBIq 0x00020002 /* Upload DEBI registers. */
+#define MC2_UPLD_IICq 0x00010001 /* Upload I2C registers. */
+#define MC2_RPSSIG2_ONq 0x20002000 /* Assert RPS_SIG2. */
+#define MC2_RPSSIG1_ONq 0x10001000 /* Assert RPS_SIG1. */
+#define MC2_RPSSIG0_ONq 0x08000800 /* Assert RPS_SIG0. */
+#define MC2_UPLD_DEBI_MASKq 0x00000002 /* Upload DEBI mask. */
+#define MC2_UPLD_IIC_MASKq 0x00000001 /* Upload I2C mask. */
+#define MC2_RPSSIG2_MASKq 0x00002000 /* RPS_SIG2 bit mask. */
+#define MC2_RPSSIG1_MASKq 0x00001000 /* RPS_SIG1 bit mask. */
+#define MC2_RPSSIG0_MASKq 0x00000800 /* RPS_SIG0 bit mask. */
#define MC2_DELAYTRIG_4USq MC2_RPSSIG1_ON
#define MC2_DELAYBUSY_4USq MC2_RPSSIG1_MASK
@@ -236,469 +225,425 @@
#define MC2_DELAYTRIG_6USq MC2_RPSSIG2_ON
#define MC2_DELAYBUSY_6USq MC2_RPSSIG2_MASK
-#define MC2_UPLD_DEBI 0x0002 // Upload DEBI.
-#define MC2_UPLD_IIC 0x0001 // Upload I2C.
-#define MC2_RPSSIG2 0x2000 // RPS signal 2 (not used).
-#define MC2_RPSSIG1 0x1000 // RPS signal 1 (DAC RPS busy).
-#define MC2_RPSSIG0 0x0800 // RPS signal 0 (ADC RPS busy).
-
-#define MC2_ADC_RPS MC2_RPSSIG0 // ADC RPS busy.
-#define MC2_DAC_RPS MC2_RPSSIG1 // DAC RPS busy.
-
-///////////////////oldies///////////
-#define MC2_UPLD_DEBIQ 0x00020002 // Upload DEBI registers.
-#define MC2_UPLD_IICQ 0x00010001 // Upload I2C registers.
-////////////////////////////////////////
-
-// PCI BUS (SAA7146) REGISTER ADDRESS OFFSETS ////////////////////////
-#define P_PCI_BT_A 0x004C // Audio DMA
- // burst/threshold
- // control.
-#define P_DEBICFG 0x007C // DEBI configuration.
-#define P_DEBICMD 0x0080 // DEBI command.
-#define P_DEBIPAGE 0x0084 // DEBI page.
-#define P_DEBIAD 0x0088 // DEBI target address.
-#define P_I2CCTRL 0x008C // I2C control.
-#define P_I2CSTAT 0x0090 // I2C status.
-#define P_BASEA2_IN 0x00AC // Audio input 2 base
- // physical DMAbuf
- // address.
-#define P_PROTA2_IN 0x00B0 // Audio input 2
- // physical DMAbuf
- // protection address.
-#define P_PAGEA2_IN 0x00B4 // Audio input 2
- // paging attributes.
-#define P_BASEA2_OUT 0x00B8 // Audio output 2 base
- // physical DMAbuf
- // address.
-#define P_PROTA2_OUT 0x00BC // Audio output 2
- // physical DMAbuf
- // protection address.
-#define P_PAGEA2_OUT 0x00C0 // Audio output 2
- // paging attributes.
-#define P_RPSPAGE0 0x00C4 // RPS0 page.
-#define P_RPSPAGE1 0x00C8 // RPS1 page.
-#define P_RPS0_TOUT 0x00D4 // RPS0 time-out.
-#define P_RPS1_TOUT 0x00D8 // RPS1 time-out.
-#define P_IER 0x00DC // Interrupt enable.
-#define P_GPIO 0x00E0 // General-purpose I/O.
-#define P_EC1SSR 0x00E4 // Event counter set 1
- // source select.
-#define P_ECT1R 0x00EC // Event counter
- // threshold set 1.
-#define P_ACON1 0x00F4 // Audio control 1.
-#define P_ACON2 0x00F8 // Audio control 2.
-#define P_MC1 0x00FC // Master control 1.
-#define P_MC2 0x0100 // Master control 2.
-#define P_RPSADDR0 0x0104 // RPS0 instruction pointer.
-#define P_RPSADDR1 0x0108 // RPS1 instruction pointer.
-#define P_ISR 0x010C // Interrupt status.
-#define P_PSR 0x0110 // Primary status.
-#define P_SSR 0x0114 // Secondary status.
-#define P_EC1R 0x0118 // Event counter set 1.
-#define P_ADP4 0x0138 // Logical audio DMA
- // pointer of audio
- // input FIFO A2_IN.
-#define P_FB_BUFFER1 0x0144 // Audio feedback buffer 1.
-#define P_FB_BUFFER2 0x0148 // Audio feedback buffer 2.
-#define P_TSL1 0x0180 // Audio time slot list 1.
-#define P_TSL2 0x01C0 // Audio time slot list 2.
-
-// LOCAL BUS (GATE ARRAY) REGISTER ADDRESS OFFSETS /////////////////
-// Analog I/O registers:
-#define LP_DACPOL 0x0082 // Write DAC polarity.
-#define LP_GSEL 0x0084 // Write ADC gain.
-#define LP_ISEL 0x0086 // Write ADC channel select.
-// Digital I/O (write only):
-#define LP_WRINTSELA 0x0042 // Write A interrupt enable.
-#define LP_WREDGSELA 0x0044 // Write A edge selection.
-#define LP_WRCAPSELA 0x0046 // Write A capture enable.
-#define LP_WRDOUTA 0x0048 // Write A digital output.
-#define LP_WRINTSELB 0x0052 // Write B interrupt enable.
-#define LP_WREDGSELB 0x0054 // Write B edge selection.
-#define LP_WRCAPSELB 0x0056 // Write B capture enable.
-#define LP_WRDOUTB 0x0058 // Write B digital output.
-#define LP_WRINTSELC 0x0062 // Write C interrupt enable.
-#define LP_WREDGSELC 0x0064 // Write C edge selection.
-#define LP_WRCAPSELC 0x0066 // Write C capture enable.
-#define LP_WRDOUTC 0x0068 // Write C digital output.
-
-// Digital I/O (read only):
-#define LP_RDDINA 0x0040 // Read digital input.
-#define LP_RDCAPFLGA 0x0048 // Read edges captured.
-#define LP_RDINTSELA 0x004A // Read interrupt
- // enable register.
-#define LP_RDEDGSELA 0x004C // Read edge
- // selection
- // register.
-#define LP_RDCAPSELA 0x004E // Read capture
- // enable register.
-#define LP_RDDINB 0x0050 // Read digital input.
-#define LP_RDCAPFLGB 0x0058 // Read edges captured.
-#define LP_RDINTSELB 0x005A // Read interrupt
- // enable register.
-#define LP_RDEDGSELB 0x005C // Read edge
- // selection
- // register.
-#define LP_RDCAPSELB 0x005E // Read capture
- // enable register.
-#define LP_RDDINC 0x0060 // Read digital input.
-#define LP_RDCAPFLGC 0x0068 // Read edges captured.
-#define LP_RDINTSELC 0x006A // Read interrupt
- // enable register.
-#define LP_RDEDGSELC 0x006C // Read edge
- // selection
- // register.
-#define LP_RDCAPSELC 0x006E // Read capture
- // enable register.
-// Counter Registers (read/write):
-#define LP_CR0A 0x0000 // 0A setup register.
-#define LP_CR0B 0x0002 // 0B setup register.
-#define LP_CR1A 0x0004 // 1A setup register.
-#define LP_CR1B 0x0006 // 1B setup register.
-#define LP_CR2A 0x0008 // 2A setup register.
-#define LP_CR2B 0x000A // 2B setup register.
-// Counter PreLoad (write) and Latch (read) Registers:
-#define LP_CNTR0ALSW 0x000C // 0A lsw.
-#define LP_CNTR0AMSW 0x000E // 0A msw.
-#define LP_CNTR0BLSW 0x0010 // 0B lsw.
-#define LP_CNTR0BMSW 0x0012 // 0B msw.
-#define LP_CNTR1ALSW 0x0014 // 1A lsw.
-#define LP_CNTR1AMSW 0x0016 // 1A msw.
-#define LP_CNTR1BLSW 0x0018 // 1B lsw.
-#define LP_CNTR1BMSW 0x001A // 1B msw.
-#define LP_CNTR2ALSW 0x001C // 2A lsw.
-#define LP_CNTR2AMSW 0x001E // 2A msw.
-#define LP_CNTR2BLSW 0x0020 // 2B lsw.
-#define LP_CNTR2BMSW 0x0022 // 2B msw.
-// Miscellaneous Registers (read/write):
-#define LP_MISC1 0x0088 // Read/write Misc1.
-#define LP_WRMISC2 0x0090 // Write Misc2.
-#define LP_RDMISC2 0x0082 // Read Misc2.
-
-// Bit masks for MISC1 register that are the same for reads and writes.
-#define MISC1_WENABLE 0x8000 // enab writes to
- // MISC2 (except Clear
- // Watchdog bit).
-#define MISC1_WDISABLE 0x0000 // Disable writes to MISC2.
-#define MISC1_EDCAP 0x1000 // enab edge capture
- // on DIO chans
- // specified by
- // LP_WRCAPSELx.
-#define MISC1_NOEDCAP 0x0000 // Disable edge
- // capture on
- // specified DIO
- // chans.
-
-// Bit masks for MISC1 register reads.
-#define RDMISC1_WDTIMEOUT 0x4000 // Watchdog timer timed out.
-
-// Bit masks for MISC2 register writes.
-#define WRMISC2_WDCLEAR 0x8000 // Reset watchdog
- // timer to zero.
-#define WRMISC2_CHARGE_ENABLE 0x4000 // enab battery
- // trickle charging.
-
-// Bit masks for MISC2 register that are the same for reads and writes.
-#define MISC2_BATT_ENABLE 0x0008 // Backup battery enable.
-#define MISC2_WDENABLE 0x0004 // Watchdog timer enable.
-#define MISC2_WDPERIOD_MASK 0x0003 // Watchdog interval
- // select mask.
-
-// Bit masks for ACON1 register.
-#define A2_RUN 0x40000000 // Run A2 based on TSL2.
-#define A1_RUN 0x20000000 // Run A1 based on TSL1.
-#define A1_SWAP 0x00200000 // Use big-endian for A1.
-#define A2_SWAP 0x00100000 // Use big-endian for A2.
-#define WS_MODES 0x00019999 // WS0 = TSL1 trigger
- // input, WS1-WS4 =
- // CS* outputs.
-
-#if PLATFORM == INTEL // Base ACON1 config:
- // always run A1 based
- // on TSL1.
-#define ACON1_BASE ( WS_MODES | A1_RUN )
+#define MC2_UPLD_DEBI 0x0002 /* Upload DEBI. */
+#define MC2_UPLD_IIC 0x0001 /* Upload I2C. */
+#define MC2_RPSSIG2 0x2000 /* RPS signal 2 (not used). */
+#define MC2_RPSSIG1 0x1000 /* RPS signal 1 (DAC RPS busy). */
+#define MC2_RPSSIG0 0x0800 /* RPS signal 0 (ADC RPS busy). */
+
+#define MC2_ADC_RPS MC2_RPSSIG0 /* ADC RPS busy. */
+#define MC2_DAC_RPS MC2_RPSSIG1 /* DAC RPS busy. */
+
+/* ***** oldies ***** */
+#define MC2_UPLD_DEBIQ 0x00020002 /* Upload DEBI registers. */
+#define MC2_UPLD_IICQ 0x00010001 /* Upload I2C registers. */
+
+/* PCI BUS (SAA7146) REGISTER ADDRESS OFFSETS */
+#define P_PCI_BT_A 0x004C /* Audio DMA burst/threshold control. */
+#define P_DEBICFG 0x007C /* DEBI configuration. */
+#define P_DEBICMD 0x0080 /* DEBI command. */
+#define P_DEBIPAGE 0x0084 /* DEBI page. */
+#define P_DEBIAD 0x0088 /* DEBI target address. */
+#define P_I2CCTRL 0x008C /* I2C control. */
+#define P_I2CSTAT 0x0090 /* I2C status. */
+#define P_BASEA2_IN 0x00AC /* Audio input 2 base physical DMAbuf
+ * address. */
+#define P_PROTA2_IN 0x00B0 /* Audio input 2 physical DMAbuf
+ * protection address. */
+#define P_PAGEA2_IN 0x00B4 /* Audio input 2 paging attributes. */
+#define P_BASEA2_OUT 0x00B8 /* Audio output 2 base physical DMAbuf
+ * address. */
+#define P_PROTA2_OUT 0x00BC /* Audio output 2 physical DMAbuf
+ * protection address. */
+#define P_PAGEA2_OUT 0x00C0 /* Audio output 2 paging attributes. */
+#define P_RPSPAGE0 0x00C4 /* RPS0 page. */
+#define P_RPSPAGE1 0x00C8 /* RPS1 page. */
+#define P_RPS0_TOUT 0x00D4 /* RPS0 time-out. */
+#define P_RPS1_TOUT 0x00D8 /* RPS1 time-out. */
+#define P_IER 0x00DC /* Interrupt enable. */
+#define P_GPIO 0x00E0 /* General-purpose I/O. */
+#define P_EC1SSR 0x00E4 /* Event counter set 1 source select. */
+#define P_ECT1R 0x00EC /* Event counter threshold set 1. */
+#define P_ACON1 0x00F4 /* Audio control 1. */
+#define P_ACON2 0x00F8 /* Audio control 2. */
+#define P_MC1 0x00FC /* Master control 1. */
+#define P_MC2 0x0100 /* Master control 2. */
+#define P_RPSADDR0 0x0104 /* RPS0 instruction pointer. */
+#define P_RPSADDR1 0x0108 /* RPS1 instruction pointer. */
+#define P_ISR 0x010C /* Interrupt status. */
+#define P_PSR 0x0110 /* Primary status. */
+#define P_SSR 0x0114 /* Secondary status. */
+#define P_EC1R 0x0118 /* Event counter set 1. */
+#define P_ADP4 0x0138 /* Logical audio DMA pointer of audio
+ * input FIFO A2_IN. */
+#define P_FB_BUFFER1 0x0144 /* Audio feedback buffer 1. */
+#define P_FB_BUFFER2 0x0148 /* Audio feedback buffer 2. */
+#define P_TSL1 0x0180 /* Audio time slot list 1. */
+#define P_TSL2 0x01C0 /* Audio time slot list 2. */
+
+/* LOCAL BUS (GATE ARRAY) REGISTER ADDRESS OFFSETS */
+/* Analog I/O registers: */
+#define LP_DACPOL 0x0082 /* Write DAC polarity. */
+#define LP_GSEL 0x0084 /* Write ADC gain. */
+#define LP_ISEL 0x0086 /* Write ADC channel select. */
+/* Digital I/O (write only): */
+#define LP_WRINTSELA 0x0042 /* Write A interrupt enable. */
+#define LP_WREDGSELA 0x0044 /* Write A edge selection. */
+#define LP_WRCAPSELA 0x0046 /* Write A capture enable. */
+#define LP_WRDOUTA 0x0048 /* Write A digital output. */
+#define LP_WRINTSELB 0x0052 /* Write B interrupt enable. */
+#define LP_WREDGSELB 0x0054 /* Write B edge selection. */
+#define LP_WRCAPSELB 0x0056 /* Write B capture enable. */
+#define LP_WRDOUTB 0x0058 /* Write B digital output. */
+#define LP_WRINTSELC 0x0062 /* Write C interrupt enable. */
+#define LP_WREDGSELC 0x0064 /* Write C edge selection. */
+#define LP_WRCAPSELC 0x0066 /* Write C capture enable. */
+#define LP_WRDOUTC 0x0068 /* Write C digital output. */
+
+/* Digital I/O (read only): */
+#define LP_RDDINA 0x0040 /* Read digital input. */
+#define LP_RDCAPFLGA 0x0048 /* Read edges captured. */
+#define LP_RDINTSELA 0x004A /* Read interrupt enable register. */
+#define LP_RDEDGSELA 0x004C /* Read edge selection register. */
+#define LP_RDCAPSELA 0x004E /* Read capture enable register. */
+#define LP_RDDINB 0x0050 /* Read digital input. */
+#define LP_RDCAPFLGB 0x0058 /* Read edges captured. */
+#define LP_RDINTSELB 0x005A /* Read interrupt enable register. */
+#define LP_RDEDGSELB 0x005C /* Read edge selection register. */
+#define LP_RDCAPSELB 0x005E /* Read capture enable register. */
+#define LP_RDDINC 0x0060 /* Read digital input. */
+#define LP_RDCAPFLGC 0x0068 /* Read edges captured. */
+#define LP_RDINTSELC 0x006A /* Read interrupt enable register. */
+#define LP_RDEDGSELC 0x006C /* Read edge selection register. */
+#define LP_RDCAPSELC 0x006E /* Read capture enable register. */
+
+/* Counter Registers (read/write): */
+#define LP_CR0A 0x0000 /* 0A setup register. */
+#define LP_CR0B 0x0002 /* 0B setup register. */
+#define LP_CR1A 0x0004 /* 1A setup register. */
+#define LP_CR1B 0x0006 /* 1B setup register. */
+#define LP_CR2A 0x0008 /* 2A setup register. */
+#define LP_CR2B 0x000A /* 2B setup register. */
+
+/* Counter PreLoad (write) and Latch (read) Registers: */
+#define LP_CNTR0ALSW 0x000C /* 0A lsw. */
+#define LP_CNTR0AMSW 0x000E /* 0A msw. */
+#define LP_CNTR0BLSW 0x0010 /* 0B lsw. */
+#define LP_CNTR0BMSW 0x0012 /* 0B msw. */
+#define LP_CNTR1ALSW 0x0014 /* 1A lsw. */
+#define LP_CNTR1AMSW 0x0016 /* 1A msw. */
+#define LP_CNTR1BLSW 0x0018 /* 1B lsw. */
+#define LP_CNTR1BMSW 0x001A /* 1B msw. */
+#define LP_CNTR2ALSW 0x001C /* 2A lsw. */
+#define LP_CNTR2AMSW 0x001E /* 2A msw. */
+#define LP_CNTR2BLSW 0x0020 /* 2B lsw. */
+#define LP_CNTR2BMSW 0x0022 /* 2B msw. */
+
+/* Miscellaneous Registers (read/write): */
+#define LP_MISC1 0x0088 /* Read/write Misc1. */
+#define LP_WRMISC2 0x0090 /* Write Misc2. */
+#define LP_RDMISC2 0x0082 /* Read Misc2. */
+
+/* Bit masks for MISC1 register that are the same for reads and writes. */
+#define MISC1_WENABLE 0x8000 /* enab writes to MISC2 (except Clear
+ * Watchdog bit). */
+#define MISC1_WDISABLE 0x0000 /* Disable writes to MISC2. */
+#define MISC1_EDCAP 0x1000 /* enab edge capture on DIO chans
+ * specified by LP_WRCAPSELx. */
+#define MISC1_NOEDCAP 0x0000 /* Disable edge capture on specified
+ * DIO chans. */
+
+/* Bit masks for MISC1 register reads. */
+#define RDMISC1_WDTIMEOUT 0x4000 /* Watchdog timer timed out. */
+
+/* Bit masks for MISC2 register writes. */
+#define WRMISC2_WDCLEAR 0x8000 /* Reset watchdog timer to zero. */
+#define WRMISC2_CHARGE_ENABLE 0x4000 /* enab battery trickle charging. */
+
+/* Bit masks for MISC2 register that are the same for reads and writes. */
+#define MISC2_BATT_ENABLE 0x0008 /* Backup battery enable. */
+#define MISC2_WDENABLE 0x0004 /* Watchdog timer enable. */
+#define MISC2_WDPERIOD_MASK 0x0003 /* Watchdog interval */
+ /* select mask. */
+
+/* Bit masks for ACON1 register. */
+#define A2_RUN 0x40000000 /* Run A2 based on TSL2. */
+#define A1_RUN 0x20000000 /* Run A1 based on TSL1. */
+#define A1_SWAP 0x00200000 /* Use big-endian for A1. */
+#define A2_SWAP 0x00100000 /* Use big-endian for A2. */
+#define WS_MODES 0x00019999 /* WS0 = TSL1 trigger */
+ /* input, WS1-WS4 = */
+ /* CS* outputs. */
+
+#if PLATFORM == INTEL /* Base ACON1 config: always run A1 based
+ * on TSL1. */
+#define ACON1_BASE (WS_MODES | A1_RUN)
#elif PLATFORM == MOTOROLA
-#define ACON1_BASE ( WS_MODES | A1_RUN | A1_SWAP | A2_SWAP )
+#define ACON1_BASE (WS_MODES | A1_RUN | A1_SWAP | A2_SWAP)
#endif
-#define ACON1_ADCSTART ACON1_BASE // Start ADC: run A1
- // based on TSL1.
-#define ACON1_DACSTART ( ACON1_BASE | A2_RUN ) // Start
- // transmit to
- // DAC: run A2
- // based on
- // TSL2.
-#define ACON1_DACSTOP ACON1_BASE // Halt A2.
-
-// Bit masks for ACON2 register.
-#define A1_CLKSRC_BCLK1 0x00000000 // A1 bit rate = BCLK1 (ADC).
-#define A2_CLKSRC_X1 0x00800000 // A2 bit rate = ACLK/1 (DACs).
-#define A2_CLKSRC_X2 0x00C00000 // A2 bit rate = ACLK/2 (DACs).
-#define A2_CLKSRC_X4 0x01400000 // A2 bit rate = ACLK/4 (DACs).
-#define INVERT_BCLK2 0x00100000 // Invert BCLK2 (DACs).
-#define BCLK2_OE 0x00040000 // enab BCLK2 (DACs).
-#define ACON2_XORMASK 0x000C0000 // XOR mask for ACON2
- // active-low bits.
-
-#define ACON2_INIT ( ACON2_XORMASK ^ ( A1_CLKSRC_BCLK1 | A2_CLKSRC_X2 | INVERT_BCLK2 | BCLK2_OE ) )
-
-// Bit masks for timeslot records.
-#define WS1 0x40000000 // WS output to assert.
+#define ACON1_ADCSTART ACON1_BASE /* Start ADC: run A1
+ * based on TSL1. */
+#define ACON1_DACSTART (ACON1_BASE | A2_RUN)
+/* Start transmit to DAC: run A2 based on TSL2. */
+#define ACON1_DACSTOP ACON1_BASE /* Halt A2. */
+
+/* Bit masks for ACON2 register. */
+#define A1_CLKSRC_BCLK1 0x00000000 /* A1 bit rate = BCLK1 (ADC). */
+#define A2_CLKSRC_X1 0x00800000 /* A2 bit rate = ACLK/1 (DACs). */
+#define A2_CLKSRC_X2 0x00C00000 /* A2 bit rate = ACLK/2 (DACs). */
+#define A2_CLKSRC_X4 0x01400000 /* A2 bit rate = ACLK/4 (DACs). */
+#define INVERT_BCLK2 0x00100000 /* Invert BCLK2 (DACs). */
+#define BCLK2_OE 0x00040000 /* enab BCLK2 (DACs). */
+#define ACON2_XORMASK 0x000C0000 /* XOR mask for ACON2 */
+ /* active-low bits. */
+
+#define ACON2_INIT (ACON2_XORMASK ^ (A1_CLKSRC_BCLK1 | A2_CLKSRC_X2 | INVERT_BCLK2 | BCLK2_OE))
+
+/* Bit masks for timeslot records. */
+#define WS1 0x40000000 /* WS output to assert. */
#define WS2 0x20000000
#define WS3 0x10000000
#define WS4 0x08000000
-#define RSD1 0x01000000 // Shift A1 data in on SD1.
-#define SDW_A1 0x00800000 // Store rcv'd char at
- // next char slot of
- // DWORD1 buffer.
-#define SIB_A1 0x00400000 // Store rcv'd char at
- // next char slot of
- // FB1 buffer.
-#define SF_A1 0x00200000 // Write unsigned long
- // buffer to input
- // FIFO.
-
-//Select parallel-to-serial converter's data source:
-#define XFIFO_0 0x00000000 // Data fifo byte 0.
-#define XFIFO_1 0x00000010 // Data fifo byte 1.
-#define XFIFO_2 0x00000020 // Data fifo byte 2.
-#define XFIFO_3 0x00000030 // Data fifo byte 3.
-#define XFB0 0x00000040 // FB_BUFFER byte 0.
-#define XFB1 0x00000050 // FB_BUFFER byte 1.
-#define XFB2 0x00000060 // FB_BUFFER byte 2.
-#define XFB3 0x00000070 // FB_BUFFER byte 3.
-#define SIB_A2 0x00000200 // Store next dword
- // from A2's input
- // shifter to FB2
- // buffer.
-#define SF_A2 0x00000100 // Store next dword
- // from A2's input
- // shifter to its
- // input fifo.
-#define LF_A2 0x00000080 // Load next dword
- // from A2's output
- // fifo into its
- // output dword
- // buffer.
-#define XSD2 0x00000008 // Shift data out on SD2.
-#define RSD3 0x00001800 // Shift data in on SD3.
-#define RSD2 0x00001000 // Shift data in on SD2.
-#define LOW_A2 0x00000002 // Drive last SD low
- // for 7 clks, then
- // tri-state.
-#define EOS 0x00000001 // End of superframe.
-
-//////////////////////
-
-// I2C configuration constants.
-#define I2C_CLKSEL 0x0400 // I2C bit rate =
- // PCIclk/480 = 68.75
- // KHz.
-#define I2C_BITRATE 68.75 // I2C bus data bit
- // rate (determined by
- // I2C_CLKSEL) in KHz.
-#define I2C_WRTIME 15.0 // Worst case time,in
- // msec, for EEPROM
- // internal write op.
-
-// I2C manifest constants.
-
-// Max retries to wait for EEPROM write.
-#define I2C_RETRIES ( I2C_WRTIME * I2C_BITRATE / 9.0 )
-#define I2C_ERR 0x0002 // I2C control/status
- // flag ERROR.
-#define I2C_BUSY 0x0001 // I2C control/status
- // flag BUSY.
-#define I2C_ABORT 0x0080 // I2C status flag ABORT.
-#define I2C_ATTRSTART 0x3 // I2C attribute START.
-#define I2C_ATTRCONT 0x2 // I2C attribute CONT.
-#define I2C_ATTRSTOP 0x1 // I2C attribute STOP.
-#define I2C_ATTRNOP 0x0 // I2C attribute NOP.
-
-// I2C read command | EEPROM address.
-#define I2CR ( devpriv->I2CAdrs | 1 )
-
-// I2C write command | EEPROM address.
-#define I2CW ( devpriv->I2CAdrs )
-
-// Code macros used for constructing I2C command bytes.
-#define I2C_B2(ATTR,VAL) ( ( (ATTR) << 6 ) | ( (VAL) << 24 ) )
-#define I2C_B1(ATTR,VAL) ( ( (ATTR) << 4 ) | ( (VAL) << 16 ) )
-#define I2C_B0(ATTR,VAL) ( ( (ATTR) << 2 ) | ( (VAL) << 8 ) )
-
-////////////////////////////////////////////////////////
-//oldest
-#define P_DEBICFGq 0x007C // DEBI configuration.
-#define P_DEBICMDq 0x0080 // DEBI command.
-#define P_DEBIPAGEq 0x0084 // DEBI page.
-#define P_DEBIADq 0x0088 // DEBI target address.
-
-#define DEBI_CFG_TOQ 0x03C00000 // timeout (15 PCI cycles)
-#define DEBI_CFG_FASTQ 0x10000000 // fast mode enable
-#define DEBI_CFG_16Q 0x00080000 // 16-bit access enable
-#define DEBI_CFG_INCQ 0x00040000 // enable address increment
-#define DEBI_CFG_TIMEROFFQ 0x00010000 // disable timer
-#define DEBI_CMD_RDQ 0x00050000 // read immediate 2 bytes
-#define DEBI_CMD_WRQ 0x00040000 // write immediate 2 bytes
-#define DEBI_PAGE_DISABLEQ 0x00000000 // paging disable
-
-///////////////////////////////////////////
-// DEBI command constants.
-#define DEBI_CMD_SIZE16 ( 2 << 17 ) // Transfer size is
- // always 2 bytes.
-#define DEBI_CMD_READ 0x00010000 // Read operation.
-#define DEBI_CMD_WRITE 0x00000000 // Write operation.
-
-// Read immediate 2 bytes.
-#define DEBI_CMD_RDWORD ( DEBI_CMD_READ | DEBI_CMD_SIZE16 )
-
-// Write immediate 2 bytes.
-#define DEBI_CMD_WRWORD ( DEBI_CMD_WRITE | DEBI_CMD_SIZE16 )
-
-// DEBI configuration constants.
-#define DEBI_CFG_XIRQ_EN 0x80000000 // enab external
- // interrupt on GPIO3.
-#define DEBI_CFG_XRESUME 0x40000000 // Resume block
- // transfer when XIRQ
- // deasserted.
-#define DEBI_CFG_FAST 0x10000000 // Fast mode enable.
-
-// 4-bit field that specifies DEBI timeout value in PCI clock cycles:
-#define DEBI_CFG_TOUT_BIT 22 // Finish DEBI cycle after
- // this many clocks.
-
-// 2-bit field that specifies Endian byte lane steering:
-#define DEBI_CFG_SWAP_NONE 0x00000000 // Straight - don't
- // swap any bytes
- // (Intel).
-#define DEBI_CFG_SWAP_2 0x00100000 // 2-byte swap (Motorola).
-#define DEBI_CFG_SWAP_4 0x00200000 // 4-byte swap.
-#define DEBI_CFG_16 0x00080000 // Slave is able to
- // serve 16-bit
- // cycles.
-
-#define DEBI_CFG_SLAVE16 0x00080000 // Slave is able to
- // serve 16-bit
- // cycles.
-#define DEBI_CFG_INC 0x00040000 // enab address
- // increment for block
- // transfers.
-#define DEBI_CFG_INTEL 0x00020000 // Intel style local bus.
-#define DEBI_CFG_TIMEROFF 0x00010000 // Disable timer.
+#define RSD1 0x01000000 /* Shift A1 data in on SD1. */
+#define SDW_A1 0x00800000 /* Store rcv'd char at next
+ * char slot of DWORD1 buffer. */
+#define SIB_A1 0x00400000 /* Store rcv'd char at next
+ * char slot of FB1 buffer. */
+#define SF_A1 0x00200000 /* Write unsigned long
+ * buffer to input FIFO. */
+
+/* Select parallel-to-serial converter's data source: */
+#define XFIFO_0 0x00000000 /* Data fifo byte 0. */
+#define XFIFO_1 0x00000010 /* Data fifo byte 1. */
+#define XFIFO_2 0x00000020 /* Data fifo byte 2. */
+#define XFIFO_3 0x00000030 /* Data fifo byte 3. */
+#define XFB0 0x00000040 /* FB_BUFFER byte 0. */
+#define XFB1 0x00000050 /* FB_BUFFER byte 1. */
+#define XFB2 0x00000060 /* FB_BUFFER byte 2. */
+#define XFB3 0x00000070 /* FB_BUFFER byte 3. */
+#define SIB_A2 0x00000200 /* Store next dword from A2's
+ * input shifter to FB2 buffer. */
+#define SF_A2 0x00000100 /* Store next dword from A2's
+ * input shifter to its input
+ * fifo. */
+#define LF_A2 0x00000080 /* Load next dword from A2's
+ * output fifo into its
+ * output dword buffer. */
+#define XSD2 0x00000008 /* Shift data out on SD2. */
+#define RSD3 0x00001800 /* Shift data in on SD3. */
+#define RSD2 0x00001000 /* Shift data in on SD2. */
+#define LOW_A2 0x00000002 /* Drive last SD low */
+ /* for 7 clks, then */
+ /* tri-state. */
+#define EOS 0x00000001 /* End of superframe. */
+
+
+/* I2C configuration constants. */
+#define I2C_CLKSEL 0x0400
+/* I2C bit rate = PCIclk/480 = 68.75 KHz. */
+
+#define I2C_BITRATE 68.75
+/* I2C bus data bit rate (determined by I2C_CLKSEL) in KHz. */
+
+#define I2C_WRTIME 15.0
+/* Worst case time, in msec, for EEPROM internal write op. */
+
+/* I2C manifest constants. */
+
+/* Max retries to wait for EEPROM write. */
+#define I2C_RETRIES (I2C_WRTIME * I2C_BITRATE / 9.0)
+#define I2C_ERR 0x0002 /* I2C control/status */
+ /* flag ERROR. */
+#define I2C_BUSY 0x0001 /* I2C control/status */
+ /* flag BUSY. */
+#define I2C_ABORT 0x0080 /* I2C status flag ABORT. */
+#define I2C_ATTRSTART 0x3 /* I2C attribute START. */
+#define I2C_ATTRCONT 0x2 /* I2C attribute CONT. */
+#define I2C_ATTRSTOP 0x1 /* I2C attribute STOP. */
+#define I2C_ATTRNOP 0x0 /* I2C attribute NOP. */
+
+/* I2C read command | EEPROM address. */
+#define I2CR (devpriv->I2CAdrs | 1)
+
+/* I2C write command | EEPROM address. */
+#define I2CW (devpriv->I2CAdrs)
+
+/* Code macros used for constructing I2C command bytes. */
+#define I2C_B2(ATTR, VAL) (((ATTR) << 6) | ((VAL) << 24))
+#define I2C_B1(ATTR, VAL) (((ATTR) << 4) | ((VAL) << 16))
+#define I2C_B0(ATTR, VAL) (((ATTR) << 2) | ((VAL) << 8))
+
+/* oldest */
+#define P_DEBICFGq 0x007C /* DEBI configuration. */
+#define P_DEBICMDq 0x0080 /* DEBI command. */
+#define P_DEBIPAGEq 0x0084 /* DEBI page. */
+#define P_DEBIADq 0x0088 /* DEBI target address. */
+
+#define DEBI_CFG_TOQ 0x03C00000 /* timeout (15 PCI cycles) */
+#define DEBI_CFG_FASTQ 0x10000000 /* fast mode enable */
+#define DEBI_CFG_16Q 0x00080000 /* 16-bit access enable */
+#define DEBI_CFG_INCQ 0x00040000 /* enable address increment */
+#define DEBI_CFG_TIMEROFFQ 0x00010000 /* disable timer */
+#define DEBI_CMD_RDQ 0x00050000 /* read immediate 2 bytes */
+#define DEBI_CMD_WRQ 0x00040000 /* write immediate 2 bytes */
+#define DEBI_PAGE_DISABLEQ 0x00000000 /* paging disable */
+
+/* DEBI command constants. */
+#define DEBI_CMD_SIZE16 (2 << 17) /* Transfer size is */
+ /* always 2 bytes. */
+#define DEBI_CMD_READ 0x00010000 /* Read operation. */
+#define DEBI_CMD_WRITE 0x00000000 /* Write operation. */
+
+/* Read immediate 2 bytes. */
+#define DEBI_CMD_RDWORD (DEBI_CMD_READ | DEBI_CMD_SIZE16)
+
+/* Write immediate 2 bytes. */
+#define DEBI_CMD_WRWORD (DEBI_CMD_WRITE | DEBI_CMD_SIZE16)
+
+/* DEBI configuration constants. */
+#define DEBI_CFG_XIRQ_EN 0x80000000 /* enab external */
+ /* interrupt on GPIO3. */
+#define DEBI_CFG_XRESUME 0x40000000 /* Resume block */
+ /* transfer when XIRQ */
+ /* deasserted. */
+#define DEBI_CFG_FAST 0x10000000 /* Fast mode enable. */
+
+/* 4-bit field that specifies DEBI timeout value in PCI clock cycles: */
+#define DEBI_CFG_TOUT_BIT 22 /* Finish DEBI cycle after */
+ /* this many clocks. */
+
+/* 2-bit field that specifies Endian byte lane steering: */
+#define DEBI_CFG_SWAP_NONE 0x00000000 /* Straight - don't */
+ /* swap any bytes */
+ /* (Intel). */
+#define DEBI_CFG_SWAP_2 0x00100000 /* 2-byte swap (Motorola). */
+#define DEBI_CFG_SWAP_4 0x00200000 /* 4-byte swap. */
+#define DEBI_CFG_16 0x00080000 /* Slave is able to */
+ /* serve 16-bit */
+ /* cycles. */
+
+#define DEBI_CFG_SLAVE16 0x00080000 /* Slave is able to */
+ /* serve 16-bit */
+ /* cycles. */
+#define DEBI_CFG_INC 0x00040000 /* enab address */
+ /* increment for block */
+ /* transfers. */
+#define DEBI_CFG_INTEL 0x00020000 /* Intel style local bus. */
+#define DEBI_CFG_TIMEROFF 0x00010000 /* Disable timer. */
#if PLATFORM == INTEL
-#define DEBI_TOUT 7 // Wait 7 PCI clocks
- // (212 ns) before
- // polling RDY.
+#define DEBI_TOUT 7 /* Wait 7 PCI clocks */
+ /* (212 ns) before */
+ /* polling RDY. */
-// Intel byte lane steering (pass through all byte lanes).
+/* Intel byte lane steering (pass through all byte lanes). */
#define DEBI_SWAP DEBI_CFG_SWAP_NONE
#elif PLATFORM == MOTOROLA
-#define DEBI_TOUT 15 // Wait 15 PCI clocks (454 ns)
- // maximum before timing out.
-#define DEBI_SWAP DEBI_CFG_SWAP_2 // Motorola byte lane steering.
+#define DEBI_TOUT 15 /* Wait 15 PCI clocks (454 ns) */
+ /* maximum before timing out. */
+#define DEBI_SWAP DEBI_CFG_SWAP_2 /* Motorola byte lane steering. */
#endif
-// DEBI page table constants.
-#define DEBI_PAGE_DISABLE 0x00000000 // Paging disable.
-
-///////////////////EXTRA FROM OTHER SANSORAY * .h////////
-
-// LoadSrc values:
-#define LOADSRC_INDX 0 // Preload core in response to
- // Index.
-#define LOADSRC_OVER 1 // Preload core in response to
- // Overflow.
-#define LOADSRCB_OVERA 2 // Preload B core in response
- // to A Overflow.
-#define LOADSRC_NONE 3 // Never preload core.
-
-// IntSrc values:
-#define INTSRC_NONE 0 // Interrupts disabled.
-#define INTSRC_OVER 1 // Interrupt on Overflow.
-#define INTSRC_INDX 2 // Interrupt on Index.
-#define INTSRC_BOTH 3 // Interrupt on Index or Overflow.
-
-// LatchSrc values:
-#define LATCHSRC_AB_READ 0 // Latch on read.
-#define LATCHSRC_A_INDXA 1 // Latch A on A Index.
-#define LATCHSRC_B_INDXB 2 // Latch B on B Index.
-#define LATCHSRC_B_OVERA 3 // Latch B on A Overflow.
-
-// IndxSrc values:
-#define INDXSRC_HARD 0 // Hardware or software index.
-#define INDXSRC_SOFT 1 // Software index only.
-
-// IndxPol values:
-#define INDXPOL_POS 0 // Index input is active high.
-#define INDXPOL_NEG 1 // Index input is active low.
-
-// ClkSrc values:
-#define CLKSRC_COUNTER 0 // Counter mode.
-#define CLKSRC_TIMER 2 // Timer mode.
-#define CLKSRC_EXTENDER 3 // Extender mode.
-
-// ClkPol values:
-#define CLKPOL_POS 0 // Counter/Extender clock is
- // active high.
-#define CLKPOL_NEG 1 // Counter/Extender clock is
- // active low.
-#define CNTDIR_UP 0 // Timer counts up.
-#define CNTDIR_DOWN 1 // Timer counts down.
-
-// ClkEnab values:
-#define CLKENAB_ALWAYS 0 // Clock always enabled.
-#define CLKENAB_INDEX 1 // Clock is enabled by index.
-
-// ClkMult values:
-#define CLKMULT_4X 0 // 4x clock multiplier.
-#define CLKMULT_2X 1 // 2x clock multiplier.
-#define CLKMULT_1X 2 // 1x clock multiplier.
-
-// Bit Field positions in COUNTER_SETUP structure:
-#define BF_LOADSRC 9 // Preload trigger.
-#define BF_INDXSRC 7 // Index source.
-#define BF_INDXPOL 6 // Index polarity.
-#define BF_CLKSRC 4 // Clock source.
-#define BF_CLKPOL 3 // Clock polarity/count direction.
-#define BF_CLKMULT 1 // Clock multiplier.
-#define BF_CLKENAB 0 // Clock enable.
-
-// Enumerated counter operating modes specified by ClkSrc bit field in
-// a COUNTER_SETUP.
-
-#define CLKSRC_COUNTER 0 // Counter: ENC_C clock, ENC_D
- // direction.
-#define CLKSRC_TIMER 2 // Timer: SYS_C clock,
- // direction specified by
- // ClkPol.
-#define CLKSRC_EXTENDER 3 // Extender: OVR_A clock,
- // ENC_D direction.
-
-// Enumerated counter clock multipliers.
-
-#define MULT_X0 0x0003 // Supports no multipliers;
- // fixed physical multiplier =
- // 3.
-#define MULT_X1 0x0002 // Supports multiplier x1;
- // fixed physical multiplier =
- // 2.
-#define MULT_X2 0x0001 // Supports multipliers x1,
- // x2; physical multipliers =
- // 1 or 2.
-#define MULT_X4 0x0000 // Supports multipliers x1,
- // x2, x4; physical
- // multipliers = 0, 1 or 2.
-
-// Sanity-check limits for parameters.
-
-#define NUM_COUNTERS 6 // Maximum valid counter
- // logical channel number.
+/* DEBI page table constants. */
+#define DEBI_PAGE_DISABLE 0x00000000 /* Paging disable. */
+
+/* ******* EXTRA FROM OTHER SANSORAY * .h ******* */
+
+/* LoadSrc values: */
+#define LOADSRC_INDX 0 /* Preload core in response to */
+ /* Index. */
+#define LOADSRC_OVER 1 /* Preload core in response to */
+ /* Overflow. */
+#define LOADSRCB_OVERA 2 /* Preload B core in response */
+ /* to A Overflow. */
+#define LOADSRC_NONE 3 /* Never preload core. */
+
+/* IntSrc values: */
+#define INTSRC_NONE 0 /* Interrupts disabled. */
+#define INTSRC_OVER 1 /* Interrupt on Overflow. */
+#define INTSRC_INDX 2 /* Interrupt on Index. */
+#define INTSRC_BOTH 3 /* Interrupt on Index or Overflow. */
+
+/* LatchSrc values: */
+#define LATCHSRC_AB_READ 0 /* Latch on read. */
+#define LATCHSRC_A_INDXA 1 /* Latch A on A Index. */
+#define LATCHSRC_B_INDXB 2 /* Latch B on B Index. */
+#define LATCHSRC_B_OVERA 3 /* Latch B on A Overflow. */
+
+/* IndxSrc values: */
+#define INDXSRC_HARD 0 /* Hardware or software index. */
+#define INDXSRC_SOFT 1 /* Software index only. */
+
+/* IndxPol values: */
+#define INDXPOL_POS 0 /* Index input is active high. */
+#define INDXPOL_NEG 1 /* Index input is active low. */
+
+/* ClkSrc values: */
+#define CLKSRC_COUNTER 0 /* Counter mode. */
+#define CLKSRC_TIMER 2 /* Timer mode. */
+#define CLKSRC_EXTENDER 3 /* Extender mode. */
+
+/* ClkPol values: */
+#define CLKPOL_POS 0 /* Counter/Extender clock is */
+ /* active high. */
+#define CLKPOL_NEG 1 /* Counter/Extender clock is */
+ /* active low. */
+#define CNTDIR_UP 0 /* Timer counts up. */
+#define CNTDIR_DOWN 1 /* Timer counts down. */
+
+/* ClkEnab values: */
+#define CLKENAB_ALWAYS 0 /* Clock always enabled. */
+#define CLKENAB_INDEX 1 /* Clock is enabled by index. */
+
+/* ClkMult values: */
+#define CLKMULT_4X 0 /* 4x clock multiplier. */
+#define CLKMULT_2X 1 /* 2x clock multiplier. */
+#define CLKMULT_1X 2 /* 1x clock multiplier. */
+
+/* Bit Field positions in COUNTER_SETUP structure: */
+#define BF_LOADSRC 9 /* Preload trigger. */
+#define BF_INDXSRC 7 /* Index source. */
+#define BF_INDXPOL 6 /* Index polarity. */
+#define BF_CLKSRC 4 /* Clock source. */
+#define BF_CLKPOL 3 /* Clock polarity/count direction. */
+#define BF_CLKMULT 1 /* Clock multiplier. */
+#define BF_CLKENAB 0 /* Clock enable. */
+
+/* Enumerated counter operating modes specified by ClkSrc bit field in */
+/* a COUNTER_SETUP. */
+
+#define CLKSRC_COUNTER 0 /* Counter: ENC_C clock, ENC_D */
+ /* direction. */
+#define CLKSRC_TIMER 2 /* Timer: SYS_C clock, */
+ /* direction specified by */
+ /* ClkPol. */
+#define CLKSRC_EXTENDER 3 /* Extender: OVR_A clock, */
+ /* ENC_D direction. */
+
+/* Enumerated counter clock multipliers. */
+
+#define MULT_X0 0x0003 /* Supports no multipliers; */
+ /* fixed physical multiplier = */
+ /* 3. */
+#define MULT_X1 0x0002 /* Supports multiplier x1; */
+ /* fixed physical multiplier = */
+ /* 2. */
+#define MULT_X2 0x0001 /* Supports multipliers x1, */
+ /* x2; physical multipliers = */
+ /* 1 or 2. */
+#define MULT_X4 0x0000 /* Supports multipliers x1, */
+ /* x2, x4; physical */
+ /* multipliers = 0, 1 or 2. */
+
+/* Sanity-check limits for parameters. */
+
+#define NUM_COUNTERS 6 /* Maximum valid counter */
+ /* logical channel number. */
#define NUM_INTSOURCES 4
#define NUM_LATCHSOURCES 4
#define NUM_CLKMULTS 4
@@ -708,59 +653,59 @@
#define NUM_INDEXSOURCES 2
#define NUM_LOADTRIGS 4
-// Bit field positions in CRA and CRB counter control registers.
-
-// Bit field positions in CRA:
-#define CRABIT_INDXSRC_B 14 // B index source.
-#define CRABIT_CLKSRC_B 12 // B clock source.
-#define CRABIT_INDXPOL_A 11 // A index polarity.
-#define CRABIT_LOADSRC_A 9 // A preload trigger.
-#define CRABIT_CLKMULT_A 7 // A clock multiplier.
-#define CRABIT_INTSRC_A 5 // A interrupt source.
-#define CRABIT_CLKPOL_A 4 // A clock polarity.
-#define CRABIT_INDXSRC_A 2 // A index source.
-#define CRABIT_CLKSRC_A 0 // A clock source.
-
-// Bit field positions in CRB:
-#define CRBBIT_INTRESETCMD 15 // Interrupt reset command.
-#define CRBBIT_INTRESET_B 14 // B interrupt reset enable.
-#define CRBBIT_INTRESET_A 13 // A interrupt reset enable.
-#define CRBBIT_CLKENAB_A 12 // A clock enable.
-#define CRBBIT_INTSRC_B 10 // B interrupt source.
-#define CRBBIT_LATCHSRC 8 // A/B latch source.
-#define CRBBIT_LOADSRC_B 6 // B preload trigger.
-#define CRBBIT_CLKMULT_B 3 // B clock multiplier.
-#define CRBBIT_CLKENAB_B 2 // B clock enable.
-#define CRBBIT_INDXPOL_B 1 // B index polarity.
-#define CRBBIT_CLKPOL_B 0 // B clock polarity.
-
-// Bit field masks for CRA and CRB.
-
-#define CRAMSK_INDXSRC_B ( (uint16_t)( 3 << CRABIT_INDXSRC_B) )
-#define CRAMSK_CLKSRC_B ( (uint16_t)( 3 << CRABIT_CLKSRC_B) )
-#define CRAMSK_INDXPOL_A ( (uint16_t)( 1 << CRABIT_INDXPOL_A) )
-#define CRAMSK_LOADSRC_A ( (uint16_t)( 3 << CRABIT_LOADSRC_A) )
-#define CRAMSK_CLKMULT_A ( (uint16_t)( 3 << CRABIT_CLKMULT_A) )
-#define CRAMSK_INTSRC_A ( (uint16_t)( 3 << CRABIT_INTSRC_A) )
-#define CRAMSK_CLKPOL_A ( (uint16_t)( 3 << CRABIT_CLKPOL_A) )
-#define CRAMSK_INDXSRC_A ( (uint16_t)( 3 << CRABIT_INDXSRC_A) )
-#define CRAMSK_CLKSRC_A ( (uint16_t)( 3 << CRABIT_CLKSRC_A) )
-
-#define CRBMSK_INTRESETCMD ( (uint16_t)( 1 << CRBBIT_INTRESETCMD) )
-#define CRBMSK_INTRESET_B ( (uint16_t)( 1 << CRBBIT_INTRESET_B) )
-#define CRBMSK_INTRESET_A ( (uint16_t)( 1 << CRBBIT_INTRESET_A) )
-#define CRBMSK_CLKENAB_A ( (uint16_t)( 1 << CRBBIT_CLKENAB_A) )
-#define CRBMSK_INTSRC_B ( (uint16_t)( 3 << CRBBIT_INTSRC_B) )
-#define CRBMSK_LATCHSRC ( (uint16_t)( 3 << CRBBIT_LATCHSRC) )
-#define CRBMSK_LOADSRC_B ( (uint16_t)( 3 << CRBBIT_LOADSRC_B) )
-#define CRBMSK_CLKMULT_B ( (uint16_t)( 3 << CRBBIT_CLKMULT_B) )
-#define CRBMSK_CLKENAB_B ( (uint16_t)( 1 << CRBBIT_CLKENAB_B) )
-#define CRBMSK_INDXPOL_B ( (uint16_t)( 1 << CRBBIT_INDXPOL_B) )
-#define CRBMSK_CLKPOL_B ( (uint16_t)( 1 << CRBBIT_CLKPOL_B) )
-
-#define CRBMSK_INTCTRL ( CRBMSK_INTRESETCMD | CRBMSK_INTRESET_A | CRBMSK_INTRESET_B ) // Interrupt reset control bits.
-
-// Bit field positions for standardized SETUP structure.
+/* Bit field positions in CRA and CRB counter control registers. */
+
+/* Bit field positions in CRA: */
+#define CRABIT_INDXSRC_B 14 /* B index source. */
+#define CRABIT_CLKSRC_B 12 /* B clock source. */
+#define CRABIT_INDXPOL_A 11 /* A index polarity. */
+#define CRABIT_LOADSRC_A 9 /* A preload trigger. */
+#define CRABIT_CLKMULT_A 7 /* A clock multiplier. */
+#define CRABIT_INTSRC_A 5 /* A interrupt source. */
+#define CRABIT_CLKPOL_A 4 /* A clock polarity. */
+#define CRABIT_INDXSRC_A 2 /* A index source. */
+#define CRABIT_CLKSRC_A 0 /* A clock source. */
+
+/* Bit field positions in CRB: */
+#define CRBBIT_INTRESETCMD 15 /* Interrupt reset command. */
+#define CRBBIT_INTRESET_B 14 /* B interrupt reset enable. */
+#define CRBBIT_INTRESET_A 13 /* A interrupt reset enable. */
+#define CRBBIT_CLKENAB_A 12 /* A clock enable. */
+#define CRBBIT_INTSRC_B 10 /* B interrupt source. */
+#define CRBBIT_LATCHSRC 8 /* A/B latch source. */
+#define CRBBIT_LOADSRC_B 6 /* B preload trigger. */
+#define CRBBIT_CLKMULT_B 3 /* B clock multiplier. */
+#define CRBBIT_CLKENAB_B 2 /* B clock enable. */
+#define CRBBIT_INDXPOL_B 1 /* B index polarity. */
+#define CRBBIT_CLKPOL_B 0 /* B clock polarity. */
+
+/* Bit field masks for CRA and CRB. */
+
+#define CRAMSK_INDXSRC_B ((uint16_t)(3 << CRABIT_INDXSRC_B))
+#define CRAMSK_CLKSRC_B ((uint16_t)(3 << CRABIT_CLKSRC_B))
+#define CRAMSK_INDXPOL_A ((uint16_t)(1 << CRABIT_INDXPOL_A))
+#define CRAMSK_LOADSRC_A ((uint16_t)(3 << CRABIT_LOADSRC_A))
+#define CRAMSK_CLKMULT_A ((uint16_t)(3 << CRABIT_CLKMULT_A))
+#define CRAMSK_INTSRC_A ((uint16_t)(3 << CRABIT_INTSRC_A))
+#define CRAMSK_CLKPOL_A ((uint16_t)(3 << CRABIT_CLKPOL_A))
+#define CRAMSK_INDXSRC_A ((uint16_t)(3 << CRABIT_INDXSRC_A))
+#define CRAMSK_CLKSRC_A ((uint16_t)(3 << CRABIT_CLKSRC_A))
+
+#define CRBMSK_INTRESETCMD ((uint16_t)(1 << CRBBIT_INTRESETCMD))
+#define CRBMSK_INTRESET_B ((uint16_t)(1 << CRBBIT_INTRESET_B))
+#define CRBMSK_INTRESET_A ((uint16_t)(1 << CRBBIT_INTRESET_A))
+#define CRBMSK_CLKENAB_A ((uint16_t)(1 << CRBBIT_CLKENAB_A))
+#define CRBMSK_INTSRC_B ((uint16_t)(3 << CRBBIT_INTSRC_B))
+#define CRBMSK_LATCHSRC ((uint16_t)(3 << CRBBIT_LATCHSRC))
+#define CRBMSK_LOADSRC_B ((uint16_t)(3 << CRBBIT_LOADSRC_B))
+#define CRBMSK_CLKMULT_B ((uint16_t)(3 << CRBBIT_CLKMULT_B))
+#define CRBMSK_CLKENAB_B ((uint16_t)(1 << CRBBIT_CLKENAB_B))
+#define CRBMSK_INDXPOL_B ((uint16_t)(1 << CRBBIT_INDXPOL_B))
+#define CRBMSK_CLKPOL_B ((uint16_t)(1 << CRBBIT_CLKPOL_B))
+
+#define CRBMSK_INTCTRL (CRBMSK_INTRESETCMD | CRBMSK_INTRESET_A | CRBMSK_INTRESET_B) /* Interrupt reset control bits. */
+
+/* Bit field positions for standardized SETUP structure. */
#define STDBIT_INTSRC 13
#define STDBIT_LATCHSRC 11
@@ -772,19 +717,18 @@
#define STDBIT_CLKMULT 1
#define STDBIT_CLKENAB 0
-// Bit field masks for standardized SETUP structure.
+/* Bit field masks for standardized SETUP structure. */
-#define STDMSK_INTSRC ( (uint16_t)( 3 << STDBIT_INTSRC ) )
-#define STDMSK_LATCHSRC ( (uint16_t)( 3 << STDBIT_LATCHSRC ) )
-#define STDMSK_LOADSRC ( (uint16_t)( 3 << STDBIT_LOADSRC ) )
-#define STDMSK_INDXSRC ( (uint16_t)( 1 << STDBIT_INDXSRC ) )
-#define STDMSK_INDXPOL ( (uint16_t)( 1 << STDBIT_INDXPOL ) )
-#define STDMSK_CLKSRC ( (uint16_t)( 3 << STDBIT_CLKSRC ) )
-#define STDMSK_CLKPOL ( (uint16_t)( 1 << STDBIT_CLKPOL ) )
-#define STDMSK_CLKMULT ( (uint16_t)( 3 << STDBIT_CLKMULT ) )
-#define STDMSK_CLKENAB ( (uint16_t)( 1 << STDBIT_CLKENAB ) )
+#define STDMSK_INTSRC ((uint16_t)(3 << STDBIT_INTSRC))
+#define STDMSK_LATCHSRC ((uint16_t)(3 << STDBIT_LATCHSRC))
+#define STDMSK_LOADSRC ((uint16_t)(3 << STDBIT_LOADSRC))
+#define STDMSK_INDXSRC ((uint16_t)(1 << STDBIT_INDXSRC))
+#define STDMSK_INDXPOL ((uint16_t)(1 << STDBIT_INDXPOL))
+#define STDMSK_CLKSRC ((uint16_t)(3 << STDBIT_CLKSRC))
+#define STDMSK_CLKPOL ((uint16_t)(1 << STDBIT_CLKPOL))
+#define STDMSK_CLKMULT ((uint16_t)(3 << STDBIT_CLKMULT))
+#define STDMSK_CLKENAB ((uint16_t)(1 << STDBIT_CLKENAB))
-//////////////////////////////////////////////////////////
/* typedef struct indexCounter */
/* { */
@@ -795,8 +739,8 @@
/* unsigned int enc; */
/* }CallCounter; */
-typedef struct bufferDMA {
+struct bufferDMA {
dma_addr_t PhysicalBase;
void *LogicalBase;
uint32_t DMAHandle;
-} DMABUF;
+};
diff --git a/drivers/staging/comedi/drivers/serial2002.c b/drivers/staging/comedi/drivers/serial2002.c
new file mode 100644
index 000000000000..844fd5e2e3a9
--- /dev/null
+++ b/drivers/staging/comedi/drivers/serial2002.c
@@ -0,0 +1,870 @@
+/*
+ comedi/drivers/serial2002.c
+ Skeleton code for a Comedi driver
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2002 Anders Blomdell <anders.blomdell@control.lth.se>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/*
+Driver: serial2002
+Description: Driver for serial connected hardware
+Devices:
+Author: Anders Blomdell
+Updated: Fri, 7 Jun 2002 12:56:45 -0700
+Status: in development
+
+*/
+
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+#include <linux/ioport.h>
+
+#include <asm/termios.h>
+#include <asm/ioctls.h>
+#include <linux/serial.h>
+#include <linux/poll.h>
+
+/*
+ * Board descriptions for two imaginary boards. Describing the
+ * boards in this way is optional, and completely driver-dependent.
+ * Some drivers use arrays such as this, other do not.
+ */
+struct serial2002_board {
+ const char *name;
+};
+
+static const struct serial2002_board serial2002_boards[] = {
+ {
+ name: "serial2002"}
+};
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct serial2002_board *)dev->board_ptr)
+
+struct serial2002_range_table_t {
+
+ // HACK...
+ int length;
+ struct comedi_krange range;
+};
+
+
+struct serial2002_private {
+
+ int port; // /dev/ttyS<port>
+ int speed; // baudrate
+ struct file *tty;
+ unsigned int ao_readback[32];
+ unsigned char digital_in_mapping[32];
+ unsigned char digital_out_mapping[32];
+ unsigned char analog_in_mapping[32];
+ unsigned char analog_out_mapping[32];
+ unsigned char encoder_in_mapping[32];
+ struct serial2002_range_table_t in_range[32], out_range[32];
+};
+
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((struct serial2002_private *)dev->private)
+
+static int serial2002_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int serial2002_detach(struct comedi_device * dev);
+struct comedi_driver driver_serial2002 = {
+ driver_name:"serial2002",
+ module:THIS_MODULE,
+ attach:serial2002_attach,
+ detach:serial2002_detach,
+ board_name:&serial2002_boards[0].name,
+ offset:sizeof(struct serial2002_board),
+ num_names:sizeof(serial2002_boards) / sizeof(struct serial2002_board),
+};
+
+static int serial2002_di_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int serial2002_do_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int serial2002_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int serial2002_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int serial2002_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+
+struct serial_data {
+ enum { is_invalid, is_digital, is_channel } kind;
+ int index;
+ unsigned long value;
+};
+
+static long tty_ioctl(struct file *f, unsigned op, unsigned long param)
+{
+#ifdef HAVE_UNLOCKED_IOCTL
+ if (f->f_op->unlocked_ioctl) {
+ return f->f_op->unlocked_ioctl(f, op, param);
+ }
+#endif
+ if (f->f_op->ioctl) {
+ return f->f_op->ioctl(f->f_dentry->d_inode, f, op, param);
+ }
+ return -ENOSYS;
+}
+
+static int tty_write(struct file *f, unsigned char *buf, int count)
+{
+ int result;
+ mm_segment_t oldfs;
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ f->f_pos = 0;
+ result = f->f_op->write(f, buf, count, &f->f_pos);
+ set_fs(oldfs);
+ return result;
+}
+
+#if 0
+/*
+ * On 2.6.26.3 this occaisonally gave me page faults, worked around by
+ * settings.c_cc[VMIN] = 0; settings.c_cc[VTIME] = 0
+ */
+static int tty_available(struct file *f)
+{
+ long result = 0;
+ mm_segment_t oldfs;
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ tty_ioctl(f, FIONREAD, (unsigned long)&result);
+ set_fs(oldfs);
+ return result;
+}
+#endif
+
+static int tty_read(struct file *f, int timeout)
+{
+ int result;
+
+ result = -1;
+ if (!IS_ERR(f)) {
+ mm_segment_t oldfs;
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ if (f->f_op->poll) {
+ struct poll_wqueues table;
+ struct timeval start, now;
+
+ do_gettimeofday(&start);
+ poll_initwait(&table);
+ while (1) {
+ long elapsed;
+ int mask;
+
+ mask = f->f_op->poll(f, &table.pt);
+ if (mask & (POLLRDNORM | POLLRDBAND | POLLIN |
+ POLLHUP | POLLERR)) {
+ break;
+ }
+ do_gettimeofday(&now);
+ elapsed =
+ (1000000 * (now.tv_sec - start.tv_sec) +
+ now.tv_usec - start.tv_usec);
+ if (elapsed > timeout) {
+ break;
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(((timeout -
+ elapsed) * HZ) / 10000);
+ }
+ poll_freewait(&table);
+ {
+ unsigned char ch;
+
+ f->f_pos = 0;
+ if (f->f_op->read(f, &ch, 1, &f->f_pos) == 1) {
+ result = ch;
+ }
+ }
+ } else {
+ /* Device does not support poll, busy wait */
+ int retries = 0;
+ while (1) {
+ unsigned char ch;
+
+ retries++;
+ if (retries >= timeout) {
+ break;
+ }
+
+ f->f_pos = 0;
+ if (f->f_op->read(f, &ch, 1, &f->f_pos) == 1) {
+ result = ch;
+ break;
+ }
+ comedi_udelay(100);
+ }
+ }
+ set_fs(oldfs);
+ }
+ return result;
+}
+
+static void tty_setspeed(struct file *f, int speed)
+{
+ mm_segment_t oldfs;
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ {
+ // Set speed
+ struct termios settings;
+
+ tty_ioctl(f, TCGETS, (unsigned long)&settings);
+// printk("Speed: %d\n", settings.c_cflag & (CBAUD | CBAUDEX));
+ settings.c_iflag = 0;
+ settings.c_oflag = 0;
+ settings.c_lflag = 0;
+ settings.c_cflag = CLOCAL | CS8 | CREAD;
+ settings.c_cc[VMIN] = 0;
+ settings.c_cc[VTIME] = 0;
+ switch (speed) {
+ case 2400:{
+ settings.c_cflag |= B2400;
+ }
+ break;
+ case 4800:{
+ settings.c_cflag |= B4800;
+ }
+ break;
+ case 9600:{
+ settings.c_cflag |= B9600;
+ }
+ break;
+ case 19200:{
+ settings.c_cflag |= B19200;
+ }
+ break;
+ case 38400:{
+ settings.c_cflag |= B38400;
+ }
+ break;
+ case 57600:{
+ settings.c_cflag |= B57600;
+ }
+ break;
+ case 115200:{
+ settings.c_cflag |= B115200;
+ }
+ break;
+ default:{
+ settings.c_cflag |= B9600;
+ }
+ break;
+ }
+ tty_ioctl(f, TCSETS, (unsigned long)&settings);
+// printk("Speed: %d\n", settings.c_cflag & (CBAUD | CBAUDEX));
+ }
+ {
+ // Set low latency
+ struct serial_struct settings;
+
+ tty_ioctl(f, TIOCGSERIAL, (unsigned long)&settings);
+ settings.flags |= ASYNC_LOW_LATENCY;
+ tty_ioctl(f, TIOCSSERIAL, (unsigned long)&settings);
+ }
+
+ set_fs(oldfs);
+}
+
+static void poll_digital(struct file *f, int channel)
+{
+ char cmd;
+
+ cmd = 0x40 | (channel & 0x1f);
+ tty_write(f, &cmd, 1);
+}
+
+static void poll_channel(struct file *f, int channel)
+{
+ char cmd;
+
+ cmd = 0x60 | (channel & 0x1f);
+ tty_write(f, &cmd, 1);
+}
+
+static struct serial_data serial_read(struct file *f, int timeout)
+{
+ struct serial_data result;
+ int length;
+
+ result.kind = is_invalid;
+ result.index = 0;
+ result.value = 0;
+ length = 0;
+ while (1) {
+ int data = tty_read(f, timeout);
+
+ length++;
+ if (data < 0) {
+ printk("serial2002 error\n");
+ break;
+ } else if (data & 0x80) {
+ result.value = (result.value << 7) | (data & 0x7f);
+ } else {
+ if (length == 1) {
+ switch ((data >> 5) & 0x03) {
+ case 0:{
+ result.value = 0;
+ result.kind = is_digital;
+ }
+ break;
+ case 1:{
+ result.value = 1;
+ result.kind = is_digital;
+ }
+ break;
+ }
+ } else {
+ result.value =
+ (result.
+ value << 2) | ((data & 0x60) >> 5);
+ result.kind = is_channel;
+ }
+ result.index = data & 0x1f;
+ break;
+ }
+ }
+ return result;
+
+}
+
+static void serial_write(struct file *f, struct serial_data data)
+{
+ if (data.kind == is_digital) {
+ unsigned char ch =
+ ((data.value << 5) & 0x20) | (data.index & 0x1f);
+ tty_write(f, &ch, 1);
+ } else {
+ unsigned char ch[6];
+ int i = 0;
+ if (data.value >= (1L << 30)) {
+ ch[i] = 0x80 | ((data.value >> 30) & 0x03);
+ i++;
+ }
+ if (data.value >= (1L << 23)) {
+ ch[i] = 0x80 | ((data.value >> 23) & 0x7f);
+ i++;
+ }
+ if (data.value >= (1L << 16)) {
+ ch[i] = 0x80 | ((data.value >> 16) & 0x7f);
+ i++;
+ }
+ if (data.value >= (1L << 9)) {
+ ch[i] = 0x80 | ((data.value >> 9) & 0x7f);
+ i++;
+ }
+ ch[i] = 0x80 | ((data.value >> 2) & 0x7f);
+ i++;
+ ch[i] = ((data.value << 5) & 0x60) | (data.index & 0x1f);
+ i++;
+ tty_write(f, ch, i);
+ }
+}
+
+static void serial_2002_open(struct comedi_device * dev)
+{
+ char port[20];
+
+ sprintf(port, "/dev/ttyS%d", devpriv->port);
+ devpriv->tty = filp_open(port, 0, O_RDWR);
+ if (IS_ERR(devpriv->tty)) {
+ printk("serial_2002: file open error = %ld\n",
+ PTR_ERR(devpriv->tty));
+ } else {
+ struct config_t {
+
+ int kind;
+ int bits;
+ int min;
+ int max;
+ };
+
+ struct config_t dig_in_config[32];
+ struct config_t dig_out_config[32];
+ struct config_t chan_in_config[32];
+ struct config_t chan_out_config[32];
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ dig_in_config[i].kind = 0;
+ dig_in_config[i].bits = 0;
+ dig_in_config[i].min = 0;
+ dig_in_config[i].max = 0;
+ dig_out_config[i].kind = 0;
+ dig_out_config[i].bits = 0;
+ dig_out_config[i].min = 0;
+ dig_out_config[i].max = 0;
+ chan_in_config[i].kind = 0;
+ chan_in_config[i].bits = 0;
+ chan_in_config[i].min = 0;
+ chan_in_config[i].max = 0;
+ chan_out_config[i].kind = 0;
+ chan_out_config[i].bits = 0;
+ chan_out_config[i].min = 0;
+ chan_out_config[i].max = 0;
+ }
+
+ tty_setspeed(devpriv->tty, devpriv->speed);
+ poll_channel(devpriv->tty, 31); // Start reading configuration
+ while (1) {
+ struct serial_data data;
+
+ data = serial_read(devpriv->tty, 1000);
+ if (data.kind != is_channel || data.index != 31
+ || !(data.value & 0xe0)) {
+ break;
+ } else {
+ int command, channel, kind;
+ struct config_t *cur_config = 0;
+
+ channel = data.value & 0x1f;
+ kind = (data.value >> 5) & 0x7;
+ command = (data.value >> 8) & 0x3;
+ switch (kind) {
+ case 1:{
+ cur_config = dig_in_config;
+ }
+ break;
+ case 2:{
+ cur_config = dig_out_config;
+ }
+ break;
+ case 3:{
+ cur_config = chan_in_config;
+ }
+ break;
+ case 4:{
+ cur_config = chan_out_config;
+ }
+ break;
+ case 5:{
+ cur_config = chan_in_config;
+ }
+ break;
+ }
+
+ if (cur_config) {
+ cur_config[channel].kind = kind;
+ switch (command) {
+ case 0:{
+ cur_config[channel].
+ bits =
+ (data.
+ value >> 10) &
+ 0x3f;
+ }
+ break;
+ case 1:{
+ int unit, sign, min;
+ unit = (data.
+ value >> 10) &
+ 0x7;
+ sign = (data.
+ value >> 13) &
+ 0x1;
+ min = (data.
+ value >> 14) &
+ 0xfffff;
+
+ switch (unit) {
+ case 0:{
+ min = min * 1000000;
+ }
+ break;
+ case 1:{
+ min = min * 1000;
+ }
+ break;
+ case 2:{
+ min = min * 1;
+ }
+ break;
+ }
+ if (sign) {
+ min = -min;
+ }
+ cur_config[channel].
+ min = min;
+ }
+ break;
+ case 2:{
+ int unit, sign, max;
+ unit = (data.
+ value >> 10) &
+ 0x7;
+ sign = (data.
+ value >> 13) &
+ 0x1;
+ max = (data.
+ value >> 14) &
+ 0xfffff;
+
+ switch (unit) {
+ case 0:{
+ max = max * 1000000;
+ }
+ break;
+ case 1:{
+ max = max * 1000;
+ }
+ break;
+ case 2:{
+ max = max * 1;
+ }
+ break;
+ }
+ if (sign) {
+ max = -max;
+ }
+ cur_config[channel].
+ max = max;
+ }
+ break;
+ }
+ }
+ }
+ }
+ for (i = 0; i <= 4; i++) {
+ // Fill in subdev data
+ struct config_t *c;
+ unsigned char *mapping = 0;
+ struct serial2002_range_table_t *range = 0;
+ int kind = 0;
+
+ switch (i) {
+ case 0:{
+ c = dig_in_config;
+ mapping = devpriv->digital_in_mapping;
+ kind = 1;
+ }
+ break;
+ case 1:{
+ c = dig_out_config;
+ mapping = devpriv->digital_out_mapping;
+ kind = 2;
+ }
+ break;
+ case 2:{
+ c = chan_in_config;
+ mapping = devpriv->analog_in_mapping;
+ range = devpriv->in_range;
+ kind = 3;
+ }
+ break;
+ case 3:{
+ c = chan_out_config;
+ mapping = devpriv->analog_out_mapping;
+ range = devpriv->out_range;
+ kind = 4;
+ }
+ break;
+ case 4:{
+ c = chan_in_config;
+ mapping = devpriv->encoder_in_mapping;
+ range = devpriv->in_range;
+ kind = 5;
+ }
+ break;
+ default:{
+ c = 0;
+ }
+ break;
+ }
+ if (c) {
+ struct comedi_subdevice *s;
+ const struct comedi_lrange **range_table_list = NULL;
+ unsigned int *maxdata_list;
+ int j, chan;
+
+ for (chan = 0, j = 0; j < 32; j++) {
+ if (c[j].kind == kind) {
+ chan++;
+ }
+ }
+ s = &dev->subdevices[i];
+ s->n_chan = chan;
+ s->maxdata = 0;
+ if (s->maxdata_list) {
+ kfree(s->maxdata_list);
+ }
+ s->maxdata_list = maxdata_list =
+ kmalloc(sizeof(unsigned int) * s->n_chan,
+ GFP_KERNEL);
+ if (s->range_table_list) {
+ kfree(s->range_table_list);
+ }
+ if (range) {
+ s->range_table = 0;
+ s->range_table_list = range_table_list =
+ kmalloc(sizeof
+ (struct serial2002_range_table_t) *
+ s->n_chan, GFP_KERNEL);
+ }
+ for (chan = 0, j = 0; j < 32; j++) {
+ if (c[j].kind == kind) {
+ if (mapping) {
+ mapping[chan] = j;
+ }
+ if (range) {
+ range[j].length = 1;
+ range[j].range.min =
+ c[j].min;
+ range[j].range.max =
+ c[j].max;
+ range_table_list[chan] =
+ (const struct
+ comedi_lrange *)
+ &range[j];
+ }
+ maxdata_list[chan] =
+ ((long long)1 << c[j].
+ bits) - 1;
+ chan++;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void serial_2002_close(struct comedi_device * dev)
+{
+ if (!IS_ERR(devpriv->tty) && (devpriv->tty != 0)) {
+ filp_close(devpriv->tty, 0);
+ }
+}
+
+static int serial2002_di_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int chan;
+
+ chan = devpriv->digital_in_mapping[CR_CHAN(insn->chanspec)];
+ for (n = 0; n < insn->n; n++) {
+ struct serial_data read;
+
+ poll_digital(devpriv->tty, chan);
+ while (1) {
+ read = serial_read(devpriv->tty, 1000);
+ if (read.kind != is_digital || read.index == chan) {
+ break;
+ }
+ }
+ data[n] = read.value;
+ }
+ return n;
+}
+
+static int serial2002_do_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int chan;
+
+ chan = devpriv->digital_out_mapping[CR_CHAN(insn->chanspec)];
+ for (n = 0; n < insn->n; n++) {
+ struct serial_data write;
+
+ write.kind = is_digital;
+ write.index = chan;
+ write.value = data[n];
+ serial_write(devpriv->tty, write);
+ }
+ return n;
+}
+
+static int serial2002_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int chan;
+
+ chan = devpriv->analog_in_mapping[CR_CHAN(insn->chanspec)];
+ for (n = 0; n < insn->n; n++) {
+ struct serial_data read;
+
+ poll_channel(devpriv->tty, chan);
+ while (1) {
+ read = serial_read(devpriv->tty, 1000);
+ if (read.kind != is_channel || read.index == chan) {
+ break;
+ }
+ }
+ data[n] = read.value;
+ }
+ return n;
+}
+
+static int serial2002_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int chan;
+
+ chan = devpriv->analog_out_mapping[CR_CHAN(insn->chanspec)];
+ for (n = 0; n < insn->n; n++) {
+ struct serial_data write;
+
+ write.kind = is_channel;
+ write.index = chan;
+ write.value = data[n];
+ serial_write(devpriv->tty, write);
+ devpriv->ao_readback[chan] = data[n];
+ }
+ return n;
+}
+
+static int serial2002_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (n = 0; n < insn->n; n++) {
+ data[n] = devpriv->ao_readback[chan];
+ }
+
+ return n;
+}
+
+static int serial2002_ei_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n;
+ int chan;
+
+ chan = devpriv->encoder_in_mapping[CR_CHAN(insn->chanspec)];
+ for (n = 0; n < insn->n; n++) {
+ struct serial_data read;
+
+ poll_channel(devpriv->tty, chan);
+ while (1) {
+ read = serial_read(devpriv->tty, 1000);
+ if (read.kind != is_channel || read.index == chan) {
+ break;
+ }
+ }
+ data[n] = read.value;
+ }
+ return n;
+}
+
+static int serial2002_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+
+ printk("comedi%d: serial2002: ", dev->minor);
+ dev->board_name = thisboard->name;
+ if (alloc_private(dev, sizeof(struct serial2002_private)) < 0) {
+ return -ENOMEM;
+ }
+ dev->open = serial_2002_open;
+ dev->close = serial_2002_close;
+ devpriv->port = it->options[0];
+ devpriv->speed = it->options[1];
+ printk("/dev/ttyS%d @ %d\n", devpriv->port, devpriv->speed);
+
+ if (alloc_subdevices(dev, 5) < 0)
+ return -ENOMEM;
+
+ /* digital input subdevice */
+ s = dev->subdevices + 0;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_read = &serial2002_di_rinsn;
+
+ /* digital output subdevice */
+ s = dev->subdevices + 1;
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITEABLE;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_write = &serial2002_do_winsn;
+
+ /* analog input subdevice */
+ s = dev->subdevices + 2;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = 0;
+ s->insn_read = &serial2002_ai_rinsn;
+
+ /* analog output subdevice */
+ s = dev->subdevices + 3;
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITEABLE;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = 0;
+ s->insn_write = &serial2002_ao_winsn;
+ s->insn_read = &serial2002_ao_rinsn;
+
+ /* encoder input subdevice */
+ s = dev->subdevices + 4;
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = 0;
+ s->insn_read = &serial2002_ei_rinsn;
+
+ return 1;
+}
+
+static int serial2002_detach(struct comedi_device * dev)
+{
+ struct comedi_subdevice *s;
+ int i;
+
+ printk("comedi%d: serial2002: remove\n", dev->minor);
+ for (i = 0; i < 4; i++) {
+ s = &dev->subdevices[i];
+ if (s->maxdata_list) {
+ kfree(s->maxdata_list);
+ }
+ if (s->range_table_list) {
+ kfree(s->range_table_list);
+ }
+ }
+ return 0;
+}
+
+COMEDI_INITCLEANUP(driver_serial2002);
diff --git a/drivers/staging/comedi/drivers/skel.c b/drivers/staging/comedi/drivers/skel.c
new file mode 100644
index 000000000000..c7700d134cc1
--- /dev/null
+++ b/drivers/staging/comedi/drivers/skel.c
@@ -0,0 +1,624 @@
+/*
+ comedi/drivers/skel.c
+ Skeleton code for a Comedi driver
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: skel
+Description: Skeleton driver, an example for driver writers
+Devices:
+Author: ds
+Updated: Mon, 18 Mar 2002 15:34:01 -0800
+Status: works
+
+This driver is a documented example on how Comedi drivers are
+written.
+
+Configuration Options:
+ none
+*/
+
+/*
+ * The previous block comment is used to automatically generate
+ * documentation in Comedi and Comedilib. The fields:
+ *
+ * Driver: the name of the driver
+ * Description: a short phrase describing the driver. Don't list boards.
+ * Devices: a full list of the boards that attempt to be supported by
+ * the driver. Format is "(manufacturer) board name [comedi name]",
+ * where comedi_name is the name that is used to configure the board.
+ * See the comment near board_name: in the struct comedi_driver structure
+ * below. If (manufacturer) or [comedi name] is missing, the previous
+ * value is used.
+ * Author: you
+ * Updated: date when the _documentation_ was last updated. Use 'date -R'
+ * to get a value for this.
+ * Status: a one-word description of the status. Valid values are:
+ * works - driver works correctly on most boards supported, and
+ * passes comedi_test.
+ * unknown - unknown. Usually put there by ds.
+ * experimental - may not work in any particular release. Author
+ * probably wants assistance testing it.
+ * bitrotten - driver has not been update in a long time, probably
+ * doesn't work, and probably is missing support for significant
+ * Comedi interface features.
+ * untested - author probably wrote it "blind", and is believed to
+ * work, but no confirmation.
+ *
+ * These headers should be followed by a blank line, and any comments
+ * you wish to say about the driver. The comment area is the place
+ * to put any known bugs, limitations, unsupported features, supported
+ * command triggers, whether or not commands are supported on particular
+ * subdevices, etc.
+ *
+ * Somewhere in the comment should be information about configuration
+ * options that are used with comedi_config.
+ */
+
+#include "../comedidev.h"
+
+#include <linux/pci.h> /* for PCI devices */
+
+/* Imaginary registers for the imaginary board */
+
+#define SKEL_SIZE 0
+
+#define SKEL_START_AI_CONV 0
+#define SKEL_AI_READ 0
+
+/*
+ * Board descriptions for two imaginary boards. Describing the
+ * boards in this way is optional, and completely driver-dependent.
+ * Some drivers use arrays such as this, other do not.
+ */
+struct skel_board {
+ const char *name;
+ int ai_chans;
+ int ai_bits;
+ int have_dio;
+};
+
+static const struct skel_board skel_boards[] = {
+ {
+ name: "skel-100",
+ ai_chans:16,
+ ai_bits: 12,
+ have_dio:1,
+ },
+ {
+ name: "skel-200",
+ ai_chans:8,
+ ai_bits: 16,
+ have_dio:0,
+ },
+};
+
+/* This is used by modprobe to translate PCI IDs to drivers. Should
+ * only be used for PCI and ISA-PnP devices */
+/* Please add your PCI vendor ID to comedidev.h, and it will be forwarded
+ * upstream. */
+#define PCI_VENDOR_ID_SKEL 0xdafe
+static DEFINE_PCI_DEVICE_TABLE(skel_pci_table) = {
+ {PCI_VENDOR_ID_SKEL, 0x0100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_SKEL, 0x0200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, skel_pci_table);
+
+/*
+ * Useful for shorthand access to the particular board structure
+ */
+#define thisboard ((const struct skel_board *)dev->board_ptr)
+
+/* 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 struct comedi_device struct. */
+struct skel_private {
+
+ int data;
+
+ /* would be useful for a PCI device */
+ struct pci_dev *pci_dev;
+
+ /* Used for AO readback */
+ unsigned int ao_readback[2];
+};
+
+/*
+ * most drivers define the following macro to make it easy to
+ * access the private structure.
+ */
+#define devpriv ((struct skel_private *)dev->private)
+
+/*
+ * The struct comedi_driver structure tells the Comedi core module
+ * which functions to call to configure/deconfigure (attach/detach)
+ * the board, and also about the kernel module that contains
+ * the device code.
+ */
+static int skel_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int skel_detach(struct comedi_device * dev);
+static struct comedi_driver driver_skel = {
+ driver_name:"dummy",
+ module:THIS_MODULE,
+ attach:skel_attach,
+ detach:skel_detach,
+/* It is not necessary to implement the following members if you are
+ * writing a driver for a ISA PnP or PCI card */
+ /* Most drivers will support multiple types of boards by
+ * having an array of board structures. These were defined
+ * in skel_boards[] above. Note that the element 'name'
+ * was first in the structure -- Comedi uses this fact to
+ * extract the name of the board without knowing any details
+ * about the structure except for its length.
+ * When a device is attached (by comedi_config), the name
+ * of the device is given to Comedi, and Comedi tries to
+ * match it by going through the list of board names. If
+ * there is a match, the address of the pointer is put
+ * into dev->board_ptr and driver->attach() is called.
+ *
+ * Note that these are not necessary if you can determine
+ * the type of board in software. ISA PnP, PCI, and PCMCIA
+ * devices are such boards.
+ */
+ board_name:&skel_boards[0].name,
+ offset:sizeof(struct skel_board),
+ num_names:sizeof(skel_boards) / sizeof(struct skel_board),
+};
+
+static int skel_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int skel_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int skel_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int skel_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int skel_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data);
+static int skel_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd);
+static int skel_ns_to_timer(unsigned int *ns, int round);
+
+/*
+ * Attach is called by the Comedi core to configure the driver
+ * for a particular board. If you specified a board_name array
+ * in the driver structure, dev->board_ptr contains that
+ * address.
+ */
+static int skel_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ struct comedi_subdevice *s;
+
+ printk("comedi%d: skel: ", dev->minor);
+
+/*
+ * If you can probe the device to determine what device in a series
+ * it is, this is the place to do it. Otherwise, dev->board_ptr
+ * should already be initialized.
+ */
+ /* dev->board_ptr = skel_probe(dev, it); */
+
+/*
+ * Initialize dev->board_name. Note that we can use the "thisboard"
+ * macro now, since we just initialized it in the last line.
+ */
+ dev->board_name = thisboard->name;
+
+/*
+ * Allocate the private structure area. alloc_private() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_private(dev, sizeof(struct skel_private)) < 0)
+ return -ENOMEM;
+
+/*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_subdevices(dev, 3) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ /* dev->read_subdev=s; */
+ /* analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ /* we support single-ended (ground) and differential */
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
+ s->n_chan = thisboard->ai_chans;
+ s->maxdata = (1 << thisboard->ai_bits) - 1;
+ s->range_table = &range_bipolar10;
+ s->len_chanlist = 16; /* This is the maximum chanlist length that
+ the board can handle */
+ s->insn_read = skel_ai_rinsn;
+/*
+* s->subdev_flags |= SDF_CMD_READ;
+* s->do_cmd = skel_ai_cmd;
+*/
+ s->do_cmdtest = skel_ai_cmdtest;
+
+ s = dev->subdevices + 1;
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 1;
+ s->maxdata = 0xffff;
+ s->range_table = &range_bipolar5;
+ s->insn_write = skel_ao_winsn;
+ s->insn_read = skel_ao_rinsn;
+
+ s = dev->subdevices + 2;
+ /* digital i/o subdevice */
+ if (thisboard->have_dio) {
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = skel_dio_insn_bits;
+ s->insn_config = skel_dio_insn_config;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ printk("attached\n");
+
+ return 0;
+}
+
+/*
+ * _detach is called to deconfigure a device. It should deallocate
+ * resources.
+ * This function is also called when _attach() fails, so it should be
+ * careful not to release resources that were not necessarily
+ * allocated by _attach(). dev->private and dev->subdevices are
+ * deallocated automatically by the core.
+ */
+static int skel_detach(struct comedi_device * dev)
+{
+ printk("comedi%d: skel: remove\n", dev->minor);
+
+ return 0;
+}
+
+/*
+ * "instructions" read/write data in "one-shot" or "software-triggered"
+ * mode.
+ */
+static int skel_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int n, i;
+ unsigned int d;
+ unsigned int status;
+
+ /* a typical programming sequence */
+
+ /* write channel to multiplexer */
+ /* outw(chan,dev->iobase + SKEL_MUX); */
+
+ /* don't wait for mux to settle */
+
+ /* convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion */
+ /* outw(0,dev->iobase + SKEL_CONVERT); */
+
+#define TIMEOUT 100
+ /* wait for conversion to end */
+ for (i = 0; i < TIMEOUT; i++) {
+ status = 1;
+ /* status = inb(dev->iobase + SKEL_STATUS); */
+ if (status)
+ break;
+ }
+ if (i == TIMEOUT) {
+ /* rt_printk() should be used instead of printk()
+ * whenever the code can be called from real-time. */
+ rt_printk("timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* read data */
+ /* d = inw(dev->iobase + SKEL_AI_DATA); */
+ d = 0;
+
+ /* mangle the data as necessary */
+ d ^= 1 << (thisboard->ai_bits - 1);
+
+ data[n] = d;
+ }
+
+ /* return the number of samples read/written */
+ return n;
+}
+
+static int skel_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_cmd * cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* cmdtest tests a particular command to see if it is valid.
+ * Using the cmdtest ioctl, a user can create a valid cmd
+ * and then have it executes by the cmd ioctl.
+ *
+ * cmdtest returns 1,2,3,4 or 0, depending on which tests
+ * the command passes. */
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
+
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
+
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
+
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
+
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ /* note that mutual compatiblity is not an issue here */
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_EXT)
+ err++;
+ if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
+ err++;
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
+#define MAX_SPEED 10000 /* in nanoseconds */
+#define MIN_SPEED 1000000000 /* in nanoseconds */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->scan_begin_arg < MAX_SPEED) {
+ cmd->scan_begin_arg = MAX_SPEED;
+ err++;
+ }
+ if (cmd->scan_begin_arg > MIN_SPEED) {
+ cmd->scan_begin_arg = MIN_SPEED;
+ err++;
+ }
+ } else {
+ /* external trigger */
+ /* should be level/edge, hi/lo specification here */
+ /* should specify multiple external triggers */
+ if (cmd->scan_begin_arg > 9) {
+ cmd->scan_begin_arg = 9;
+ err++;
+ }
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_arg < MAX_SPEED) {
+ cmd->convert_arg = MAX_SPEED;
+ err++;
+ }
+ if (cmd->convert_arg > MIN_SPEED) {
+ cmd->convert_arg = MIN_SPEED;
+ err++;
+ }
+ } else {
+ /* external trigger */
+ /* see above */
+ if (cmd->convert_arg > 9) {
+ cmd->convert_arg = 9;
+ err++;
+ }
+ }
+
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (cmd->stop_arg > 0x00ffffff) {
+ cmd->stop_arg = 0x00ffffff;
+ err++;
+ }
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ skel_ns_to_timer(&cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ skel_ns_to_timer(&cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->convert_arg)
+ err++;
+ if (cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->scan_begin_arg <
+ cmd->convert_arg * cmd->scan_end_arg) {
+ cmd->scan_begin_arg =
+ cmd->convert_arg * cmd->scan_end_arg;
+ err++;
+ }
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+/* This function doesn't require a particular form, this is just
+ * what happens to be used in some of the drivers. It should
+ * convert ns nanoseconds to a counter value suitable for programming
+ * the device. Also, it should adjust ns so that it cooresponds to
+ * the actual time that the device will use. */
+static int skel_ns_to_timer(unsigned int *ns, int round)
+{
+ /* trivial timer */
+ /* if your timing is done through two cascaded timers, the
+ * i8253_cascade_ns_to_timer() function in 8253.h can be
+ * very helpful. There are also i8254_load() and i8254_mm_load()
+ * which can be used to load values into the ubiquitous 8254 counters
+ */
+
+ return *ns;
+}
+
+static int skel_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ printk("skel_ao_winsn\n");
+ /* Writing a list of values to an AO channel is probably not
+ * very useful, but that's how the interface is defined. */
+ for (i = 0; i < insn->n; i++) {
+ /* a typical programming sequence */
+ /* outw(data[i],dev->iobase + SKEL_DA0 + chan); */
+ devpriv->ao_readback[chan] = data[i];
+ }
+
+ /* return the number of samples read/written */
+ return i;
+}
+
+/* AO subdevices should have a read insn as well as a write insn.
+ * Usually this means copying a value stored in devpriv. */
+static int skel_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = devpriv->ao_readback[chan];
+
+ return i;
+}
+
+/* DIO devices are slightly special. Although it is possible to
+ * implement the insn_read/insn_write interface, it is much more
+ * useful to applications if you implement the insn_bits interface.
+ * This allows packed reading/writing of the DIO channels. The
+ * comedi core can convert between insn_bits and insn_read/write */
+static int skel_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
+
+ /* The insn data is a mask in data[0] and the new data
+ * in data[1], each channel cooresponding to a bit. */
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+ /* Write out the new digital output lines */
+ /* outw(s->state,dev->iobase + SKEL_DIO); */
+ }
+
+ /* on return, data[1] contains the value of the digital
+ * input and output lines. */
+ /* data[1]=inw(dev->iobase + SKEL_DIO); */
+ /* or we could just return the software copy of the output values if
+ * it was a purely digital output subdevice */
+ /* data[1]=s->state; */
+
+ return 2;
+}
+
+static int skel_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+
+ /* The input or output configuration of each digital line is
+ * configured by a special insn_config instruction. chanspec
+ * contains the channel to be changed, and data[0] contains the
+ * value COMEDI_INPUT or COMEDI_OUTPUT. */
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= 1 << chan;
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~(1 << chan);
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (s->
+ io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ /* outw(s->io_bits,dev->iobase + SKEL_DIO_CONFIG); */
+
+ return insn->n;
+}
+
+/*
+ * A convenient macro that defines init_module() and cleanup_module(),
+ * as necessary.
+ */
+COMEDI_INITCLEANUP(driver_skel);
+/* If you are writing a PCI driver you should use COMEDI_PCI_INITCLEANUP instead.
+*/
+/* COMEDI_PCI_INITCLEANUP(driver_skel, skel_pci_table) */
diff --git a/drivers/staging/comedi/drivers/ssv_dnp.c b/drivers/staging/comedi/drivers/ssv_dnp.c
new file mode 100644
index 000000000000..1628d216cdd4
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ssv_dnp.c
@@ -0,0 +1,311 @@
+/*
+ comedi/drivers/ssv_dnp.c
+ generic comedi driver for SSV Embedded Systems' DIL/Net-PCs
+ Copyright (C) 2001 Robert Schwebel <robert@schwebel.de>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: ssv_dnp
+Description: SSV Embedded Systems DIL/Net-PC
+Author: Robert Schwebel <robert@schwebel.de>
+Devices: [SSV Embedded Systems] DIL/Net-PC 1486 (dnp-1486)
+Status: unknown
+*/
+
+/* include files ----------------------------------------------------------- */
+
+#include "../comedidev.h"
+
+/* Some global definitions: the registers of the DNP ----------------------- */
+/* */
+/* For port A and B the mode register has bits corresponding to the output */
+/* pins, where Bit-N = 0 -> input, Bit-N = 1 -> output. Note that bits */
+/* 4 to 7 correspond to pin 0..3 for port C data register. Ensure that bits */
+/* 0..3 remain unchanged! For details about Port C Mode Register see */
+/* the remarks in dnp_insn_config() below. */
+
+#define CSCIR 0x22 /* Chip Setup and Control Index Register */
+#define CSCDR 0x23 /* Chip Setup and Control Data Register */
+#define PAMR 0xa5 /* Port A Mode Register */
+#define PADR 0xa9 /* Port A Data Register */
+#define PBMR 0xa4 /* Port B Mode Register */
+#define PBDR 0xa8 /* Port B Data Register */
+#define PCMR 0xa3 /* Port C Mode Register */
+#define PCDR 0xa7 /* Port C Data Register */
+
+/* This data structure holds information about the supported boards -------- */
+
+struct dnp_board {
+ const char *name;
+ int ai_chans;
+ int ai_bits;
+ int have_dio;
+};
+
+static const struct dnp_board dnp_boards[] = { /* we only support one DNP 'board' */
+ { /* variant at the moment */
+ name: "dnp-1486",
+ ai_chans:16,
+ ai_bits: 12,
+ have_dio:1,
+ },
+};
+
+/* Useful for shorthand access to the particular board structure ----------- */
+#define thisboard ((const struct dnp_board *)dev->board_ptr)
+
+/* This structure is for data unique to the DNP driver --------------------- */
+struct dnp_private_data {
+
+ //
+};
+
+
+/* Shorthand macro for faster access to the private data ------------------- */
+#define devpriv ((dnp_private *)dev->private)
+
+/* ------------------------------------------------------------------------- */
+/* The struct comedi_driver structure tells the Comedi core module which functions */
+/* to call to configure/deconfigure (attach/detach) the board, and also */
+/* about the kernel module that contains the device code. */
+/* */
+/* In the following section we define the API of this driver. */
+/* ------------------------------------------------------------------------- */
+
+static int dnp_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int dnp_detach(struct comedi_device * dev);
+
+static struct comedi_driver driver_dnp = {
+ driver_name:"ssv_dnp",
+ module:THIS_MODULE,
+ attach:dnp_attach,
+ detach:dnp_detach,
+ board_name:&dnp_boards[0].name,
+ /* only necessary for non-PnP devs */
+ offset:sizeof(struct dnp_board),/* like ISA-PnP, PCI or PCMCIA. */
+ num_names:sizeof(dnp_boards) / sizeof(struct dnp_board),
+};
+
+COMEDI_INITCLEANUP(driver_dnp);
+
+static int dnp_dio_insn_bits(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data);
+
+static int dnp_dio_insn_config(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data);
+
+/* ------------------------------------------------------------------------- */
+/* Attach is called by comedi core to configure the driver for a particular */
+/* board. If you specified a board_name array in the driver structure, */
+/* dev->board_ptr contains that address. */
+/* ------------------------------------------------------------------------- */
+
+static int dnp_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+
+ struct comedi_subdevice *s;
+
+ printk("comedi%d: dnp: ", dev->minor);
+
+ /* Autoprobing: this should find out which board we have. Currently only */
+ /* the 1486 board is supported and autoprobing is not implemented :-) */
+ //dev->board_ptr = dnp_probe(dev);
+
+ /* Initialize the name of the board. We can use the "thisboard" macro now. */
+ dev->board_name = thisboard->name;
+
+ /* Allocate the private structure area. alloc_private() is a convenient */
+ /* macro defined in comedidev.h. */
+ if (alloc_private(dev, sizeof(struct dnp_private_data)) < 0)
+ return -ENOMEM;
+
+ /* Allocate the subdevice structures. alloc_subdevice() is a convenient */
+ /* macro defined in comedidev.h. */
+
+ if (alloc_subdevices(dev, 1) < 0)
+ return -ENOMEM;
+
+ s = dev->subdevices + 0;
+ /* digital i/o subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 20;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = dnp_dio_insn_bits;
+ s->insn_config = dnp_dio_insn_config;
+
+ printk("attached\n");
+
+ /* We use the I/O ports 0x22,0x23 and 0xa3-0xa9, which are always
+ * allocated for the primary 8259, so we don't need to allocate them
+ * ourselves. */
+
+ /* configure all ports as input (default) */
+ outb(PAMR, CSCIR);
+ outb(0x00, CSCDR);
+ outb(PBMR, CSCIR);
+ outb(0x00, CSCDR);
+ outb(PCMR, CSCIR);
+ outb((inb(CSCDR) & 0xAA), CSCDR);
+
+ return 1;
+
+}
+
+/* ------------------------------------------------------------------------- */
+/* detach is called to deconfigure a device. It should deallocate the */
+/* resources. This function is also called when _attach() fails, so it */
+/* should be careful not to release resources that were not necessarily */
+/* allocated by _attach(). dev->private and dev->subdevices are */
+/* deallocated automatically by the core. */
+/* ------------------------------------------------------------------------- */
+
+static int dnp_detach(struct comedi_device * dev)
+{
+
+ /* configure all ports as input (default) */
+ outb(PAMR, CSCIR);
+ outb(0x00, CSCDR);
+ outb(PBMR, CSCIR);
+ outb(0x00, CSCDR);
+ outb(PCMR, CSCIR);
+ outb((inb(CSCDR) & 0xAA), CSCDR);
+
+ /* announce that we are finished */
+ printk("comedi%d: dnp: remove\n", dev->minor);
+
+ return 0;
+
+}
+
+/* ------------------------------------------------------------------------- */
+/* The insn_bits interface allows packed reading/writing of DIO channels. */
+/* The comedi core can convert between insn_bits and insn_read/write, so you */
+/* are able to use these instructions as well. */
+/* ------------------------------------------------------------------------- */
+
+static int dnp_dio_insn_bits(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+
+ if (insn->n != 2)
+ return -EINVAL; /* insn uses data[0] and data[1] */
+
+ /* The insn data is a mask in data[0] and the new data in data[1], each */
+ /* channel cooresponding to a bit. */
+
+ /* Ports A and B are straight forward: each bit corresponds to an output */
+ /* pin with the same order. Port C is different: bits 0...3 correspond to */
+ /* bits 4...7 of the output register (PCDR). */
+
+ if (data[0]) {
+
+ outb(PADR, CSCIR);
+ outb((inb(CSCDR)
+ & ~(u8) (data[0] & 0x0000FF))
+ | (u8) (data[1] & 0x0000FF), CSCDR);
+
+ outb(PBDR, CSCIR);
+ outb((inb(CSCDR)
+ & ~(u8) ((data[0] & 0x00FF00) >> 8))
+ | (u8) ((data[1] & 0x00FF00) >> 8), CSCDR);
+
+ outb(PCDR, CSCIR);
+ outb((inb(CSCDR)
+ & ~(u8) ((data[0] & 0x0F0000) >> 12))
+ | (u8) ((data[1] & 0x0F0000) >> 12), CSCDR);
+ }
+
+ /* on return, data[1] contains the value of the digital input lines. */
+ outb(PADR, CSCIR);
+ data[0] = inb(CSCDR);
+ outb(PBDR, CSCIR);
+ data[0] += inb(CSCDR) << 8;
+ outb(PCDR, CSCIR);
+ data[0] += ((inb(CSCDR) & 0xF0) << 12);
+
+ return 2;
+
+}
+
+/* ------------------------------------------------------------------------- */
+/* Configure the direction of the bidirectional digital i/o pins. chanspec */
+/* contains the channel to be changed and data[0] contains either */
+/* COMEDI_INPUT or COMEDI_OUTPUT. */
+/* ------------------------------------------------------------------------- */
+
+static int dnp_dio_insn_config(struct comedi_device * dev,
+ struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data)
+{
+
+ u8 register_buffer;
+
+ int chan = CR_CHAN(insn->chanspec); /* reduces chanspec to lower 16 bits */
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ case INSN_CONFIG_DIO_INPUT:
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (inb(CSCDR) & (1 << chan)) ? COMEDI_OUTPUT :
+ COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ /* Test: which port does the channel belong to? */
+
+ /* We have to pay attention with port C: this is the meaning of PCMR: */
+ /* Bit in PCMR: 7 6 5 4 3 2 1 0 */
+ /* Corresponding port C pin: d 3 d 2 d 1 d 0 d= don't touch */
+
+ if ((chan >= 0) && (chan <= 7)) {
+ /* this is port A */
+ outb(PAMR, CSCIR);
+ } else if ((chan >= 8) && (chan <= 15)) {
+ /* this is port B */
+ chan -= 8;
+ outb(PBMR, CSCIR);
+ } else if ((chan >= 16) && (chan <= 19)) {
+ /* this is port C; multiplication with 2 brings bits into correct */
+ /* position for PCMR! */
+ chan -= 16;
+ chan *= 2;
+ outb(PCMR, CSCIR);
+ } else {
+ return -EINVAL;
+ }
+
+ /* read 'old' direction of the port and set bits (out=1, in=0) */
+ register_buffer = inb(CSCDR);
+ if (data[0] == COMEDI_OUTPUT) {
+ register_buffer |= (1 << chan);
+ } else {
+ register_buffer &= ~(1 << chan);
+ }
+ outb(register_buffer, CSCDR);
+
+ return 1;
+
+}
diff --git a/drivers/staging/comedi/drivers/unioxx5.c b/drivers/staging/comedi/drivers/unioxx5.c
new file mode 100644
index 000000000000..dd3b1119319b
--- /dev/null
+++ b/drivers/staging/comedi/drivers/unioxx5.c
@@ -0,0 +1,515 @@
+/***************************************************************************
+ * *
+ * comedi/drivers/unioxx5.c *
+ * Driver for Fastwel UNIOxx-5 (analog and digital i/o) boards. *
+ * *
+ * Copyright (C) 2006 Kruchinin Daniil (asgard) [asgard@etersoft.ru] *
+ * *
+ * COMEDI - Linux Control and Measurement Device Interface *
+ * Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ ***************************************************************************/
+/*
+
+Driver: unioxx5
+Description: Driver for Fastwel UNIOxx-5 (analog and digital i/o) boards.
+Author: Kruchinin Daniil (asgard) <asgard@etersoft.ru>
+Status: unknown
+Updated: 2006-10-09
+Devices: [Fastwel] UNIOxx-5 (unioxx5),
+
+ This card supports digital and analog I/O. It written for g01
+ subdevices only.
+ channels range: 0 .. 23 dio channels
+ and 0 .. 11 analog modules range
+ During attaching unioxx5 module displays modules identifiers
+ (see dmesg after comedi_config) in format:
+ | [module_number] module_id |
+
+*/
+
+#include "../comedidev.h"
+#include <linux/ioport.h>
+
+#define DRIVER_NAME "unioxx5"
+#define UNIOXX5_SIZE 0x10
+#define UNIOXX5_SUBDEV_BASE 0xA000 /* base addr of first subdev */
+#define UNIOXX5_SUBDEV_ODDS 0x400
+
+/* modules types */
+#define MODULE_DIGITAL 0
+#define MODULE_OUTPUT_MASK 0x80 /* analog input/output */
+
+/* constants for digital i/o */
+#define UNIOXX5_NUM_OF_CHANS 24
+
+/* constants for analog i/o */
+#define TxBE 0x10 /* transmit buffer enable */
+#define RxCA 0x20 /* 1 receive character available */
+#define Rx2CA 0x40 /* 2 receive character available */
+#define Rx4CA 0x80 /* 4 receive character available */
+
+/* bytes mask errors */
+#define Rx2CA_ERR_MASK 0x04 /* 2 bytes receiving error */
+#define Rx4CA_ERR_MASK 0x08 /* 4 bytes receiving error */
+
+/* channel modes */
+#define ALL_2_INPUT 0 /* config all digital channels to input */
+#define ALL_2_OUTPUT 1 /* config all digital channels to output */
+
+/* 'private' structure for each subdevice */
+struct unioxx5_subd_priv {
+ int usp_iobase;
+ unsigned char usp_module_type[12]; /* 12 modules. each can be 70L or 73L */
+ unsigned char usp_extra_data[12][4]; /* for saving previous written value for analog modules */
+ unsigned char usp_prev_wr_val[3]; /* previous written value */
+ unsigned char usp_prev_cn_val[3]; /* previous channel value */
+};
+
+static int unioxx5_attach(struct comedi_device * dev, struct comedi_devconfig * it);
+static int unioxx5_subdev_write(struct comedi_device * dev, struct comedi_subdevice * subdev,
+ struct comedi_insn * insn, unsigned int * data);
+static int unioxx5_subdev_read(struct comedi_device * dev, struct comedi_subdevice * subdev,
+ struct comedi_insn * insn, unsigned int * data);
+static int unioxx5_insn_config(struct comedi_device * dev, struct comedi_subdevice * subdev,
+ struct comedi_insn * insn, unsigned int * data);
+static int unioxx5_detach(struct comedi_device * dev);
+static int __unioxx5_subdev_init(struct comedi_subdevice * subdev, int subdev_iobase,
+ int minor);
+static int __unioxx5_digital_write(struct unioxx5_subd_priv * usp, unsigned int * data,
+ int channel, int minor);
+static int __unioxx5_digital_read(struct unioxx5_subd_priv * usp, unsigned int * data,
+ int channel, int minor);
+//static void __unioxx5_digital_config(struct unioxx5_subd_priv* usp, int mode);
+static int __unioxx5_analog_write(struct unioxx5_subd_priv * usp, unsigned int * data,
+ int channel, int minor);
+static int __unioxx5_analog_read(struct unioxx5_subd_priv * usp, unsigned int * data,
+ int channel, int minor);
+static int __unioxx5_define_chan_offset(int chan_num);
+static void __unioxx5_analog_config(struct unioxx5_subd_priv * usp, int channel);
+
+static struct comedi_driver unioxx5_driver = {
+ driver_name:DRIVER_NAME,
+ module:THIS_MODULE,
+ attach:unioxx5_attach,
+ detach:unioxx5_detach
+};
+
+COMEDI_INITCLEANUP(unioxx5_driver);
+
+static int unioxx5_attach(struct comedi_device * dev, struct comedi_devconfig * it)
+{
+ int iobase, i, n_subd;
+ int id, num, ba;
+
+ iobase = it->options[0];
+
+ dev->board_name = DRIVER_NAME;
+ dev->iobase = iobase;
+ iobase += UNIOXX5_SUBDEV_BASE;
+
+ /* defining number of subdevices and getting they types (it must be 'g01') */
+ for (i = n_subd = 0, ba = iobase; i < 4; i++, ba += UNIOXX5_SUBDEV_ODDS) {
+ id = inb(ba + 0xE);
+ num = inb(ba + 0xF);
+
+ if (id != 'g' || num != 1)
+ continue;
+
+ n_subd++;
+ }
+
+ /* unioxx5 can has from two to four subdevices */
+ if (n_subd < 2) {
+ printk(KERN_ERR
+ "your card must has at least 2 'g01' subdevices\n");
+ return -1;
+ }
+
+ if (alloc_subdevices(dev, n_subd) < 0) {
+ printk(KERN_ERR "out of memory\n");
+ return -ENOMEM;
+ }
+
+ /* initializing each of for same subdevices */
+ for (i = 0; i < n_subd; i++, iobase += UNIOXX5_SUBDEV_ODDS) {
+ if (__unioxx5_subdev_init(&dev->subdevices[i], iobase,
+ dev->minor) < 0)
+ return -1;
+ }
+
+ printk("attached\n");
+ return 0;
+}
+
+static int unioxx5_subdev_read(struct comedi_device * dev, struct comedi_subdevice * subdev,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ struct unioxx5_subd_priv *usp = subdev->private;
+ int channel, type;
+
+ channel = CR_CHAN(insn->chanspec);
+ type = usp->usp_module_type[channel / 2]; /* defining module type(analog or digital) */
+
+ if (type == MODULE_DIGITAL) {
+ if (!__unioxx5_digital_read(usp, data, channel, dev->minor))
+ return -1;
+ } else {
+ if (!__unioxx5_analog_read(usp, data, channel, dev->minor))
+ return -1;
+ }
+
+ return 1;
+}
+
+static int unioxx5_subdev_write(struct comedi_device * dev, struct comedi_subdevice * subdev,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ struct unioxx5_subd_priv *usp = subdev->private;
+ int channel, type;
+
+ channel = CR_CHAN(insn->chanspec);
+ type = usp->usp_module_type[channel / 2]; /* defining module type(analog or digital) */
+
+ if (type == MODULE_DIGITAL) {
+ if (!__unioxx5_digital_write(usp, data, channel, dev->minor))
+ return -1;
+ } else {
+ if (!__unioxx5_analog_write(usp, data, channel, dev->minor))
+ return -1;
+ }
+
+ return 1;
+}
+
+/* for digital modules only */
+static int unioxx5_insn_config(struct comedi_device * dev, struct comedi_subdevice * subdev,
+ struct comedi_insn * insn, unsigned int * data)
+{
+ int channel_offset, flags, channel = CR_CHAN(insn->chanspec), type;
+ struct unioxx5_subd_priv *usp = subdev->private;
+ int mask = 1 << (channel & 0x07);
+
+ type = usp->usp_module_type[channel / 2];
+
+ if (type != MODULE_DIGITAL) {
+ printk(KERN_ERR
+ "comedi%d: channel configuration accessible only for digital modules\n",
+ dev->minor);
+ return -1;
+ }
+
+ if ((channel_offset = __unioxx5_define_chan_offset(channel)) < 0) {
+ printk(KERN_ERR
+ "comedi%d: undefined channel %d. channel range is 0 .. 23\n",
+ dev->minor, channel);
+ return -1;
+ }
+
+ /* gets previously written value */
+ flags = usp->usp_prev_cn_val[channel_offset - 1];
+
+ switch (*data) {
+ case COMEDI_INPUT:
+ flags &= ~mask;
+ break;
+ case COMEDI_OUTPUT:
+ flags |= mask;
+ break;
+ default:
+ printk(KERN_ERR "comedi%d: unknown flag\n", dev->minor);
+ return -1;
+ }
+
+ /* *\
+ * sets channels buffer to 1(after this we are allowed to *
+ * change channel type on input or output) *
+ \* */
+ outb(1, usp->usp_iobase + 0);
+ outb(flags, usp->usp_iobase + channel_offset); /* changes type of _one_ channel */
+ outb(0, usp->usp_iobase + 0); /* sets channels bank to 0(allows directly input/output) */
+ usp->usp_prev_cn_val[channel_offset - 1] = flags; /* saves written value */
+
+ return 0;
+}
+
+static int unioxx5_detach(struct comedi_device * dev)
+{
+ int i;
+ struct comedi_subdevice *subdev;
+ struct unioxx5_subd_priv *usp;
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ subdev = &dev->subdevices[i];
+ usp = subdev->private;
+ release_region(usp->usp_iobase, UNIOXX5_SIZE);
+ kfree(subdev->private);
+ }
+
+ return 0;
+}
+
+/* initializing subdevice with given address */
+static int __unioxx5_subdev_init(struct comedi_subdevice * subdev, int subdev_iobase,
+ int minor)
+{
+ struct unioxx5_subd_priv *usp;
+ int i, to, ndef_flag = 0;
+
+ if (!request_region(subdev_iobase, UNIOXX5_SIZE, DRIVER_NAME)) {
+ printk(KERN_ERR "comedi%d: I/O port conflict\n", minor);
+ return -EIO;
+ }
+
+ if ((usp = (struct unioxx5_subd_priv *) kzalloc(sizeof(*usp),
+ GFP_KERNEL)) == NULL) {
+ printk(KERN_ERR "comedi%d: erorr! --> out of memory!\n", minor);
+ return -1;
+ }
+
+ usp->usp_iobase = subdev_iobase;
+ printk("comedi%d: |", minor);
+
+ /* defining modules types */
+ for (i = 0; i < 12; i++) {
+ to = 10000;
+
+ __unioxx5_analog_config(usp, i * 2);
+ outb(i + 1, subdev_iobase + 5); /* sends channel number to card */
+ outb('H', subdev_iobase + 6); /* requests EEPROM world */
+ while (!(inb(subdev_iobase + 0) & TxBE)) ; /* waits while writting will be allowed */
+ outb(0, subdev_iobase + 6);
+
+ /* waits while reading of two bytes will be allowed */
+ while (!(inb(subdev_iobase + 0) & Rx2CA)) {
+ if (--to <= 0) {
+ ndef_flag = 1;
+ break;
+ }
+ }
+
+ if (ndef_flag) {
+ usp->usp_module_type[i] = 0;
+ ndef_flag = 0;
+ } else
+ usp->usp_module_type[i] = inb(subdev_iobase + 6);
+
+ printk(" [%d] 0x%02x |", i, usp->usp_module_type[i]);
+ comedi_udelay(1);
+ }
+
+ printk("\n");
+
+ /* initial subdevice for digital or analog i/o */
+ subdev->type = COMEDI_SUBD_DIO;
+ subdev->private = usp;
+ subdev->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ subdev->n_chan = UNIOXX5_NUM_OF_CHANS;
+ subdev->maxdata = 0xFFF;
+ subdev->range_table = &range_digital;
+ subdev->insn_read = unioxx5_subdev_read;
+ subdev->insn_write = unioxx5_subdev_write;
+ subdev->insn_config = unioxx5_insn_config; /* for digital modules only!!! */
+
+ printk("subdevice configured\n");
+
+ return 0;
+}
+
+static int __unioxx5_digital_write(struct unioxx5_subd_priv * usp, unsigned int * data,
+ int channel, int minor)
+{
+ int channel_offset, val;
+ int mask = 1 << (channel & 0x07);
+
+ if ((channel_offset = __unioxx5_define_chan_offset(channel)) < 0) {
+ printk(KERN_ERR
+ "comedi%d: undefined channel %d. channel range is 0 .. 23\n",
+ minor, channel);
+ return 0;
+ }
+
+ val = usp->usp_prev_wr_val[channel_offset - 1]; /* getting previous written value */
+
+ if (*data)
+ val |= mask;
+ else
+ val &= ~mask;
+
+ outb(val, usp->usp_iobase + channel_offset);
+ usp->usp_prev_wr_val[channel_offset - 1] = val; /* saving new written value */
+
+ return 1;
+}
+
+/* function for digital reading */
+static int __unioxx5_digital_read(struct unioxx5_subd_priv * usp, unsigned int * data,
+ int channel, int minor)
+{
+ int channel_offset, mask = 1 << (channel & 0x07);
+
+ if ((channel_offset = __unioxx5_define_chan_offset(channel)) < 0) {
+ printk(KERN_ERR
+ "comedi%d: undefined channel %d. channel range is 0 .. 23\n",
+ minor, channel);
+ return 0;
+ }
+
+ *data = inb(usp->usp_iobase + channel_offset);
+ *data &= mask;
+
+ if (channel_offset > 1)
+ channel -= 2 << channel_offset; /* this operation is created for correct readed value to 0 or 1 */
+
+ *data >>= channel;
+ return 1;
+}
+
+#if 0 /* not used? */
+static void __unioxx5_digital_config(struct unioxx5_subd_priv * usp, int mode)
+{
+ int i, mask;
+
+ mask = (mode == ALL_2_OUTPUT) ? 0xFF : 0x00;
+ printk("COMEDI: mode = %d\n", mask);
+
+ outb(1, usp->usp_iobase + 0);
+
+ for (i = 0; i < 3; i++)
+ outb(mask, usp->usp_iobase + i);
+
+ outb(0, usp->usp_iobase + 0);
+}
+#endif
+
+static int __unioxx5_analog_write(struct unioxx5_subd_priv * usp, unsigned int * data,
+ int channel, int minor)
+{
+ int module, i;
+
+ module = channel / 2; /* definig module number(0 .. 11) */
+ i = (channel % 2) << 1; /* depends on type of channel (A or B) */
+
+ /* defining if given module can work on output */
+ if (!(usp->usp_module_type[module] & MODULE_OUTPUT_MASK)) {
+ printk(KERN_ERR
+ "comedi%d: module in position %d with id 0x%0x is for input only!\n",
+ minor, module, usp->usp_module_type[module]);
+ return 0;
+ }
+
+ __unioxx5_analog_config(usp, channel);
+ /* saving minor byte */
+ usp->usp_extra_data[module][i++] = (unsigned char)(*data & 0x00FF);
+ /* saving major byte */
+ usp->usp_extra_data[module][i] = (unsigned char)((*data & 0xFF00) >> 8);
+
+ //while(!((inb(usp->usp_iobase + 0)) & TxBE));
+ outb(module + 1, usp->usp_iobase + 5); /* sending module number to card(1 .. 12) */
+ outb('W', usp->usp_iobase + 6); /* sends (W)rite command to module */
+
+ /* sending for bytes to module(one byte per cycle iteration) */
+ for (i = 0; i < 4; i++) {
+ while (!((inb(usp->usp_iobase + 0)) & TxBE)) ; /* waits while writting will be allowed */
+ outb(usp->usp_extra_data[module][i], usp->usp_iobase + 6);
+ }
+
+ return 1;
+}
+
+static int __unioxx5_analog_read(struct unioxx5_subd_priv * usp, unsigned int * data,
+ int channel, int minor)
+{
+ int module_no, read_ch;
+ char control;
+
+ module_no = channel / 2;
+ read_ch = channel % 2; /* depend on type of channel (A or B) */
+
+ /* defining if given module can work on input */
+ if (usp->usp_module_type[module_no] & MODULE_OUTPUT_MASK) {
+ printk(KERN_ERR
+ "comedi%d: module in position %d with id 0x%02x is for output only",
+ minor, module_no, usp->usp_module_type[module_no]);
+ return 0;
+ }
+
+ __unioxx5_analog_config(usp, channel);
+ outb(module_no + 1, usp->usp_iobase + 5); /* sends module number to card(1 .. 12) */
+ outb('V', usp->usp_iobase + 6); /* sends to module (V)erify command */
+ control = inb(usp->usp_iobase); /* get control register byte */
+
+ /* waits while reading four bytes will be allowed */
+ while (!((control = inb(usp->usp_iobase + 0)) & Rx4CA)) ;
+
+ /* if four bytes readding error occurs - return 0(false) */
+ if ((control & Rx4CA_ERR_MASK)) {
+ printk("COMEDI: 4 bytes error\n");
+ return 0;
+ }
+
+ if (read_ch)
+ *data = inw(usp->usp_iobase + 6); /* channel B */
+ else
+ *data = inw(usp->usp_iobase + 4); /* channel A */
+
+ return 1;
+}
+
+/* configure channels for analog i/o (even to output, odd to input) */
+static void __unioxx5_analog_config(struct unioxx5_subd_priv * usp, int channel)
+{
+ int chan_a, chan_b, conf, channel_offset;
+
+ channel_offset = __unioxx5_define_chan_offset(channel);
+ conf = usp->usp_prev_cn_val[channel_offset - 1];
+ chan_a = chan_b = 1;
+
+ /* setting channel A and channel B mask */
+ if (channel % 2 == 0) {
+ chan_a <<= channel & 0x07;
+ chan_b <<= (channel + 1) & 0x07;
+ } else {
+ chan_a <<= (channel - 1) & 0x07;
+ chan_b <<= channel & 0x07;
+ }
+
+ conf |= chan_a; /* even channel ot output */
+ conf &= ~chan_b; /* odd channel to input */
+
+ outb(1, usp->usp_iobase + 0);
+ outb(conf, usp->usp_iobase + channel_offset);
+ outb(0, usp->usp_iobase + 0);
+
+ usp->usp_prev_cn_val[channel_offset - 1] = conf;
+}
+
+/* *\
+ * this function defines if the given channel number *
+ * enters in default numeric interspace(from 0 to 23) *
+ * and it returns address offset for usage needed *
+ * channel. *
+\* */
+
+static int __unioxx5_define_chan_offset(int chan_num)
+{
+
+ if (chan_num < 0 || chan_num > 23)
+ return -1;
+
+ return (chan_num >> 3) + 1;
+}
diff --git a/drivers/staging/comedi/drivers/usbdux.c b/drivers/staging/comedi/drivers/usbdux.c
index 35138257be7f..d0b59e98314b 100644
--- a/drivers/staging/comedi/drivers/usbdux.c
+++ b/drivers/staging/comedi/drivers/usbdux.c
@@ -1,4 +1,4 @@
-#define DRIVER_VERSION "v2.1"
+#define DRIVER_VERSION "v2.2"
#define DRIVER_AUTHOR "Bernd Porr, BerndPorr@f2s.com"
#define DRIVER_DESC "Stirling/ITL USB-DUX -- Bernd.Porr@f2s.com"
/*
@@ -25,8 +25,8 @@ Driver: usbdux
Description: University of Stirling USB DAQ & INCITE Technology Limited
Devices: [ITL] USB-DUX (usbdux.o)
Author: Bernd Porr <BerndPorr@f2s.com>
-Updated: 25 Nov 2007
-Status: Testing
+Updated: 8 Dec 2008
+Status: Stable
Configuration options:
You have to upload firmware with the -i option. The
firmware is usually installed under /usr/share/usb or
@@ -79,6 +79,7 @@ sampling rate. If you sample two channels you get 4kHz and so on.
* 1.2: added PWM suport via EP4
* 2.0: PWM seems to be stable and is not interfering with the other functions
* 2.1: changed PWM API
+ * 2.2: added firmware kernel request to fix an udev problem
*
*/
@@ -94,6 +95,7 @@ sampling rate. If you sample two channels you get 4kHz and so on.
#include <linux/smp_lock.h>
#include <linux/fcntl.h>
#include <linux/compiler.h>
+#include <linux/firmware.h>
#include "../comedidev.h"
@@ -212,7 +214,7 @@ sampling rate. If you sample two channels you get 4kHz and so on.
/**************************************************/
/* comedi constants */
-static const comedi_lrange range_usbdux_ai_range = { 4, {
+static const struct comedi_lrange range_usbdux_ai_range = { 4, {
BIP_RANGE(4.096),
BIP_RANGE(4.096 / 2),
UNI_RANGE(4.096),
@@ -220,7 +222,7 @@ static const comedi_lrange range_usbdux_ai_range = { 4, {
}
};
-static const comedi_lrange range_usbdux_ao_range = { 2, {
+static const struct comedi_lrange range_usbdux_ao_range = { 2, {
BIP_RANGE(4.096),
UNI_RANGE(4.096),
}
@@ -251,7 +253,7 @@ struct usbduxsub {
/* pwm-transfer handling */
struct urb *urbPwm;
/* PWM period */
- lsampl_t pwmPeriod;
+ unsigned int pwmPeriod;
/* PWM internal delay for the GPIF in the FX2 */
int8_t pwmDelay;
/* size of the PWM buffer which holds the bit pattern */
@@ -267,7 +269,7 @@ struct usbduxsub {
/* interface structure in 2.6 */
struct usb_interface *interface;
/* comedi device for the interrupt context */
- comedi_device *comedidev;
+ struct comedi_device *comedidev;
/* is it USB_SPEED_HIGH or not? */
short int high_speed;
/* asynchronous command is running */
@@ -361,7 +363,7 @@ static int usbdux_ai_stop(struct usbduxsub *this_usbduxsub, int do_unlink)
* This will cancel a running acquisition operation.
* This is called by comedi but never from inside the driver.
*/
-static int usbdux_ai_cancel(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct usbduxsub *this_usbduxsub;
int res = 0;
@@ -390,8 +392,8 @@ static void usbduxsub_ai_IsocIrq(struct urb *urb)
{
int i, err, n;
struct usbduxsub *this_usbduxsub;
- comedi_device *this_comedidev;
- comedi_subdevice *s;
+ struct comedi_device *this_comedidev;
+ struct comedi_subdevice *s;
/* the context variable points to the subdevice */
this_comedidev = urb->context;
@@ -559,7 +561,7 @@ static int usbdux_ao_stop(struct usbduxsub *this_usbduxsub, int do_unlink)
}
/* force unlink, is called by comedi */
-static int usbdux_ao_cancel(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_ao_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct usbduxsub *this_usbduxsub = dev->private;
int res = 0;
@@ -584,8 +586,8 @@ static void usbduxsub_ao_IsocIrq(struct urb *urb)
int i, ret;
int8_t *datap;
struct usbduxsub *this_usbduxsub;
- comedi_device *this_comedidev;
- comedi_subdevice *s;
+ struct comedi_device *this_comedidev;
+ struct comedi_subdevice *s;
/* the context variable points to the subdevice */
this_comedidev = urb->context;
@@ -654,7 +656,7 @@ static void usbduxsub_ao_IsocIrq(struct urb *urb)
((uint8_t *) (urb->transfer_buffer))[0] =
s->async->cmd.chanlist_len;
for (i = 0; i < s->async->cmd.chanlist_len; i++) {
- sampl_t temp;
+ short temp;
if (i >= NUMOUTCHANNELS)
break;
@@ -718,31 +720,29 @@ static int usbduxsub_start(struct usbduxsub *usbduxsub)
int errcode = 0;
uint8_t local_transfer_buffer[16];
- if (usbduxsub->probed) {
- /* 7f92 to zero */
- local_transfer_buffer[0] = 0;
- errcode = usb_control_msg(usbduxsub->usbdev,
- /* create a pipe for a control transfer */
- usb_sndctrlpipe(usbduxsub->usbdev, 0),
- /* bRequest, "Firmware" */
- USBDUXSUB_FIRMWARE,
- /* bmRequestType */
- VENDOR_DIR_OUT,
- /* Value */
- USBDUXSUB_CPUCS,
- /* Index */
- 0x0000,
- /* address of the transfer buffer */
- local_transfer_buffer,
- /* Length */
- 1,
- /* Timeout */
- EZTIMEOUT);
- if (errcode < 0) {
- dev_err(&usbduxsub->interface->dev,
- "comedi_: control msg failed (start)\n");
- return errcode;
- }
+ /* 7f92 to zero */
+ local_transfer_buffer[0] = 0;
+ errcode = usb_control_msg(usbduxsub->usbdev,
+ /* create a pipe for a control transfer */
+ usb_sndctrlpipe(usbduxsub->usbdev, 0),
+ /* bRequest, "Firmware" */
+ USBDUXSUB_FIRMWARE,
+ /* bmRequestType */
+ VENDOR_DIR_OUT,
+ /* Value */
+ USBDUXSUB_CPUCS,
+ /* Index */
+ 0x0000,
+ /* address of the transfer buffer */
+ local_transfer_buffer,
+ /* Length */
+ 1,
+ /* Timeout */
+ EZTIMEOUT);
+ if (errcode < 0) {
+ dev_err(&usbduxsub->interface->dev,
+ "comedi_: control msg failed (start)\n");
+ return errcode;
}
return 0;
}
@@ -752,28 +752,27 @@ static int usbduxsub_stop(struct usbduxsub *usbduxsub)
int errcode = 0;
uint8_t local_transfer_buffer[16];
- if (usbduxsub->probed) {
- /* 7f92 to one */
- local_transfer_buffer[0] = 1;
- errcode = usb_control_msg(usbduxsub->usbdev,
- usb_sndctrlpipe(usbduxsub->usbdev, 0),
- /* bRequest, "Firmware" */
- USBDUXSUB_FIRMWARE,
- /* bmRequestType */
- VENDOR_DIR_OUT,
- /* Value */
- USBDUXSUB_CPUCS,
- /* Index */
- 0x0000, local_transfer_buffer,
- /* Length */
- 1,
- /* Timeout */
- EZTIMEOUT);
- if (errcode < 0) {
- dev_err(&usbduxsub->interface->dev,
- "comedi_: control msg failed (stop)\n");
- return errcode;
- }
+
+ /* 7f92 to one */
+ local_transfer_buffer[0] = 1;
+ errcode = usb_control_msg(usbduxsub->usbdev,
+ usb_sndctrlpipe(usbduxsub->usbdev, 0),
+ /* bRequest, "Firmware" */
+ USBDUXSUB_FIRMWARE,
+ /* bmRequestType */
+ VENDOR_DIR_OUT,
+ /* Value */
+ USBDUXSUB_CPUCS,
+ /* Index */
+ 0x0000, local_transfer_buffer,
+ /* Length */
+ 1,
+ /* Timeout */
+ EZTIMEOUT);
+ if (errcode < 0) {
+ dev_err(&usbduxsub->interface->dev,
+ "comedi_: control msg failed (stop)\n");
+ return errcode;
}
return 0;
}
@@ -784,13 +783,7 @@ static int usbduxsub_upload(struct usbduxsub *usbduxsub,
{
int errcode;
- if (usbduxsub->probed) {
- dev_dbg(&usbduxsub->interface->dev,
- "comedi%d: usbdux: uploading %d bytes"
- " to addr %d, first byte=%d.\n",
- usbduxsub->comedidev->minor, len,
- startAddr, local_transfer_buffer[0]);
- errcode = usb_control_msg(usbduxsub->usbdev,
+ errcode = usb_control_msg(usbduxsub->usbdev,
usb_sndctrlpipe(usbduxsub->usbdev, 0),
/* brequest, firmware */
USBDUXSUB_FIRMWARE,
@@ -806,16 +799,12 @@ static int usbduxsub_upload(struct usbduxsub *usbduxsub,
len,
/* timeout */
EZTIMEOUT);
- dev_dbg(&usbduxsub->interface->dev,
- "comedi_: result=%d\n", errcode);
- if (errcode < 0) {
- dev_err(&usbduxsub->interface->dev,
- "comedi_: upload failed\n");
- return errcode;
- }
- } else {
- /* no device on the bus for this index */
- return -EFAULT;
+ dev_dbg(&usbduxsub->interface->dev,
+ "comedi_: result=%d\n", errcode);
+ if (errcode < 0) {
+ dev_err(&usbduxsub->interface->dev,
+ "comedi_: upload failed\n");
+ return errcode;
}
return 0;
}
@@ -907,8 +896,8 @@ static int usbduxsub_submit_OutURBs(struct usbduxsub *usbduxsub)
return 0;
}
-static int usbdux_ai_cmdtest(comedi_device *dev, comedi_subdevice *s,
- comedi_cmd *cmd)
+static int usbdux_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
{
int err = 0, tmp, i;
unsigned int tmpTimer;
@@ -1127,7 +1116,7 @@ static int receive_dux_commands(struct usbduxsub *this_usbduxsub, int command)
return -EFAULT;
}
-static int usbdux_ai_inttrig(comedi_device *dev, comedi_subdevice *s,
+static int usbdux_ai_inttrig(struct comedi_device *dev, struct comedi_subdevice *s,
unsigned int trignum)
{
int ret;
@@ -1171,9 +1160,9 @@ static int usbdux_ai_inttrig(comedi_device *dev, comedi_subdevice *s,
return 1;
}
-static int usbdux_ai_cmd(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
- comedi_cmd *cmd = &s->async->cmd;
+ struct comedi_cmd *cmd = &s->async->cmd;
unsigned int chan, range;
int i, ret;
struct usbduxsub *this_usbduxsub = dev->private;
@@ -1287,11 +1276,11 @@ static int usbdux_ai_cmd(comedi_device *dev, comedi_subdevice *s)
}
/* Mode 0 is used to get a single conversion on demand */
-static int usbdux_ai_insn_read(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int i;
- lsampl_t one = 0;
+ unsigned int one = 0;
int chan, range;
int err;
struct usbduxsub *this_usbduxsub = dev->private;
@@ -1348,8 +1337,8 @@ static int usbdux_ai_insn_read(comedi_device *dev, comedi_subdevice *s,
/************************************/
/* analog out */
-static int usbdux_ao_insn_read(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int i;
int chan = CR_CHAN(insn->chanspec);
@@ -1370,8 +1359,8 @@ static int usbdux_ao_insn_read(comedi_device *dev, comedi_subdevice *s,
return i;
}
-static int usbdux_ao_insn_write(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_ao_insn_write(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int i, err;
int chan = CR_CHAN(insn->chanspec);
@@ -1420,7 +1409,7 @@ static int usbdux_ao_insn_write(comedi_device *dev, comedi_subdevice *s,
return i;
}
-static int usbdux_ao_inttrig(comedi_device *dev, comedi_subdevice *s,
+static int usbdux_ao_inttrig(struct comedi_device *dev, struct comedi_subdevice *s,
unsigned int trignum)
{
int ret;
@@ -1461,8 +1450,8 @@ static int usbdux_ao_inttrig(comedi_device *dev, comedi_subdevice *s,
return 1;
}
-static int usbdux_ao_cmdtest(comedi_device *dev, comedi_subdevice *s,
- comedi_cmd *cmd)
+static int usbdux_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
{
int err = 0, tmp;
struct usbduxsub *this_usbduxsub = dev->private;
@@ -1600,9 +1589,9 @@ static int usbdux_ao_cmdtest(comedi_device *dev, comedi_subdevice *s,
return 0;
}
-static int usbdux_ao_cmd(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
- comedi_cmd *cmd = &s->async->cmd;
+ struct comedi_cmd *cmd = &s->async->cmd;
unsigned int chan, gain;
int i, ret;
struct usbduxsub *this_usbduxsub = dev->private;
@@ -1708,8 +1697,8 @@ static int usbdux_ao_cmd(comedi_device *dev, comedi_subdevice *s)
return 0;
}
-static int usbdux_dio_insn_config(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
int chan = CR_CHAN(insn->chanspec);
@@ -1739,8 +1728,8 @@ static int usbdux_dio_insn_config(comedi_device *dev, comedi_subdevice *s,
return insn->n;
}
-static int usbdux_dio_insn_bits(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
struct usbduxsub *this_usbduxsub = dev->private;
@@ -1786,8 +1775,8 @@ static int usbdux_dio_insn_bits(comedi_device *dev, comedi_subdevice *s,
}
/* reads the 4 counters, only two are used just now */
-static int usbdux_counter_read(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_counter_read(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
struct usbduxsub *this_usbduxsub = dev->private;
int chan = insn->chanspec;
@@ -1820,8 +1809,8 @@ static int usbdux_counter_read(comedi_device *dev, comedi_subdevice *s,
return 1;
}
-static int usbdux_counter_write(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_counter_write(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
struct usbduxsub *this_usbduxsub = dev->private;
int err;
@@ -1850,8 +1839,8 @@ static int usbdux_counter_write(comedi_device *dev, comedi_subdevice *s,
return 1;
}
-static int usbdux_counter_config(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_counter_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
/* nothing to do so far */
return 2;
@@ -1894,7 +1883,7 @@ static int usbdux_pwm_stop(struct usbduxsub *this_usbduxsub, int do_unlink)
}
/* force unlink - is called by comedi */
-static int usbdux_pwm_cancel(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_pwm_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct usbduxsub *this_usbduxsub = dev->private;
int res = 0;
@@ -1916,8 +1905,8 @@ static void usbduxsub_pwm_irq(struct urb *urb)
{
int ret;
struct usbduxsub *this_usbduxsub;
- comedi_device *this_comedidev;
- comedi_subdevice *s;
+ struct comedi_device *this_comedidev;
+ struct comedi_subdevice *s;
/* printk(KERN_DEBUG "PWM: IRQ\n"); */
@@ -2007,8 +1996,8 @@ static int usbduxsub_submit_PwmURBs(struct usbduxsub *usbduxsub)
return 0;
}
-static int usbdux_pwm_period(comedi_device *dev, comedi_subdevice *s,
- lsampl_t period)
+static int usbdux_pwm_period(struct comedi_device *dev, struct comedi_subdevice *s,
+ unsigned int period)
{
struct usbduxsub *this_usbduxsub = dev->private;
int fx2delay = 255;
@@ -2035,7 +2024,7 @@ static int usbdux_pwm_period(comedi_device *dev, comedi_subdevice *s,
}
/* is called from insn so there's no need to do all the sanity checks */
-static int usbdux_pwm_start(comedi_device *dev, comedi_subdevice *s)
+static int usbdux_pwm_start(struct comedi_device *dev, struct comedi_subdevice *s)
{
int ret, i;
struct usbduxsub *this_usbduxsub = dev->private;
@@ -2067,8 +2056,8 @@ static int usbdux_pwm_start(comedi_device *dev, comedi_subdevice *s)
}
/* generates the bit pattern for PWM with the optional sign bit */
-static int usbdux_pwm_pattern(comedi_device *dev, comedi_subdevice *s,
- int channel, lsampl_t value, lsampl_t sign)
+static int usbdux_pwm_pattern(struct comedi_device *dev, struct comedi_subdevice *s,
+ int channel, unsigned int value, unsigned int sign)
{
struct usbduxsub *this_usbduxsub = dev->private;
int i, szbuf;
@@ -2108,8 +2097,8 @@ static int usbdux_pwm_pattern(comedi_device *dev, comedi_subdevice *s,
return 1;
}
-static int usbdux_pwm_write(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_pwm_write(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
struct usbduxsub *this_usbduxsub = dev->private;
@@ -2133,16 +2122,16 @@ static int usbdux_pwm_write(comedi_device *dev, comedi_subdevice *s,
data[0], 0);
}
-static int usbdux_pwm_read(comedi_device *x1, comedi_subdevice *x2,
- comedi_insn *x3, lsampl_t *x4)
+static int usbdux_pwm_read(struct comedi_device *x1, struct comedi_subdevice *x2,
+ struct comedi_insn *x3, unsigned int *x4)
{
/* not needed */
return -EINVAL;
};
/* switches on/off PWM */
-static int usbdux_pwm_config(comedi_device *dev, comedi_subdevice *s,
- comedi_insn *insn, lsampl_t *data)
+static int usbdux_pwm_config(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
struct usbduxsub *this_usbduxsub = dev->private;
switch (data[0]) {
@@ -2292,13 +2281,13 @@ static unsigned hex2unsigned(char *h)
#define FIRMWARE_MAX_LEN 0x2000
/* taken from David Brownell's fxload and adjusted for this driver */
-static int read_firmware(struct usbduxsub *usbduxsub, void *firmwarePtr,
+static int read_firmware(struct usbduxsub *usbduxsub, const void *firmwarePtr,
long size)
{
struct device *dev = &usbduxsub->interface->dev;
int i = 0;
unsigned char *fp = (char *)firmwarePtr;
- unsigned char *firmwareBinary = NULL;
+ unsigned char *firmwareBinary;
int res = 0;
int maxAddr = 0;
@@ -2322,6 +2311,7 @@ static int read_firmware(struct usbduxsub *usbduxsub, void *firmwarePtr,
j++;
if (j >= sizeof(buf)) {
dev_err(dev, "comedi_: bogus firmware file!\n");
+ kfree(firmwareBinary);
return -1;
}
}
@@ -2344,6 +2334,7 @@ static int read_firmware(struct usbduxsub *usbduxsub, void *firmwarePtr,
if (buf[0] != ':') {
dev_err(dev, "comedi_: upload: not an ihex record: %s",
buf);
+ kfree(firmwareBinary);
return -EFAULT;
}
@@ -2360,6 +2351,7 @@ static int read_firmware(struct usbduxsub *usbduxsub, void *firmwarePtr,
if (maxAddr >= FIRMWARE_MAX_LEN) {
dev_err(dev, "comedi_: firmware upload goes "
"beyond FX2 RAM boundaries.\n");
+ kfree(firmwareBinary);
return -EFAULT;
}
/* dev_dbg(dev, "comedi_: off=%x, len=%x:\n", off, len); */
@@ -2375,6 +2367,7 @@ static int read_firmware(struct usbduxsub *usbduxsub, void *firmwarePtr,
if (type != 0) {
dev_err(dev, "comedi_: unsupported record type: %u\n",
type);
+ kfree(firmwareBinary);
return -EFAULT;
}
@@ -2395,6 +2388,34 @@ static int read_firmware(struct usbduxsub *usbduxsub, void *firmwarePtr,
return res;
}
+static void usbdux_firmware_request_complete_handler(const struct firmware *fw,
+ void *context)
+{
+ struct usbduxsub *usbduxsub_tmp = context;
+ struct usb_device *usbdev = usbduxsub_tmp->usbdev;
+ int ret;
+
+ if (fw == NULL) {
+ dev_err(&usbdev->dev,
+ "Firmware complete handler without firmware!\n");
+ return;
+ }
+
+ /*
+ * we need to upload the firmware here because fw will be
+ * freed once we've left this function
+ */
+ ret = read_firmware(usbduxsub_tmp, fw->data, fw->size);
+
+ if (ret) {
+ dev_err(&usbdev->dev,
+ "Could not upload firmware (err=%d)\n",
+ ret);
+ return;
+ }
+ comedi_usb_auto_config(usbdev, BOARDNAME);
+}
+
/* allocate memory for the urbs and initialise them */
static int usbduxsub_probe(struct usb_interface *uinterf,
const struct usb_device_id *id)
@@ -2403,6 +2424,7 @@ static int usbduxsub_probe(struct usb_interface *uinterf,
struct device *dev = &uinterf->dev;
int i;
int index;
+ int ret;
dev_dbg(dev, "comedi_: usbdux_: "
"finding a free structure for the usb-device\n");
@@ -2637,6 +2659,19 @@ static int usbduxsub_probe(struct usb_interface *uinterf,
/* we've reached the bottom of the function */
usbduxsub[index].probed = 1;
up(&start_stop_sem);
+
+ ret = request_firmware_nowait(THIS_MODULE,
+ FW_ACTION_HOTPLUG,
+ "usbdux_firmware.hex",
+ &udev->dev,
+ usbduxsub + index,
+ usbdux_firmware_request_complete_handler);
+
+ if (ret) {
+ dev_err(dev, "Could not load firmware (err=%d)\n", ret);
+ return ret;
+ }
+
dev_info(dev, "comedi_: usbdux%d "
"has been successfully initialised.\n", index);
/* success */
@@ -2658,6 +2693,7 @@ static void usbduxsub_disconnect(struct usb_interface *intf)
"comedi_: BUG! called with wrong ptr!!!\n");
return;
}
+ comedi_usb_auto_unconfig(udev);
down(&start_stop_sem);
down(&usbduxsub_tmp->sem);
tidy_up(usbduxsub_tmp);
@@ -2667,14 +2703,14 @@ static void usbduxsub_disconnect(struct usb_interface *intf)
}
/* is called when comedi-config is called */
-static int usbdux_attach(comedi_device *dev, comedi_devconfig *it)
+static int usbdux_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
int ret;
int index;
int i;
struct usbduxsub *udev;
- comedi_subdevice *s = NULL;
+ struct comedi_subdevice *s = NULL;
dev->private = NULL;
down(&start_stop_sem);
@@ -2834,7 +2870,7 @@ static int usbdux_attach(comedi_device *dev, comedi_devconfig *it)
return 0;
}
-static int usbdux_detach(comedi_device *dev)
+static int usbdux_detach(struct comedi_device *dev)
{
struct usbduxsub *usbduxsub_tmp;
@@ -2867,26 +2903,13 @@ static int usbdux_detach(comedi_device *dev)
}
/* main driver struct */
-static comedi_driver driver_usbdux = {
+static struct comedi_driver driver_usbdux = {
.driver_name = "usbdux",
.module = THIS_MODULE,
.attach = usbdux_attach,
.detach = usbdux_detach,
};
-static void init_usb_devices(void)
-{
- int index;
-
- /* all devices entries are invalid to begin with */
- /* they will become valid by the probe function */
- /* and then finally by the attach-function */
- for (index = 0; index < NUMUSBDUX; index++) {
- memset(&(usbduxsub[index]), 0x00, sizeof(usbduxsub[index]));
- init_MUTEX(&(usbduxsub[index].sem));
- }
-}
-
/* Table with the USB-devices: just now only testing IDs */
static struct usb_device_id usbduxsub_table[] = {
{USB_DEVICE(0x13d8, 0x0001) },
@@ -2907,18 +2930,17 @@ static struct usb_driver usbduxsub_driver = {
/* Can't use the nice macro as I have also to initialise the USB */
/* subsystem: */
/* registering the usb-system _and_ the comedi-driver */
-static int init_usbdux(void)
+static int __init init_usbdux(void)
{
printk(KERN_INFO KBUILD_MODNAME ": "
DRIVER_VERSION ":" DRIVER_DESC "\n");
- init_usb_devices();
usb_register(&usbduxsub_driver);
comedi_driver_register(&driver_usbdux);
return 0;
}
/* deregistering the comedi driver and the usb-subsystem */
-static void exit_usbdux(void)
+static void __exit exit_usbdux(void)
{
comedi_driver_unregister(&driver_usbdux);
usb_deregister(&usbduxsub_driver);
diff --git a/drivers/staging/comedi/drivers/usbduxfast.c b/drivers/staging/comedi/drivers/usbduxfast.c
index 3a00ff0cfc5a..2fb64de3f86b 100644
--- a/drivers/staging/comedi/drivers/usbduxfast.c
+++ b/drivers/staging/comedi/drivers/usbduxfast.c
@@ -1,33 +1,20 @@
-#define DRIVER_VERSION "v0.99a"
-#define DRIVER_AUTHOR "Bernd Porr, BerndPorr@f2s.com"
-#define DRIVER_DESC "USB-DUXfast, BerndPorr@f2s.com"
-/*
- comedi/drivers/usbduxfast.c
- Copyright (C) 2004 Bernd Porr, Bernd.Porr@f2s.com
-
- 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.
-*/
-
/*
-Driver: usbduxfast
-Description: ITL USB-DUXfast
-Devices: [ITL] USB-DUX (usbduxfast.o)
-Author: Bernd Porr <BerndPorr@f2s.com>
-Updated: 04 Dec 2006
-Status: testing
-*/
+ * Copyright (C) 2004 Bernd Porr, Bernd.Porr@f2s.com
+ *
+ * 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.
+ */
/*
* I must give credit here to Chris Baugher who
@@ -42,12 +29,16 @@ Status: testing
* 0.9: Dropping the first data packet which seems to be from the last transfer.
* Buffer overflows in the FX2 are handed over to comedi.
* 0.92: Dropping now 4 packets. The quad buffer has to be emptied.
- * Added insn command basically for testing. Sample rate is 1MHz/16ch=62.5kHz
+ * Added insn command basically for testing. Sample rate is
+ * 1MHz/16ch=62.5kHz
* 0.99: Ian Abbott pointed out a bug which has been corrected. Thanks!
* 0.99a: added external trigger.
+ * 1.00: added firmware kernel request to the driver which fixed
+ * udev coldplug problem
*/
#include <linux/kernel.h>
+#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -59,536 +50,549 @@ Status: testing
#include "comedi_fc.h"
#include "../comedidev.h"
-// (un)comment this if you want to have debug info.
-//#define CONFIG_COMEDI_DEBUG
-#undef CONFIG_COMEDI_DEBUG
+#define DRIVER_VERSION "v1.0"
+#define DRIVER_AUTHOR "Bernd Porr, BerndPorr@f2s.com"
+#define DRIVER_DESC "USB-DUXfast, BerndPorr@f2s.com"
#define BOARDNAME "usbduxfast"
-// timeout for the USB-transfer
-#define EZTIMEOUT 30
+/*
+ * timeout for the USB-transfer
+ */
+#define EZTIMEOUT 30
-// constants for "firmware" upload and download
-#define USBDUXFASTSUB_FIRMWARE 0xA0
-#define VENDOR_DIR_IN 0xC0
-#define VENDOR_DIR_OUT 0x40
+/*
+ * constants for "firmware" upload and download
+ */
+#define USBDUXFASTSUB_FIRMWARE 0xA0
+#define VENDOR_DIR_IN 0xC0
+#define VENDOR_DIR_OUT 0x40
-// internal adresses of the 8051 processor
-#define USBDUXFASTSUB_CPUCS 0xE600
+/*
+ * internal adresses of the 8051 processor
+ */
+#define USBDUXFASTSUB_CPUCS 0xE600
-// max lenghth of the transfer-buffer for software upload
-#define TB_LEN 0x2000
+/*
+ * max lenghth of the transfer-buffer for software upload
+ */
+#define TB_LEN 0x2000
-// Input endpoint number
-#define BULKINEP 6
+/*
+ * input endpoint number
+ */
+#define BULKINEP 6
-// Endpoint for the A/D channellist: bulk OUT
-#define CHANNELLISTEP 4
+/*
+ * endpoint for the A/D channellist: bulk OUT
+ */
+#define CHANNELLISTEP 4
-// Number of channels
-#define NUMCHANNELS 32
+/*
+ * number of channels
+ */
+#define NUMCHANNELS 32
-// size of the waveform descriptor
-#define WAVESIZE 0x20
+/*
+ * size of the waveform descriptor
+ */
+#define WAVESIZE 0x20
-// Size of one A/D value
-#define SIZEADIN ((sizeof(int16_t)))
+/*
+ * size of one A/D value
+ */
+#define SIZEADIN (sizeof(int16_t))
-// Size of the input-buffer IN BYTES
-#define SIZEINBUF 512
+/*
+ * size of the input-buffer IN BYTES
+ */
+#define SIZEINBUF 512
-// 16 bytes.
-#define SIZEINSNBUF 512
+/*
+ * 16 bytes
+ */
+#define SIZEINSNBUF 512
-// Size of the buffer for the dux commands
-#define SIZEOFDUXBUFFER 256 // bytes
+/*
+ * size of the buffer for the dux commands in bytes
+ */
+#define SIZEOFDUXBUFFER 256
-// Number of in-URBs which receive the data: min=5
-#define NUMOFINBUFFERSHIGH 10
+/*
+ * number of in-URBs which receive the data: min=5
+ */
+#define NUMOFINBUFFERSHIGH 10
-// Total number of usbduxfast devices
-#define NUMUSBDUXFAST 16
+/*
+ * total number of usbduxfast devices
+ */
+#define NUMUSBDUXFAST 16
-// Number of subdevices
-#define N_SUBDEVICES 1
+/*
+ * number of subdevices
+ */
+#define N_SUBDEVICES 1
-// Analogue in subdevice
-#define SUBDEV_AD 0
+/*
+ * analogue in subdevice
+ */
+#define SUBDEV_AD 0
-// min delay steps for more than one channel
-// basically when the mux gives up. ;-)
-#define MIN_SAMPLING_PERIOD 9 // steps at 30MHz in the FX2
+/*
+ * min delay steps for more than one channel
+ * basically when the mux gives up ;-)
+ *
+ * steps at 30MHz in the FX2
+ */
+#define MIN_SAMPLING_PERIOD 9
-// Max number of 1/30MHz delay steps:
-#define MAX_SAMPLING_PERIOD 500
+/*
+ * max number of 1/30MHz delay steps
+ */
+#define MAX_SAMPLING_PERIOD 500
-// Number of received packets to ignore before we start handing data over to comedi.
-// It's quad buffering and we have to ignore 4 packets.
-#define PACKETS_TO_IGNORE 4
+/*
+ * number of received packets to ignore before we start handing data
+ * over to comedi, it's quad buffering and we have to ignore 4 packets
+ */
+#define PACKETS_TO_IGNORE 4
-/////////////////////////////////////////////
-// comedi constants
-static const comedi_lrange range_usbduxfast_ai_range = { 2, {
- BIP_RANGE(0.75),
- BIP_RANGE(0.5),
- }
+/*
+ * comedi constants
+ */
+static const struct comedi_lrange range_usbduxfast_ai_range = {
+ 2, { BIP_RANGE(0.75), BIP_RANGE(0.5) }
};
/*
* private structure of one subdevice
+ *
+ * this is the structure which holds all the data of this driver
+ * one sub device just now: A/D
*/
-
-// This is the structure which holds all the data of this driver
-// one sub device just now: A/D
-typedef struct {
- // attached?
- int attached;
- // is it associated with a subdevice?
- int probed;
- // pointer to the usb-device
- struct usb_device *usbdev;
- // BULK-transfer handling: urb
- struct urb *urbIn;
+struct usbduxfastsub_s {
+ int attached; /* is attached? */
+ int probed; /* is it associated with a subdevice? */
+ struct usb_device *usbdev; /* pointer to the usb-device */
+ struct urb *urbIn; /* BULK-transfer handling: urb */
int8_t *transfer_buffer;
- // input buffer for single insn
- int16_t *insnBuffer;
- // interface number
- int ifnum;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
- // interface structure in 2.6
- struct usb_interface *interface;
-#endif
- // comedi device for the interrupt context
- comedi_device *comedidev;
- // asynchronous command is running
- short int ai_cmd_running;
- // continous aquisition
- short int ai_continous;
- // number of samples to aquire
- long int ai_sample_count;
- // commands
- uint8_t *dux_commands;
- // counter which ignores the first buffers
- int ignore;
+ int16_t *insnBuffer; /* input buffer for single insn */
+ int ifnum; /* interface number */
+ struct usb_interface *interface; /* interface structure */
+ struct comedi_device *comedidev; /* comedi device for the interrupt
+ context */
+ short int ai_cmd_running; /* asynchronous command is running */
+ short int ai_continous; /* continous aquisition */
+ long int ai_sample_count; /* number of samples to aquire */
+ uint8_t *dux_commands; /* commands */
+ int ignore; /* counter which ignores the first
+ buffers */
struct semaphore sem;
-} usbduxfastsub_t;
+};
-// The pointer to the private usb-data of the driver
-// is also the private data for the comedi-device.
-// This has to be global as the usb subsystem needs
-// global variables. The other reason is that this
-// structure must be there _before_ any comedi
-// command is issued. The usb subsystem must be
-// initialised before comedi can access it.
-static usbduxfastsub_t usbduxfastsub[NUMUSBDUXFAST];
+/*
+ * The pointer to the private usb-data of the driver
+ * is also the private data for the comedi-device.
+ * This has to be global as the usb subsystem needs
+ * global variables. The other reason is that this
+ * structure must be there _before_ any comedi
+ * command is issued. The usb subsystem must be
+ * initialised before comedi can access it.
+ */
+static struct usbduxfastsub_s usbduxfastsub[NUMUSBDUXFAST];
static DECLARE_MUTEX(start_stop_sem);
-// bulk transfers to usbduxfast
-
+/*
+ * bulk transfers to usbduxfast
+ */
#define SENDADCOMMANDS 0
#define SENDINITEP6 1
-static int send_dux_commands(usbduxfastsub_t * this_usbduxfastsub, int cmd_type)
+static int send_dux_commands(struct usbduxfastsub_s *udfs, int cmd_type)
{
- int result, nsent;
- this_usbduxfastsub->dux_commands[0] = cmd_type;
+ int tmp, nsent;
+
+ udfs->dux_commands[0] = cmd_type;
+
#ifdef CONFIG_COMEDI_DEBUG
- int i;
- printk("comedi%d: usbduxfast: dux_commands: ",
- this_usbduxfastsub->comedidev->minor);
- for (i = 0; i < SIZEOFDUXBUFFER; i++) {
- printk(" %02x", this_usbduxfastsub->dux_commands[i]);
- }
+ printk(KERN_DEBUG "comedi%d: usbduxfast: dux_commands: ",
+ udfs->comedidev->minor);
+ for (tmp = 0; tmp < SIZEOFDUXBUFFER; tmp++)
+ printk(" %02x", udfs->dux_commands[tmp]);
printk("\n");
#endif
- result = usb_bulk_msg(this_usbduxfastsub->usbdev,
- usb_sndbulkpipe(this_usbduxfastsub->usbdev,
- CHANNELLISTEP),
- this_usbduxfastsub->dux_commands, SIZEOFDUXBUFFER,
- &nsent, 10000);
- if (result < 0) {
- printk("comedi%d: could not transmit dux_commands to the usb-device, err=%d\n", this_usbduxfastsub->comedidev->minor, result);
- }
- return result;
+
+ tmp = usb_bulk_msg(udfs->usbdev,
+ usb_sndbulkpipe(udfs->usbdev, CHANNELLISTEP),
+ udfs->dux_commands, SIZEOFDUXBUFFER, &nsent, 10000);
+ if (tmp < 0)
+ printk(KERN_ERR "comedi%d: could not transmit dux_commands to"
+ "the usb-device, err=%d\n", udfs->comedidev->minor, tmp);
+ return tmp;
}
-// Stops the data acquision
-// It should be safe to call this function from any context
-static int usbduxfastsub_unlink_InURBs(usbduxfastsub_t * usbduxfastsub_tmp)
+/*
+ * Stops the data acquision.
+ * It should be safe to call this function from any context.
+ */
+static int usbduxfastsub_unlink_InURBs(struct usbduxfastsub_s *udfs)
{
int j = 0;
int err = 0;
- if (usbduxfastsub_tmp && usbduxfastsub_tmp->urbIn) {
- usbduxfastsub_tmp->ai_cmd_running = 0;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
- j = usb_unlink_urb(usbduxfastsub_tmp->urbIn);
- if (j < 0) {
- err = j;
- }
-#else
- // waits until a running transfer is over
- usb_kill_urb(usbduxfastsub_tmp->urbIn);
+ if (udfs && udfs->urbIn) {
+ udfs->ai_cmd_running = 0;
+ /* waits until a running transfer is over */
+ usb_kill_urb(udfs->urbIn);
j = 0;
-#endif
}
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi: usbduxfast: unlinked InURB: res=%d\n", j);
+ printk(KERN_DEBUG "comedi: usbduxfast: unlinked InURB: res=%d\n", j);
#endif
return err;
}
-/* This will stop a running acquisition operation */
-// Is called from within this driver from both the
-// interrupt context and from comedi
-static int usbduxfast_ai_stop(usbduxfastsub_t * this_usbduxfastsub,
+/*
+ * This will stop a running acquisition operation.
+ * Is called from within this driver from both the
+ * interrupt context and from comedi.
+ */
+static int usbduxfast_ai_stop(struct usbduxfastsub_s *udfs,
int do_unlink)
{
int ret = 0;
- if (!this_usbduxfastsub) {
- printk("comedi?: usbduxfast_ai_stop: this_usbduxfastsub=NULL!\n");
+ if (!udfs) {
+ printk(KERN_ERR "comedi?: usbduxfast_ai_stop: udfs=NULL!\n");
return -EFAULT;
}
+
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi: usbduxfast_ai_stop\n");
+ printk(KERN_DEBUG "comedi: usbduxfast_ai_stop\n");
#endif
- this_usbduxfastsub->ai_cmd_running = 0;
+ udfs->ai_cmd_running = 0;
- if (do_unlink) {
- // stop aquistion
- ret = usbduxfastsub_unlink_InURBs(this_usbduxfastsub);
- }
+ if (do_unlink)
+ ret = usbduxfastsub_unlink_InURBs(udfs); /* stop aquistion */
return ret;
}
-// This will cancel a running acquisition operation.
-// This is called by comedi but never from inside the
-// driver.
-static int usbduxfast_ai_cancel(comedi_device * dev, comedi_subdevice * s)
+/*
+ * This will cancel a running acquisition operation.
+ * This is called by comedi but never from inside the driver.
+ */
+static int usbduxfast_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
- usbduxfastsub_t *this_usbduxfastsub;
- int res = 0;
+ struct usbduxfastsub_s *udfs;
+ int ret;
- // force unlink of all urbs
+ /* force unlink of all urbs */
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi: usbduxfast_ai_cancel\n");
+ printk(KERN_DEBUG "comedi: usbduxfast_ai_cancel\n");
#endif
- this_usbduxfastsub = dev->private;
- if (!this_usbduxfastsub) {
- printk("comedi: usbduxfast_ai_cancel: this_usbduxfastsub=NULL\n");
+ udfs = dev->private;
+ if (!udfs) {
+ printk(KERN_ERR "comedi: usbduxfast_ai_cancel: udfs=NULL\n");
return -EFAULT;
}
- down(&this_usbduxfastsub->sem);
- if (!(this_usbduxfastsub->probed)) {
- up(&this_usbduxfastsub->sem);
+ down(&udfs->sem);
+ if (!udfs->probed) {
+ up(&udfs->sem);
return -ENODEV;
}
- // unlink
- res = usbduxfast_ai_stop(this_usbduxfastsub, 1);
- up(&this_usbduxfastsub->sem);
+ /* unlink */
+ ret = usbduxfast_ai_stop(udfs, 1);
+ up(&udfs->sem);
- return res;
+ return ret;
}
-// analogue IN
-// interrupt service routine
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
-static void usbduxfastsub_ai_Irq(struct urb *urb)
-#else
+/*
+ * analogue IN
+ * interrupt service routine
+ */
static void usbduxfastsub_ai_Irq(struct urb *urb PT_REGS_ARG)
-#endif
{
int n, err;
- usbduxfastsub_t *this_usbduxfastsub;
- comedi_device *this_comedidev;
- comedi_subdevice *s;
+ struct usbduxfastsub_s *udfs;
+ struct comedi_device *this_comedidev;
+ struct comedi_subdevice *s;
uint16_t *p;
- // sanity checks
- // is the urb there?
+ /* sanity checks - is the urb there? */
if (!urb) {
- printk("comedi_: usbduxfast_: ao int-handler called with urb=NULL!\n");
+ printk(KERN_ERR "comedi_: usbduxfast_: ao int-handler called "
+ "with urb=NULL!\n");
return;
}
- // the context variable points to the subdevice
+ /* the context variable points to the subdevice */
this_comedidev = urb->context;
if (!this_comedidev) {
- printk("comedi_: usbduxfast_: urb context is a NULL pointer!\n");
+ printk(KERN_ERR "comedi_: usbduxfast_: urb context is a NULL "
+ "pointer!\n");
return;
}
- // the private structure of the subdevice is usbduxfastsub_t
- this_usbduxfastsub = this_comedidev->private;
- if (!this_usbduxfastsub) {
- printk("comedi_: usbduxfast_: private of comedi subdev is a NULL pointer!\n");
+ /* the private structure of the subdevice is usbduxfastsub_s */
+ udfs = this_comedidev->private;
+ if (!udfs) {
+ printk(KERN_ERR "comedi_: usbduxfast_: private of comedi "
+ "subdev is a NULL pointer!\n");
return;
}
- // are we running a command?
- if (unlikely(!(this_usbduxfastsub->ai_cmd_running))) {
- // not running a command
- // do not continue execution if no asynchronous command is running
- // in particular not resubmit
+ /* are we running a command? */
+ if (unlikely(!udfs->ai_cmd_running)) {
+ /*
+ * not running a command
+ * do not continue execution if no asynchronous command
+ * is running in particular not resubmit
+ */
return;
}
- if (unlikely(!(this_usbduxfastsub->attached))) {
- // no comedi device there
+ if (unlikely(!udfs->attached)) {
+ /* no comedi device there */
return;
}
- // subdevice which is the AD converter
+ /* subdevice which is the AD converter */
s = this_comedidev->subdevices + SUBDEV_AD;
- // first we test if something unusual has just happened
+ /* first we test if something unusual has just happened */
switch (urb->status) {
case 0:
break;
- // happens after an unlink command or when the device is plugged out
+ /*
+ * happens after an unlink command or when the device
+ * is plugged out
+ */
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
case -ECONNABORTED:
- // tell this comedi
+ /* tell this comedi */
s->async->events |= COMEDI_CB_EOA;
s->async->events |= COMEDI_CB_ERROR;
- comedi_event(this_usbduxfastsub->comedidev, s);
- // stop the transfer w/o unlink
- usbduxfast_ai_stop(this_usbduxfastsub, 0);
+ comedi_event(udfs->comedidev, s);
+ /* stop the transfer w/o unlink */
+ usbduxfast_ai_stop(udfs, 0);
return;
default:
- printk("comedi%d: usbduxfast: non-zero urb status received in ai intr context: %d\n", this_usbduxfastsub->comedidev->minor, urb->status);
+ printk("comedi%d: usbduxfast: non-zero urb status received in "
+ "ai intr context: %d\n",
+ udfs->comedidev->minor, urb->status);
s->async->events |= COMEDI_CB_EOA;
s->async->events |= COMEDI_CB_ERROR;
- comedi_event(this_usbduxfastsub->comedidev, s);
- usbduxfast_ai_stop(this_usbduxfastsub, 0);
+ comedi_event(udfs->comedidev, s);
+ usbduxfast_ai_stop(udfs, 0);
return;
}
p = urb->transfer_buffer;
- if (!this_usbduxfastsub->ignore) {
- if (!(this_usbduxfastsub->ai_continous)) {
- // not continous, fixed number of samples
+ if (!udfs->ignore) {
+ if (!udfs->ai_continous) {
+ /* not continous, fixed number of samples */
n = urb->actual_length / sizeof(uint16_t);
- if (unlikely(this_usbduxfastsub->ai_sample_count < n)) {
- // we have send only a fraction of the bytes received
+ if (unlikely(udfs->ai_sample_count < n)) {
+ /*
+ * we have send only a fraction of the bytes
+ * received
+ */
cfc_write_array_to_buffer(s,
urb->transfer_buffer,
- this_usbduxfastsub->ai_sample_count *
- sizeof(uint16_t));
- usbduxfast_ai_stop(this_usbduxfastsub, 0);
- // say comedi that the acquistion is over
+ udfs->ai_sample_count
+ * sizeof(uint16_t));
+ usbduxfast_ai_stop(udfs, 0);
+ /* say comedi that the acquistion is over */
s->async->events |= COMEDI_CB_EOA;
- comedi_event(this_usbduxfastsub->comedidev, s);
+ comedi_event(udfs->comedidev, s);
return;
}
- this_usbduxfastsub->ai_sample_count -= n;
+ udfs->ai_sample_count -= n;
}
- // write the full buffer to comedi
- cfc_write_array_to_buffer(s,
- urb->transfer_buffer, urb->actual_length);
+ /* write the full buffer to comedi */
+ cfc_write_array_to_buffer(s, urb->transfer_buffer,
+ urb->actual_length);
- // tell comedi that data is there
- comedi_event(this_usbduxfastsub->comedidev, s);
+ /* tell comedi that data is there */
+ comedi_event(udfs->comedidev, s);
} else {
- // ignore this packet
- this_usbduxfastsub->ignore--;
+ /* ignore this packet */
+ udfs->ignore--;
}
- // command is still running
- // resubmit urb for BULK transfer
- urb->dev = this_usbduxfastsub->usbdev;
+ /*
+ * command is still running
+ * resubmit urb for BULK transfer
+ */
+ urb->dev = udfs->usbdev;
urb->status = 0;
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0) {
- printk("comedi%d: usbduxfast: urb resubm failed: %d",
- this_usbduxfastsub->comedidev->minor, err);
+ printk(KERN_ERR "comedi%d: usbduxfast: urb resubm failed: %d",
+ udfs->comedidev->minor, err);
s->async->events |= COMEDI_CB_EOA;
s->async->events |= COMEDI_CB_ERROR;
- comedi_event(this_usbduxfastsub->comedidev, s);
- usbduxfast_ai_stop(this_usbduxfastsub, 0);
+ comedi_event(udfs->comedidev, s);
+ usbduxfast_ai_stop(udfs, 0);
}
}
-static int usbduxfastsub_start(usbduxfastsub_t * usbduxfastsub)
+static int usbduxfastsub_start(struct usbduxfastsub_s *udfs)
{
- int errcode = 0;
+ int ret;
unsigned char local_transfer_buffer[16];
- if (usbduxfastsub->probed) {
- // 7f92 to zero
- local_transfer_buffer[0] = 0;
- errcode = usb_control_msg(usbduxfastsub->usbdev,
- // create a pipe for a control transfer
- usb_sndctrlpipe(usbduxfastsub->usbdev, 0),
- // bRequest, "Firmware"
- USBDUXFASTSUB_FIRMWARE,
- // bmRequestType
- VENDOR_DIR_OUT,
- // Value
- USBDUXFASTSUB_CPUCS,
- // Index
- 0x0000,
- // address of the transfer buffer
- local_transfer_buffer,
- // Length
- 1,
- // Timeout
- EZTIMEOUT);
- if (errcode < 0) {
- printk("comedi_: usbduxfast_: control msg failed (start)\n");
- return errcode;
- }
+ /* 7f92 to zero */
+ local_transfer_buffer[0] = 0;
+ ret = usb_control_msg(udfs->usbdev,
+ usb_sndctrlpipe(udfs->usbdev, 0),
+ USBDUXFASTSUB_FIRMWARE, /* bRequest, "Firmware" */
+ VENDOR_DIR_OUT, /* bmRequestType */
+ USBDUXFASTSUB_CPUCS, /* Value */
+ 0x0000, /* Index */
+ local_transfer_buffer, /* address of the transfer buffer */
+ 1, /* Length */
+ EZTIMEOUT); /* Timeout */
+ if (ret < 0) {
+ printk("comedi_: usbduxfast_: control msg failed (start)\n");
+ return ret;
}
+
return 0;
}
-static int usbduxfastsub_stop(usbduxfastsub_t * usbduxfastsub)
+static int usbduxfastsub_stop(struct usbduxfastsub_s *udfs)
{
- int errcode = 0;
-
+ int ret;
unsigned char local_transfer_buffer[16];
- if (usbduxfastsub->probed) {
- // 7f92 to one
- local_transfer_buffer[0] = 1;
- errcode = usb_control_msg(usbduxfastsub->usbdev,
- usb_sndctrlpipe(usbduxfastsub->usbdev, 0),
- // bRequest, "Firmware"
- USBDUXFASTSUB_FIRMWARE,
- // bmRequestType
- VENDOR_DIR_OUT,
- // Value
- USBDUXFASTSUB_CPUCS,
- // Index
- 0x0000, local_transfer_buffer,
- // Length
- 1,
- // Timeout
- EZTIMEOUT);
- if (errcode < 0) {
- printk("comedi_: usbduxfast: control msg failed (stop)\n");
- return errcode;
- }
+
+ /* 7f92 to one */
+ local_transfer_buffer[0] = 1;
+ ret = usb_control_msg(udfs->usbdev,
+ usb_sndctrlpipe(udfs->usbdev, 0),
+ USBDUXFASTSUB_FIRMWARE, /* bRequest, "Firmware" */
+ VENDOR_DIR_OUT, /* bmRequestType */
+ USBDUXFASTSUB_CPUCS, /* Value */
+ 0x0000, /* Index */
+ local_transfer_buffer,
+ 1, /* Length */
+ EZTIMEOUT); /* Timeout */
+ if (ret < 0) {
+ printk(KERN_ERR "comedi_: usbduxfast: control msg failed "
+ "(stop)\n");
+ return ret;
}
+
return 0;
}
-static int usbduxfastsub_upload(usbduxfastsub_t * usbduxfastsub,
+static int usbduxfastsub_upload(struct usbduxfastsub_s *udfs,
unsigned char *local_transfer_buffer,
unsigned int startAddr, unsigned int len)
{
- int errcode;
+ int ret;
- if (usbduxfastsub->probed) {
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi%d: usbduxfast: uploading %d bytes",
- usbduxfastsub->comedidev->minor, len);
- printk(" to addr %d, first byte=%d.\n",
- startAddr, local_transfer_buffer[0]);
+ printk(KERN_DEBUG "comedi: usbduxfast: uploading %d bytes", len);
+ printk(KERN_DEBUG " to addr %d, first byte=%d.\n",
+ startAddr, local_transfer_buffer[0]);
#endif
- errcode = usb_control_msg(usbduxfastsub->usbdev,
- usb_sndctrlpipe(usbduxfastsub->usbdev, 0),
- // brequest, firmware
- USBDUXFASTSUB_FIRMWARE,
- // bmRequestType
- VENDOR_DIR_OUT,
- // value
- startAddr,
- // index
- 0x0000,
- // our local safe buffer
- local_transfer_buffer,
- // length
- len,
- // timeout
- EZTIMEOUT);
+ ret = usb_control_msg(udfs->usbdev,
+ usb_sndctrlpipe(udfs->usbdev, 0),
+ USBDUXFASTSUB_FIRMWARE, /* brequest, firmware */
+ VENDOR_DIR_OUT, /* bmRequestType */
+ startAddr, /* value */
+ 0x0000, /* index */
+ local_transfer_buffer, /* our local safe buffer */
+ len, /* length */
+ EZTIMEOUT); /* timeout */
+
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi_: usbduxfast: result=%d\n", errcode);
+ printk(KERN_DEBUG "comedi_: usbduxfast: result=%d\n", ret);
#endif
- if (errcode < 0) {
- printk("comedi_: usbduxfast: uppload failed\n");
- return errcode;
- }
- } else {
- // no device on the bus for this index
- return -EFAULT;
+
+ if (ret < 0) {
+ printk(KERN_ERR "comedi_: usbduxfast: uppload failed\n");
+ return ret;
}
+
return 0;
}
-int firmwareUpload(usbduxfastsub_t * usbduxfastsub,
- unsigned char *firmwareBinary, int sizeFirmware)
+int firmwareUpload(struct usbduxfastsub_s *udfs, unsigned char *firmwareBinary,
+ int sizeFirmware)
{
int ret;
- if (!firmwareBinary) {
+ if (!firmwareBinary)
return 0;
- }
- ret = usbduxfastsub_stop(usbduxfastsub);
+
+ ret = usbduxfastsub_stop(udfs);
if (ret < 0) {
- printk("comedi_: usbduxfast: can not stop firmware\n");
+ printk(KERN_ERR "comedi_: usbduxfast: can not stop firmware\n");
return ret;
}
- ret = usbduxfastsub_upload(usbduxfastsub,
- firmwareBinary, 0, sizeFirmware);
+ ret = usbduxfastsub_upload(udfs, firmwareBinary, 0, sizeFirmware);
if (ret < 0) {
- printk("comedi_: usbduxfast: firmware upload failed\n");
+ printk(KERN_ERR "comedi_: usbduxfast: firmware upload failed\n");
return ret;
}
- ret = usbduxfastsub_start(usbduxfastsub);
+ ret = usbduxfastsub_start(udfs);
if (ret < 0) {
- printk("comedi_: usbduxfast: can not start firmware\n");
+ printk(KERN_ERR "comedi_: usbduxfast: can not start firmware\n");
return ret;
}
+
return 0;
}
-int usbduxfastsub_submit_InURBs(usbduxfastsub_t * usbduxfastsub)
+int usbduxfastsub_submit_InURBs(struct usbduxfastsub_s *udfs)
{
- int errFlag;
+ int ret;
- if (!usbduxfastsub) {
+ if (!udfs)
return -EFAULT;
- }
- usb_fill_bulk_urb(usbduxfastsub->urbIn,
- usbduxfastsub->usbdev,
- usb_rcvbulkpipe(usbduxfastsub->usbdev, BULKINEP),
- usbduxfastsub->transfer_buffer,
- SIZEINBUF, usbduxfastsub_ai_Irq, usbduxfastsub->comedidev);
+
+ usb_fill_bulk_urb(udfs->urbIn, udfs->usbdev,
+ usb_rcvbulkpipe(udfs->usbdev, BULKINEP),
+ udfs->transfer_buffer,
+ SIZEINBUF, usbduxfastsub_ai_Irq, udfs->comedidev);
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi%d: usbduxfast: submitting in-urb: %x,%x\n",
- usbduxfastsub->comedidev->minor,
- (int)(usbduxfastsub->urbIn->context),
- (int)(usbduxfastsub->urbIn->dev));
+ printk(KERN_DEBUG "comedi%d: usbduxfast: submitting in-urb: "
+ "0x%p,0x%p\n", udfs->comedidev->minor, udfs->urbIn->context,
+ udfs->urbIn->dev);
#endif
- errFlag = usb_submit_urb(usbduxfastsub->urbIn, GFP_ATOMIC);
- if (errFlag) {
- printk("comedi_: usbduxfast: ai: usb_submit_urb error %d\n",
- errFlag);
- return errFlag;
+ ret = usb_submit_urb(udfs->urbIn, GFP_ATOMIC);
+ if (ret) {
+ printk(KERN_ERR "comedi_: usbduxfast: ai: usb_submit_urb error"
+ " %d\n", ret);
+ return ret;
}
return 0;
}
-static int usbduxfast_ai_cmdtest(comedi_device * dev,
- comedi_subdevice * s, comedi_cmd * cmd)
+static int usbduxfast_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
int err = 0, stop_mask = 0;
- long int steps, tmp = 0;
+ long int steps, tmp;
int minSamplPer;
- usbduxfastsub_t *this_usbduxfastsub = dev->private;
- if (!(this_usbduxfastsub->probed)) {
+ struct usbduxfastsub_s *udfs = dev->private;
+
+ if (!udfs->probed)
return -ENODEV;
- }
+
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi%d: usbduxfast_ai_cmdtest\n", dev->minor);
- printk("comedi%d: usbduxfast: convert_arg=%u scan_begin_arg=%u\n",
- dev->minor, cmd->convert_arg, cmd->scan_begin_arg);
+ printk(KERN_DEBUG "comedi%d: usbduxfast_ai_cmdtest\n", dev->minor);
+ printk(KERN_DEBUG "comedi%d: usbduxfast: convert_arg=%u "
+ "scan_begin_arg=%u\n",
+ dev->minor, cmd->convert_arg, cmd->scan_begin_arg);
#endif
/* step 1: make sure trigger sources are trivially valid */
@@ -621,7 +625,9 @@ static int usbduxfast_ai_cmdtest(comedi_device * dev,
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually compatible */
+ /*
+ * step 2: make sure trigger sources are unique and mutually compatible
+ */
if (cmd->start_src != TRIG_NOW &&
cmd->start_src != TRIG_EXT && cmd->start_src != TRIG_INT)
@@ -636,7 +642,7 @@ static int usbduxfast_ai_cmdtest(comedi_device * dev,
cmd->stop_src != TRIG_EXT && cmd->stop_src != TRIG_NONE)
err++;
- // can't have external stop and start triggers at once
+ /* can't have external stop and start triggers at once */
if (cmd->start_src == TRIG_EXT && cmd->stop_src == TRIG_EXT)
err++;
@@ -650,29 +656,28 @@ static int usbduxfast_ai_cmdtest(comedi_device * dev,
err++;
}
- if (!cmd->chanlist_len) {
+ if (!cmd->chanlist_len)
err++;
- }
+
if (cmd->scan_end_arg != cmd->chanlist_len) {
cmd->scan_end_arg = cmd->chanlist_len;
err++;
}
- if (cmd->chanlist_len == 1) {
+ if (cmd->chanlist_len == 1)
minSamplPer = 1;
- } else {
+ else
minSamplPer = MIN_SAMPLING_PERIOD;
- }
if (cmd->convert_src == TRIG_TIMER) {
steps = cmd->convert_arg * 30;
- if (steps < (minSamplPer * 1000)) {
+ if (steps < (minSamplPer * 1000))
steps = minSamplPer * 1000;
- }
- if (steps > (MAX_SAMPLING_PERIOD * 1000)) {
+
+ if (steps > (MAX_SAMPLING_PERIOD * 1000))
steps = MAX_SAMPLING_PERIOD * 1000;
- }
- // calc arg again
+
+ /* calc arg again */
tmp = steps / 30;
if (cmd->convert_arg != tmp) {
cmd->convert_arg = tmp;
@@ -680,10 +685,10 @@ static int usbduxfast_ai_cmdtest(comedi_device * dev,
}
}
- if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->scan_begin_src == TRIG_TIMER)
err++;
- }
- // stop source
+
+ /* stop source */
switch (cmd->stop_src) {
case TRIG_COUNT:
if (!cmd->stop_arg) {
@@ -697,7 +702,10 @@ static int usbduxfast_ai_cmdtest(comedi_device * dev,
err++;
}
break;
- // TRIG_EXT doesn't care since it doesn't trigger off a numbered channel
+ /*
+ * TRIG_EXT doesn't care since it doesn't trigger
+ * off a numbered channel
+ */
default:
break;
}
@@ -711,600 +719,688 @@ static int usbduxfast_ai_cmdtest(comedi_device * dev,
}
-static int usbduxfast_ai_inttrig(comedi_device * dev,
- comedi_subdevice * s, unsigned int trignum)
+static int usbduxfast_ai_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s, unsigned int trignum)
{
int ret;
- usbduxfastsub_t *this_usbduxfastsub = dev->private;
- if (!this_usbduxfastsub) {
+ struct usbduxfastsub_s *udfs = dev->private;
+
+ if (!udfs)
return -EFAULT;
- }
- down(&this_usbduxfastsub->sem);
- if (!(this_usbduxfastsub->probed)) {
- up(&this_usbduxfastsub->sem);
+
+ down(&udfs->sem);
+ if (!udfs->probed) {
+ up(&udfs->sem);
return -ENODEV;
}
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi%d: usbduxfast_ai_inttrig\n", dev->minor);
+ printk(KERN_DEBUG "comedi%d: usbduxfast_ai_inttrig\n", dev->minor);
#endif
if (trignum != 0) {
- printk("comedi%d: usbduxfast_ai_inttrig: invalid trignum\n",
- dev->minor);
- up(&this_usbduxfastsub->sem);
+ printk(KERN_ERR "comedi%d: usbduxfast_ai_inttrig: invalid"
+ " trignum\n", dev->minor);
+ up(&udfs->sem);
return -EINVAL;
}
- if (!(this_usbduxfastsub->ai_cmd_running)) {
- this_usbduxfastsub->ai_cmd_running = 1;
- ret = usbduxfastsub_submit_InURBs(this_usbduxfastsub);
+ if (!udfs->ai_cmd_running) {
+ udfs->ai_cmd_running = 1;
+ ret = usbduxfastsub_submit_InURBs(udfs);
if (ret < 0) {
- printk("comedi%d: usbduxfast_ai_inttrig: urbSubmit: err=%d\n", dev->minor, ret);
- this_usbduxfastsub->ai_cmd_running = 0;
- up(&this_usbduxfastsub->sem);
+ printk(KERN_ERR "comedi%d: usbduxfast_ai_inttrig: "
+ "urbSubmit: err=%d\n", dev->minor, ret);
+ udfs->ai_cmd_running = 0;
+ up(&udfs->sem);
return ret;
}
s->async->inttrig = NULL;
} else {
- printk("comedi%d: ai_inttrig but acqu is already running\n",
- dev->minor);
+ printk(KERN_ERR "comedi%d: ai_inttrig but acqu is already"
+ " running\n", dev->minor);
}
- up(&this_usbduxfastsub->sem);
+ up(&udfs->sem);
return 1;
}
-// offsets for the GPIF bytes
-// the first byte is the command byte
-#define LENBASE 1+0x00
-#define OPBASE 1+0x08
-#define OUTBASE 1+0x10
-#define LOGBASE 1+0x18
+/*
+ * offsets for the GPIF bytes
+ * the first byte is the command byte
+ */
+#define LENBASE (1+0x00)
+#define OPBASE (1+0x08)
+#define OUTBASE (1+0x10)
+#define LOGBASE (1+0x18)
-static int usbduxfast_ai_cmd(comedi_device * dev, comedi_subdevice * s)
+static int usbduxfast_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
- comedi_cmd *cmd = &s->async->cmd;
+ struct comedi_cmd *cmd = &s->async->cmd;
unsigned int chan, gain, rngmask = 0xff;
int i, j, ret;
- usbduxfastsub_t *this_usbduxfastsub = dev->private;
+ struct usbduxfastsub_s *udfs;
int result;
long steps, steps_tmp;
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi%d: usbduxfast_ai_cmd\n", dev->minor);
+ printk(KERN_DEBUG "comedi%d: usbduxfast_ai_cmd\n", dev->minor);
#endif
- if (!this_usbduxfastsub) {
+ udfs = dev->private;
+ if (!udfs)
return -EFAULT;
- }
- down(&this_usbduxfastsub->sem);
- if (!(this_usbduxfastsub->probed)) {
- up(&this_usbduxfastsub->sem);
+
+ down(&udfs->sem);
+ if (!udfs->probed) {
+ up(&udfs->sem);
return -ENODEV;
}
- if (this_usbduxfastsub->ai_cmd_running) {
- printk("comedi%d: ai_cmd not possible. Another ai_cmd is running.\n", dev->minor);
- up(&this_usbduxfastsub->sem);
+ if (udfs->ai_cmd_running) {
+ printk(KERN_ERR "comedi%d: ai_cmd not possible. Another ai_cmd"
+ " is running.\n", dev->minor);
+ up(&udfs->sem);
return -EBUSY;
}
- // set current channel of the running aquisition to zero
+ /* set current channel of the running aquisition to zero */
s->async->cur_chan = 0;
- // ignore the first buffers from the device if there is an error condition
- this_usbduxfastsub->ignore = PACKETS_TO_IGNORE;
+ /*
+ * ignore the first buffers from the device if there
+ * is an error condition
+ */
+ udfs->ignore = PACKETS_TO_IGNORE;
if (cmd->chanlist_len > 0) {
gain = CR_RANGE(cmd->chanlist[0]);
for (i = 0; i < cmd->chanlist_len; ++i) {
chan = CR_CHAN(cmd->chanlist[i]);
if (chan != i) {
- printk("comedi%d: cmd is accepting only consecutive channels.\n", dev->minor);
- up(&this_usbduxfastsub->sem);
+ printk(KERN_ERR "comedi%d: cmd is accepting "
+ "only consecutive channels.\n",
+ dev->minor);
+ up(&udfs->sem);
return -EINVAL;
}
if ((gain != CR_RANGE(cmd->chanlist[i]))
&& (cmd->chanlist_len > 3)) {
- printk("comedi%d: the gain must be the same for all channels.\n", dev->minor);
- up(&this_usbduxfastsub->sem);
+ printk(KERN_ERR "comedi%d: the gain must be"
+ " the same for all channels.\n",
+ dev->minor);
+ up(&udfs->sem);
return -EINVAL;
}
if (i >= NUMCHANNELS) {
- printk("comedi%d: channel list too long\n",
- dev->minor);
+ printk(KERN_ERR "comedi%d: channel list too"
+ " long\n", dev->minor);
break;
}
}
}
steps = 0;
if (cmd->scan_begin_src == TRIG_TIMER) {
- printk("comedi%d: usbduxfast: scan_begin_src==TRIG_TIMER not valid.\n", dev->minor);
- up(&this_usbduxfastsub->sem);
+ printk(KERN_ERR "comedi%d: usbduxfast: "
+ "scan_begin_src==TRIG_TIMER not valid.\n", dev->minor);
+ up(&udfs->sem);
return -EINVAL;
}
- if (cmd->convert_src == TRIG_TIMER) {
+ if (cmd->convert_src == TRIG_TIMER)
steps = (cmd->convert_arg * 30) / 1000;
- }
+
if ((steps < MIN_SAMPLING_PERIOD) && (cmd->chanlist_len != 1)) {
- printk("comedi%d: usbduxfast: ai_cmd: steps=%ld, scan_begin_arg=%d. Not properly tested by cmdtest?\n", dev->minor, steps, cmd->scan_begin_arg);
- up(&this_usbduxfastsub->sem);
+ printk(KERN_ERR "comedi%d: usbduxfast: ai_cmd: steps=%ld, "
+ "scan_begin_arg=%d. Not properly tested by cmdtest?\n",
+ dev->minor, steps, cmd->scan_begin_arg);
+ up(&udfs->sem);
return -EINVAL;
}
if (steps > MAX_SAMPLING_PERIOD) {
- printk("comedi%d: usbduxfast: ai_cmd: sampling rate too low.\n",
- dev->minor);
- up(&this_usbduxfastsub->sem);
+ printk(KERN_ERR "comedi%d: usbduxfast: ai_cmd: sampling rate "
+ "too low.\n", dev->minor);
+ up(&udfs->sem);
return -EINVAL;
}
if ((cmd->start_src == TRIG_EXT) && (cmd->chanlist_len != 1)
&& (cmd->chanlist_len != 16)) {
- printk("comedi%d: usbduxfast: ai_cmd: TRIG_EXT only with 1 or 16 channels possible.\n", dev->minor);
- up(&this_usbduxfastsub->sem);
+ printk(KERN_ERR "comedi%d: usbduxfast: ai_cmd: TRIG_EXT only"
+ " with 1 or 16 channels possible.\n", dev->minor);
+ up(&udfs->sem);
return -EINVAL;
}
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi%d: usbduxfast: steps=%ld, convert_arg=%u, ai_timer=%u\n",
- dev->minor,
- steps, cmd->convert_arg, this_usbduxfastsub->ai_timer);
+ printk(KERN_DEBUG "comedi%d: usbduxfast: steps=%ld, convert_arg=%u\n",
+ dev->minor, steps, cmd->convert_arg);
#endif
switch (cmd->chanlist_len) {
- // one channel
case 1:
+ /*
+ * one channel
+ */
+
if (CR_RANGE(cmd->chanlist[0]) > 0)
rngmask = 0xff - 0x04;
else
rngmask = 0xff;
- // for external trigger: looping in this state until the RDY0 pin
- // becomes zero
- if (cmd->start_src == TRIG_EXT) { // we loop here until ready has been set
- this_usbduxfastsub->dux_commands[LENBASE + 0] = 0x01; // branch back to state 0
- this_usbduxfastsub->dux_commands[OPBASE + 0] = 0x01; // deceision state w/o data
- this_usbduxfastsub->dux_commands[OUTBASE + 0] =
- 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 0] = 0x00; // RDY0 = 0
- } else { // we just proceed to state 1
- this_usbduxfastsub->dux_commands[LENBASE + 0] = 1;
- this_usbduxfastsub->dux_commands[OPBASE + 0] = 0;
- this_usbduxfastsub->dux_commands[OUTBASE + 0] =
- 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 0] = 0;
+ /*
+ * for external trigger: looping in this state until
+ * the RDY0 pin becomes zero
+ */
+
+ /* we loop here until ready has been set */
+ if (cmd->start_src == TRIG_EXT) {
+ /* branch back to state 0 */
+ udfs->dux_commands[LENBASE+0] = 0x01;
+ /* deceision state w/o data */
+ udfs->dux_commands[OPBASE+0] = 0x01;
+ udfs->dux_commands[OUTBASE+0] = 0xFF & rngmask;
+ /* RDY0 = 0 */
+ udfs->dux_commands[LOGBASE+0] = 0x00;
+ } else { /* we just proceed to state 1 */
+ udfs->dux_commands[LENBASE+0] = 1;
+ udfs->dux_commands[OPBASE+0] = 0;
+ udfs->dux_commands[OUTBASE+0] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+0] = 0;
}
if (steps < MIN_SAMPLING_PERIOD) {
- // for fast single channel aqu without mux
+ /* for fast single channel aqu without mux */
if (steps <= 1) {
- // we just stay here at state 1 and rexecute the same state
- // this gives us 30MHz sampling rate
- this_usbduxfastsub->dux_commands[LENBASE + 1] = 0x89; // branch back to state 1
- this_usbduxfastsub->dux_commands[OPBASE + 1] = 0x03; // deceision state with data
- this_usbduxfastsub->dux_commands[OUTBASE + 1] =
- 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 1] = 0xFF; // doesn't matter
+ /*
+ * we just stay here at state 1 and rexecute
+ * the same state this gives us 30MHz sampling
+ * rate
+ */
+
+ /* branch back to state 1 */
+ udfs->dux_commands[LENBASE+1] = 0x89;
+ /* deceision state with data */
+ udfs->dux_commands[OPBASE+1] = 0x03;
+ udfs->dux_commands[OUTBASE+1] = 0xFF & rngmask;
+ /* doesn't matter */
+ udfs->dux_commands[LOGBASE+1] = 0xFF;
} else {
- // we loop through two states: data and delay: max rate is 15Mhz
- this_usbduxfastsub->dux_commands[LENBASE + 1] =
- steps - 1;
- this_usbduxfastsub->dux_commands[OPBASE + 1] = 0x02; // data
- this_usbduxfastsub->dux_commands[OUTBASE + 1] =
- 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 1] = 0; // doesn't matter
-
- this_usbduxfastsub->dux_commands[LENBASE + 2] = 0x09; // branch back to state 1
- this_usbduxfastsub->dux_commands[OPBASE + 2] = 0x01; // deceision state w/o data
- this_usbduxfastsub->dux_commands[OUTBASE + 2] =
- 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 2] = 0xFF; // doesn't matter
+ /*
+ * we loop through two states: data and delay
+ * max rate is 15MHz
+ */
+ udfs->dux_commands[LENBASE+1] = steps - 1;
+ /* data */
+ udfs->dux_commands[OPBASE+1] = 0x02;
+ udfs->dux_commands[OUTBASE+1] = 0xFF & rngmask;
+ /* doesn't matter */
+ udfs->dux_commands[LOGBASE+1] = 0;
+ /* branch back to state 1 */
+ udfs->dux_commands[LENBASE+2] = 0x09;
+ /* deceision state w/o data */
+ udfs->dux_commands[OPBASE+2] = 0x01;
+ udfs->dux_commands[OUTBASE+2] = 0xFF & rngmask;
+ /* doesn't matter */
+ udfs->dux_commands[LOGBASE+2] = 0xFF;
}
} else {
- // we loop through 3 states: 2x delay and 1x data. This gives a min
- // sampling rate of 60kHz.
+ /*
+ * we loop through 3 states: 2x delay and 1x data
+ * this gives a min sampling rate of 60kHz
+ */
- // we have 1 state with duration 1
+ /* we have 1 state with duration 1 */
steps = steps - 1;
- // do the first part of the delay
- this_usbduxfastsub->dux_commands[LENBASE + 1] =
- steps / 2;
- this_usbduxfastsub->dux_commands[OPBASE + 1] = 0;
- this_usbduxfastsub->dux_commands[OUTBASE + 1] =
- 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 1] = 0;
-
- // and the second part
- this_usbduxfastsub->dux_commands[LENBASE + 2] =
- steps - steps / 2;
- this_usbduxfastsub->dux_commands[OPBASE + 2] = 0;
- this_usbduxfastsub->dux_commands[OUTBASE + 2] =
- 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 2] = 0;
-
- // get the data and branch back
- this_usbduxfastsub->dux_commands[LENBASE + 3] = 0x09; // branch back to state 1
- this_usbduxfastsub->dux_commands[OPBASE + 3] = 0x03; // deceision state w data
- this_usbduxfastsub->dux_commands[OUTBASE + 3] =
- 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 3] = 0xFF; // doesn't matter
+ /* do the first part of the delay */
+ udfs->dux_commands[LENBASE+1] = steps / 2;
+ udfs->dux_commands[OPBASE+1] = 0;
+ udfs->dux_commands[OUTBASE+1] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+1] = 0;
+
+ /* and the second part */
+ udfs->dux_commands[LENBASE+2] = steps - steps / 2;
+ udfs->dux_commands[OPBASE+2] = 0;
+ udfs->dux_commands[OUTBASE+2] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+2] = 0;
+
+ /* get the data and branch back */
+
+ /* branch back to state 1 */
+ udfs->dux_commands[LENBASE+3] = 0x09;
+ /* deceision state w data */
+ udfs->dux_commands[OPBASE+3] = 0x03;
+ udfs->dux_commands[OUTBASE+3] = 0xFF & rngmask;
+ /* doesn't matter */
+ udfs->dux_commands[LOGBASE+3] = 0xFF;
}
break;
case 2:
- // two channels
- // commit data to the FIFO
+ /*
+ * two channels
+ * commit data to the FIFO
+ */
+
if (CR_RANGE(cmd->chanlist[0]) > 0)
rngmask = 0xff - 0x04;
else
rngmask = 0xff;
- this_usbduxfastsub->dux_commands[LENBASE + 0] = 1;
- this_usbduxfastsub->dux_commands[OPBASE + 0] = 0x02; // data
- this_usbduxfastsub->dux_commands[OUTBASE + 0] = 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 0] = 0;
- // we have 1 state with duration 1: state 0
+ udfs->dux_commands[LENBASE+0] = 1;
+ /* data */
+ udfs->dux_commands[OPBASE+0] = 0x02;
+ udfs->dux_commands[OUTBASE+0] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+0] = 0;
+
+ /* we have 1 state with duration 1: state 0 */
steps_tmp = steps - 1;
if (CR_RANGE(cmd->chanlist[1]) > 0)
rngmask = 0xff - 0x04;
else
rngmask = 0xff;
- // do the first part of the delay
- this_usbduxfastsub->dux_commands[LENBASE + 1] = steps_tmp / 2;
- this_usbduxfastsub->dux_commands[OPBASE + 1] = 0;
- this_usbduxfastsub->dux_commands[OUTBASE + 1] = 0xFE & rngmask; //count
- this_usbduxfastsub->dux_commands[LOGBASE + 1] = 0;
-
- // and the second part
- this_usbduxfastsub->dux_commands[LENBASE + 2] =
- steps_tmp - steps_tmp / 2;
- this_usbduxfastsub->dux_commands[OPBASE + 2] = 0;
- this_usbduxfastsub->dux_commands[OUTBASE + 2] = 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 2] = 0;
-
- this_usbduxfastsub->dux_commands[LENBASE + 3] = 1;
- this_usbduxfastsub->dux_commands[OPBASE + 3] = 0x02; // data
- this_usbduxfastsub->dux_commands[OUTBASE + 3] = 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 3] = 0;
-
- // we have 2 states with duration 1: step 6 and the IDLE state
+
+ /* do the first part of the delay */
+ udfs->dux_commands[LENBASE+1] = steps_tmp / 2;
+ udfs->dux_commands[OPBASE+1] = 0;
+ /* count */
+ udfs->dux_commands[OUTBASE+1] = 0xFE & rngmask;
+ udfs->dux_commands[LOGBASE+1] = 0;
+
+ /* and the second part */
+ udfs->dux_commands[LENBASE+2] = steps_tmp - steps_tmp / 2;
+ udfs->dux_commands[OPBASE+2] = 0;
+ udfs->dux_commands[OUTBASE+2] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+2] = 0;
+
+ udfs->dux_commands[LENBASE+3] = 1;
+ /* data */
+ udfs->dux_commands[OPBASE+3] = 0x02;
+ udfs->dux_commands[OUTBASE+3] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+3] = 0;
+
+ /*
+ * we have 2 states with duration 1: step 6 and
+ * the IDLE state
+ */
steps_tmp = steps - 2;
if (CR_RANGE(cmd->chanlist[0]) > 0)
rngmask = 0xff - 0x04;
else
rngmask = 0xff;
- // do the first part of the delay
- this_usbduxfastsub->dux_commands[LENBASE + 4] = steps_tmp / 2;
- this_usbduxfastsub->dux_commands[OPBASE + 4] = 0;
- this_usbduxfastsub->dux_commands[OUTBASE + 4] = (0xFF - 0x02) & rngmask; //reset
- this_usbduxfastsub->dux_commands[LOGBASE + 4] = 0;
-
- // and the second part
- this_usbduxfastsub->dux_commands[LENBASE + 5] =
- steps_tmp - steps_tmp / 2;
- this_usbduxfastsub->dux_commands[OPBASE + 5] = 0;
- this_usbduxfastsub->dux_commands[OUTBASE + 5] = 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 5] = 0;
-
- this_usbduxfastsub->dux_commands[LENBASE + 6] = 1;
- this_usbduxfastsub->dux_commands[OPBASE + 6] = 0;
- this_usbduxfastsub->dux_commands[OUTBASE + 6] = 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 6] = 0;
+
+ /* do the first part of the delay */
+ udfs->dux_commands[LENBASE+4] = steps_tmp / 2;
+ udfs->dux_commands[OPBASE+4] = 0;
+ /* reset */
+ udfs->dux_commands[OUTBASE+4] = (0xFF - 0x02) & rngmask;
+ udfs->dux_commands[LOGBASE+4] = 0;
+
+ /* and the second part */
+ udfs->dux_commands[LENBASE+5] = steps_tmp - steps_tmp / 2;
+ udfs->dux_commands[OPBASE+5] = 0;
+ udfs->dux_commands[OUTBASE+5] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+5] = 0;
+
+ udfs->dux_commands[LENBASE+6] = 1;
+ udfs->dux_commands[OPBASE+6] = 0;
+ udfs->dux_commands[OUTBASE+6] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+6] = 0;
break;
case 3:
- // three channels
+ /*
+ * three channels
+ */
for (j = 0; j < 1; j++) {
if (CR_RANGE(cmd->chanlist[j]) > 0)
rngmask = 0xff - 0x04;
else
rngmask = 0xff;
- // commit data to the FIFO and do the first part of the delay
- this_usbduxfastsub->dux_commands[LENBASE + j * 2] =
- steps / 2;
- this_usbduxfastsub->dux_commands[OPBASE + j * 2] = 0x02; // data
- this_usbduxfastsub->dux_commands[OUTBASE + j * 2] = 0xFF & rngmask; // no change
- this_usbduxfastsub->dux_commands[LOGBASE + j * 2] = 0;
+ /*
+ * commit data to the FIFO and do the first part
+ * of the delay
+ */
+ udfs->dux_commands[LENBASE+j*2] = steps / 2;
+ /* data */
+ udfs->dux_commands[OPBASE+j*2] = 0x02;
+ /* no change */
+ udfs->dux_commands[OUTBASE+j*2] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+j*2] = 0;
if (CR_RANGE(cmd->chanlist[j + 1]) > 0)
rngmask = 0xff - 0x04;
else
rngmask = 0xff;
- // do the second part of the delay
- this_usbduxfastsub->dux_commands[LENBASE + j * 2 + 1] =
- steps - steps / 2;
- this_usbduxfastsub->dux_commands[OPBASE + j * 2 + 1] = 0; // no data
- this_usbduxfastsub->dux_commands[OUTBASE + j * 2 + 1] = 0xFE & rngmask; //count
- this_usbduxfastsub->dux_commands[LOGBASE + j * 2 + 1] =
- 0;
+
+ /* do the second part of the delay */
+ udfs->dux_commands[LENBASE+j*2+1] = steps - steps / 2;
+ /* no data */
+ udfs->dux_commands[OPBASE+j*2+1] = 0;
+ /* count */
+ udfs->dux_commands[OUTBASE+j*2+1] = 0xFE & rngmask;
+ udfs->dux_commands[LOGBASE+j*2+1] = 0;
}
- // 2 steps with duration 1: the idele step and step 6:
+ /* 2 steps with duration 1: the idele step and step 6: */
steps_tmp = steps - 2;
- // commit data to the FIFO and do the first part of the delay
- this_usbduxfastsub->dux_commands[LENBASE + 4] = steps_tmp / 2;
- this_usbduxfastsub->dux_commands[OPBASE + 4] = 0x02; // data
- this_usbduxfastsub->dux_commands[OUTBASE + 4] = 0xFF & rngmask; // no change
- this_usbduxfastsub->dux_commands[LOGBASE + 4] = 0;
+
+ /* commit data to the FIFO and do the first part of the delay */
+ udfs->dux_commands[LENBASE+4] = steps_tmp / 2;
+ /* data */
+ udfs->dux_commands[OPBASE+4] = 0x02;
+ udfs->dux_commands[OUTBASE+4] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+4] = 0;
if (CR_RANGE(cmd->chanlist[0]) > 0)
rngmask = 0xff - 0x04;
else
rngmask = 0xff;
- // do the second part of the delay
- this_usbduxfastsub->dux_commands[LENBASE + 5] =
- steps_tmp - steps_tmp / 2;
- this_usbduxfastsub->dux_commands[OPBASE + 5] = 0; // no data
- this_usbduxfastsub->dux_commands[OUTBASE + 5] = (0xFF - 0x02) & rngmask; // reset
- this_usbduxfastsub->dux_commands[LOGBASE + 5] = 0;
-
- this_usbduxfastsub->dux_commands[LENBASE + 6] = 1;
- this_usbduxfastsub->dux_commands[OPBASE + 6] = 0;
- this_usbduxfastsub->dux_commands[OUTBASE + 6] = 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 6] = 0;
+
+ /* do the second part of the delay */
+ udfs->dux_commands[LENBASE+5] = steps_tmp - steps_tmp / 2;
+ /* no data */
+ udfs->dux_commands[OPBASE+5] = 0;
+ /* reset */
+ udfs->dux_commands[OUTBASE+5] = (0xFF - 0x02) & rngmask;
+ udfs->dux_commands[LOGBASE+5] = 0;
+
+ udfs->dux_commands[LENBASE+6] = 1;
+ udfs->dux_commands[OPBASE+6] = 0;
+ udfs->dux_commands[OUTBASE+6] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+6] = 0;
case 16:
if (CR_RANGE(cmd->chanlist[0]) > 0)
rngmask = 0xff - 0x04;
else
rngmask = 0xff;
- if (cmd->start_src == TRIG_EXT) { // we loop here until ready has been set
- this_usbduxfastsub->dux_commands[LENBASE + 0] = 0x01; // branch back to state 0
- this_usbduxfastsub->dux_commands[OPBASE + 0] = 0x01; // deceision state w/o data
- this_usbduxfastsub->dux_commands[OUTBASE + 0] = (0xFF - 0x02) & rngmask; // reset
- this_usbduxfastsub->dux_commands[LOGBASE + 0] = 0x00; // RDY0 = 0
- } else { // we just proceed to state 1
- this_usbduxfastsub->dux_commands[LENBASE + 0] = 255; // 30us reset pulse
- this_usbduxfastsub->dux_commands[OPBASE + 0] = 0;
- this_usbduxfastsub->dux_commands[OUTBASE + 0] = (0xFF - 0x02) & rngmask; // reset
- this_usbduxfastsub->dux_commands[LOGBASE + 0] = 0;
+
+ if (cmd->start_src == TRIG_EXT) {
+ /*
+ * we loop here until ready has been set
+ */
+
+ /* branch back to state 0 */
+ udfs->dux_commands[LENBASE+0] = 0x01;
+ /* deceision state w/o data */
+ udfs->dux_commands[OPBASE+0] = 0x01;
+ /* reset */
+ udfs->dux_commands[OUTBASE+0] = (0xFF-0x02) & rngmask;
+ /* RDY0 = 0 */
+ udfs->dux_commands[LOGBASE+0] = 0x00;
+ } else {
+ /*
+ * we just proceed to state 1
+ */
+
+ /* 30us reset pulse */
+ udfs->dux_commands[LENBASE+0] = 255;
+ udfs->dux_commands[OPBASE+0] = 0;
+ /* reset */
+ udfs->dux_commands[OUTBASE+0] = (0xFF-0x02) & rngmask;
+ udfs->dux_commands[LOGBASE+0] = 0;
}
- // commit data to the FIFO
- this_usbduxfastsub->dux_commands[LENBASE + 1] = 1;
- this_usbduxfastsub->dux_commands[OPBASE + 1] = 0x02; // data
- this_usbduxfastsub->dux_commands[OUTBASE + 1] = 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 1] = 0;
+ /* commit data to the FIFO */
+ udfs->dux_commands[LENBASE+1] = 1;
+ /* data */
+ udfs->dux_commands[OPBASE+1] = 0x02;
+ udfs->dux_commands[OUTBASE+1] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+1] = 0;
- // we have 2 states with duration 1
+ /* we have 2 states with duration 1 */
steps = steps - 2;
- // do the first part of the delay
- this_usbduxfastsub->dux_commands[LENBASE + 2] = steps / 2;
- this_usbduxfastsub->dux_commands[OPBASE + 2] = 0;
- this_usbduxfastsub->dux_commands[OUTBASE + 2] = 0xFE & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 2] = 0;
-
- // and the second part
- this_usbduxfastsub->dux_commands[LENBASE + 3] =
- steps - steps / 2;
- this_usbduxfastsub->dux_commands[OPBASE + 3] = 0;
- this_usbduxfastsub->dux_commands[OUTBASE + 3] = 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 3] = 0;
-
- this_usbduxfastsub->dux_commands[LENBASE + 4] = 0x09; // branch back to state 1
- this_usbduxfastsub->dux_commands[OPBASE + 4] = 0x01; // deceision state w/o data
- this_usbduxfastsub->dux_commands[OUTBASE + 4] = 0xFF & rngmask;
- this_usbduxfastsub->dux_commands[LOGBASE + 4] = 0xFF; // doesn't matter
+ /* do the first part of the delay */
+ udfs->dux_commands[LENBASE+2] = steps / 2;
+ udfs->dux_commands[OPBASE+2] = 0;
+ udfs->dux_commands[OUTBASE+2] = 0xFE & rngmask;
+ udfs->dux_commands[LOGBASE+2] = 0;
+
+ /* and the second part */
+ udfs->dux_commands[LENBASE+3] = steps - steps / 2;
+ udfs->dux_commands[OPBASE+3] = 0;
+ udfs->dux_commands[OUTBASE+3] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+3] = 0;
+
+ /* branch back to state 1 */
+ udfs->dux_commands[LENBASE+4] = 0x09;
+ /* deceision state w/o data */
+ udfs->dux_commands[OPBASE+4] = 0x01;
+ udfs->dux_commands[OUTBASE+4] = 0xFF & rngmask;
+ /* doesn't matter */
+ udfs->dux_commands[LOGBASE+4] = 0xFF;
break;
default:
- printk("comedi %d: unsupported combination of channels\n",
- dev->minor);
- up(&this_usbduxfastsub->sem);
+ printk(KERN_ERR "comedi %d: unsupported combination of "
+ "channels\n", dev->minor);
+ up(&udfs->sem);
return -EFAULT;
}
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi %d: sending commands to the usb device\n", dev->minor);
+ printk(KERN_DEBUG "comedi %d: sending commands to the usb device\n",
+ dev->minor);
#endif
- // 0 means that the AD commands are sent
- result = send_dux_commands(this_usbduxfastsub, SENDADCOMMANDS);
+ /* 0 means that the AD commands are sent */
+ result = send_dux_commands(udfs, SENDADCOMMANDS);
if (result < 0) {
- printk("comedi%d: adc command could not be submitted. Aborting...\n", dev->minor);
- up(&this_usbduxfastsub->sem);
+ printk(KERN_ERR "comedi%d: adc command could not be submitted."
+ "Aborting...\n", dev->minor);
+ up(&udfs->sem);
return result;
}
if (cmd->stop_src == TRIG_COUNT) {
- this_usbduxfastsub->ai_sample_count =
- (cmd->stop_arg) * (cmd->scan_end_arg);
- if (usbduxfastsub->ai_sample_count < 1) {
- printk("comedi%d: (cmd->stop_arg)*(cmd->scan_end_arg)<1, aborting.\n", dev->minor);
- up(&this_usbduxfastsub->sem);
+ udfs->ai_sample_count = cmd->stop_arg * cmd->scan_end_arg;
+ if (udfs->ai_sample_count < 1) {
+ printk(KERN_ERR "comedi%d: "
+ "(cmd->stop_arg)*(cmd->scan_end_arg)<1, "
+ "aborting.\n", dev->minor);
+ up(&udfs->sem);
return -EFAULT;
}
- this_usbduxfastsub->ai_continous = 0;
+ udfs->ai_continous = 0;
} else {
- // continous aquisition
- this_usbduxfastsub->ai_continous = 1;
- this_usbduxfastsub->ai_sample_count = 0;
+ /* continous aquisition */
+ udfs->ai_continous = 1;
+ udfs->ai_sample_count = 0;
}
if ((cmd->start_src == TRIG_NOW) || (cmd->start_src == TRIG_EXT)) {
- // enable this acquisition operation
- this_usbduxfastsub->ai_cmd_running = 1;
- ret = usbduxfastsub_submit_InURBs(this_usbduxfastsub);
+ /* enable this acquisition operation */
+ udfs->ai_cmd_running = 1;
+ ret = usbduxfastsub_submit_InURBs(udfs);
if (ret < 0) {
- this_usbduxfastsub->ai_cmd_running = 0;
- // fixme: unlink here??
- up(&this_usbduxfastsub->sem);
+ udfs->ai_cmd_running = 0;
+ /* fixme: unlink here?? */
+ up(&udfs->sem);
return ret;
}
s->async->inttrig = NULL;
} else {
- /* TRIG_INT */
- // don't enable the acquision operation
- // wait for an internal signal
+ /*
+ * TRIG_INT
+ * don't enable the acquision operation
+ * wait for an internal signal
+ */
s->async->inttrig = usbduxfast_ai_inttrig;
}
- up(&this_usbduxfastsub->sem);
+ up(&udfs->sem);
return 0;
}
-/* Mode 0 is used to get a single conversion on demand */
-static int usbduxfast_ai_insn_read(comedi_device * dev,
- comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+/*
+ * Mode 0 is used to get a single conversion on demand.
+ */
+static int usbduxfast_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
{
int i, j, n, actual_length;
int chan, range, rngmask;
int err;
- usbduxfastsub_t *usbduxfastsub = dev->private;
+ struct usbduxfastsub_s *udfs;
- if (!usbduxfastsub) {
- printk("comedi%d: ai_insn_read: no usb dev.\n", dev->minor);
+ udfs = dev->private;
+ if (!udfs) {
+ printk(KERN_ERR "comedi%d: ai_insn_read: no usb dev.\n",
+ dev->minor);
return -ENODEV;
}
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi%d: ai_insn_read, insn->n=%d, insn->subdev=%d\n",
- dev->minor, insn->n, insn->subdev);
+ printk(KERN_DEBUG "comedi%d: ai_insn_read, insn->n=%d, "
+ "insn->subdev=%d\n", dev->minor, insn->n, insn->subdev);
#endif
- down(&usbduxfastsub->sem);
- if (!(usbduxfastsub->probed)) {
- up(&usbduxfastsub->sem);
+ down(&udfs->sem);
+ if (!udfs->probed) {
+ up(&udfs->sem);
return -ENODEV;
}
- if (usbduxfastsub->ai_cmd_running) {
- printk("comedi%d: ai_insn_read not possible. Async Command is running.\n", dev->minor);
- up(&usbduxfastsub->sem);
+ if (udfs->ai_cmd_running) {
+ printk(KERN_ERR "comedi%d: ai_insn_read not possible. Async "
+ "Command is running.\n", dev->minor);
+ up(&udfs->sem);
return -EBUSY;
}
- // sample one channel
+ /* sample one channel */
chan = CR_CHAN(insn->chanspec);
range = CR_RANGE(insn->chanspec);
- // set command for the first channel
+ /* set command for the first channel */
if (range > 0)
rngmask = 0xff - 0x04;
else
rngmask = 0xff;
- // commit data to the FIFO
- usbduxfastsub->dux_commands[LENBASE + 0] = 1;
- usbduxfastsub->dux_commands[OPBASE + 0] = 0x02; // data
- usbduxfastsub->dux_commands[OUTBASE + 0] = 0xFF & rngmask;
- usbduxfastsub->dux_commands[LOGBASE + 0] = 0;
-
- // do the first part of the delay
- usbduxfastsub->dux_commands[LENBASE + 1] = 12;
- usbduxfastsub->dux_commands[OPBASE + 1] = 0;
- usbduxfastsub->dux_commands[OUTBASE + 1] = 0xFE & rngmask;
- usbduxfastsub->dux_commands[LOGBASE + 1] = 0;
-
- usbduxfastsub->dux_commands[LENBASE + 2] = 1;
- usbduxfastsub->dux_commands[OPBASE + 2] = 0;
- usbduxfastsub->dux_commands[OUTBASE + 2] = 0xFE & rngmask;
- usbduxfastsub->dux_commands[LOGBASE + 2] = 0;
-
- usbduxfastsub->dux_commands[LENBASE + 3] = 1;
- usbduxfastsub->dux_commands[OPBASE + 3] = 0;
- usbduxfastsub->dux_commands[OUTBASE + 3] = 0xFE & rngmask;
- usbduxfastsub->dux_commands[LOGBASE + 3] = 0;
-
- usbduxfastsub->dux_commands[LENBASE + 4] = 1;
- usbduxfastsub->dux_commands[OPBASE + 4] = 0;
- usbduxfastsub->dux_commands[OUTBASE + 4] = 0xFE & rngmask;
- usbduxfastsub->dux_commands[LOGBASE + 4] = 0;
-
- // second part
- usbduxfastsub->dux_commands[LENBASE + 5] = 12;
- usbduxfastsub->dux_commands[OPBASE + 5] = 0;
- usbduxfastsub->dux_commands[OUTBASE + 5] = 0xFF & rngmask;
- usbduxfastsub->dux_commands[LOGBASE + 5] = 0;
-
- usbduxfastsub->dux_commands[LENBASE + 6] = 1;
- usbduxfastsub->dux_commands[OPBASE + 6] = 0;
- usbduxfastsub->dux_commands[OUTBASE + 6] = 0xFF & rngmask;
- usbduxfastsub->dux_commands[LOGBASE + 0] = 0;
+
+ /* commit data to the FIFO */
+ udfs->dux_commands[LENBASE+0] = 1;
+ /* data */
+ udfs->dux_commands[OPBASE+0] = 0x02;
+ udfs->dux_commands[OUTBASE+0] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+0] = 0;
+
+ /* do the first part of the delay */
+ udfs->dux_commands[LENBASE+1] = 12;
+ udfs->dux_commands[OPBASE+1] = 0;
+ udfs->dux_commands[OUTBASE+1] = 0xFE & rngmask;
+ udfs->dux_commands[LOGBASE+1] = 0;
+
+ udfs->dux_commands[LENBASE+2] = 1;
+ udfs->dux_commands[OPBASE+2] = 0;
+ udfs->dux_commands[OUTBASE+2] = 0xFE & rngmask;
+ udfs->dux_commands[LOGBASE+2] = 0;
+
+ udfs->dux_commands[LENBASE+3] = 1;
+ udfs->dux_commands[OPBASE+3] = 0;
+ udfs->dux_commands[OUTBASE+3] = 0xFE & rngmask;
+ udfs->dux_commands[LOGBASE+3] = 0;
+
+ udfs->dux_commands[LENBASE+4] = 1;
+ udfs->dux_commands[OPBASE+4] = 0;
+ udfs->dux_commands[OUTBASE+4] = 0xFE & rngmask;
+ udfs->dux_commands[LOGBASE+4] = 0;
+
+ /* second part */
+ udfs->dux_commands[LENBASE+5] = 12;
+ udfs->dux_commands[OPBASE+5] = 0;
+ udfs->dux_commands[OUTBASE+5] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+5] = 0;
+
+ udfs->dux_commands[LENBASE+6] = 1;
+ udfs->dux_commands[OPBASE+6] = 0;
+ udfs->dux_commands[OUTBASE+6] = 0xFF & rngmask;
+ udfs->dux_commands[LOGBASE+0] = 0;
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi %d: sending commands to the usb device\n", dev->minor);
+ printk(KERN_DEBUG "comedi %d: sending commands to the usb device\n",
+ dev->minor);
#endif
- // 0 means that the AD commands are sent
- err = send_dux_commands(usbduxfastsub, SENDADCOMMANDS);
+ /* 0 means that the AD commands are sent */
+ err = send_dux_commands(udfs, SENDADCOMMANDS);
if (err < 0) {
- printk("comedi%d: adc command could not be submitted. Aborting...\n", dev->minor);
- up(&usbduxfastsub->sem);
+ printk(KERN_ERR "comedi%d: adc command could not be submitted."
+ "Aborting...\n", dev->minor);
+ up(&udfs->sem);
return err;
}
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi%d: usbduxfast: submitting in-urb: %x,%x\n",
- usbduxfastsub->comedidev->minor,
- (int)(usbduxfastsub->urbIn->context),
- (int)(usbduxfastsub->urbIn->dev));
+ printk(KERN_DEBUG "comedi%d: usbduxfast: submitting in-urb: "
+ "0x%p,0x%p\n", udfs->comedidev->minor, udfs->urbIn->context,
+ udfs->urbIn->dev);
#endif
for (i = 0; i < PACKETS_TO_IGNORE; i++) {
- err = usb_bulk_msg(usbduxfastsub->usbdev,
- usb_rcvbulkpipe(usbduxfastsub->usbdev,
- BULKINEP),
- usbduxfastsub->transfer_buffer, SIZEINBUF,
+ err = usb_bulk_msg(udfs->usbdev,
+ usb_rcvbulkpipe(udfs->usbdev, BULKINEP),
+ udfs->transfer_buffer, SIZEINBUF,
&actual_length, 10000);
if (err < 0) {
- printk("comedi%d: insn timeout. No data.\n",
+ printk(KERN_ERR "comedi%d: insn timeout. No data.\n",
dev->minor);
- up(&usbduxfastsub->sem);
+ up(&udfs->sem);
return err;
}
}
- // data points
+ /* data points */
for (i = 0; i < insn->n;) {
- err = usb_bulk_msg(usbduxfastsub->usbdev,
- usb_rcvbulkpipe(usbduxfastsub->usbdev,
- BULKINEP),
- usbduxfastsub->transfer_buffer, SIZEINBUF,
+ err = usb_bulk_msg(udfs->usbdev,
+ usb_rcvbulkpipe(udfs->usbdev, BULKINEP),
+ udfs->transfer_buffer, SIZEINBUF,
&actual_length, 10000);
if (err < 0) {
- printk("comedi%d: insn data error: %d\n",
+ printk(KERN_ERR "comedi%d: insn data error: %d\n",
dev->minor, err);
- up(&usbduxfastsub->sem);
+ up(&udfs->sem);
return err;
}
n = actual_length / sizeof(uint16_t);
if ((n % 16) != 0) {
- printk("comedi%d: insn data packet corrupted.\n",
- dev->minor);
- up(&usbduxfastsub->sem);
+ printk(KERN_ERR "comedi%d: insn data packet "
+ "corrupted.\n", dev->minor);
+ up(&udfs->sem);
return -EINVAL;
}
for (j = chan; (j < n) && (i < insn->n); j = j + 16) {
- data[i] =
- ((uint16_t *) (usbduxfastsub->
- transfer_buffer))[j];
+ data[i] = ((uint16_t *) (udfs->transfer_buffer))[j];
i++;
}
}
- up(&usbduxfastsub->sem);
+ up(&udfs->sem);
return i;
}
static unsigned hex2unsigned(char *h)
{
unsigned hi, lo;
- if (h[0] > '9') {
+
+ if (h[0] > '9')
hi = h[0] - 'A' + 0x0a;
- } else {
+ else
hi = h[0] - '0';
- }
- if (h[1] > '9') {
+
+ if (h[1] > '9')
lo = h[1] - 'A' + 0x0a;
- } else {
+ else
lo = h[1] - '0';
- }
+
return hi * 0x10 + lo;
}
-// for FX2
+/* for FX2 */
#define FIRMWARE_MAX_LEN 0x2000
-// taken from David Brownell's fxload and adjusted for this driver
-static int read_firmware(usbduxfastsub_t * usbduxfastsub, void *firmwarePtr,
- long size)
+/*
+ * taken from David Brownell's fxload and adjusted for this driver
+ */
+static int read_firmware(struct usbduxfastsub_s *udfs, const void *firmwarePtr,
+ long size)
{
int i = 0;
unsigned char *fp = (char *)firmwarePtr;
- unsigned char *firmwareBinary = NULL;
+ unsigned char *firmwareBinary;
int res = 0;
int maxAddr = 0;
firmwareBinary = kmalloc(FIRMWARE_MAX_LEN, GFP_KERNEL);
if (!firmwareBinary) {
- printk("comedi_: usbduxfast: mem alloc for firmware failed\n");
+ printk(KERN_ERR "comedi_: usbduxfast: mem alloc for firmware "
+ " failed\n");
return -ENOMEM;
}
@@ -1315,31 +1411,37 @@ static int read_firmware(usbduxfastsub_t * usbduxfastsub, void *firmwarePtr,
int idx, off;
int j = 0;
- // get one line
+ /* get one line */
while ((i < size) && (fp[i] != 13) && (fp[i] != 10)) {
buf[j] = fp[i];
i++;
j++;
if (j >= sizeof(buf)) {
- printk("comedi_: usbduxfast: bogus firmware file!\n");
+ printk(KERN_ERR "comedi_: usbduxfast: bogus "
+ "firmware file!\n");
+ kfree(firmwareBinary);
return -1;
}
}
- // get rid of LF/CR/...
+ /* get rid of LF/CR/... */
while ((i < size) && ((fp[i] == 13) || (fp[i] == 10)
- || (fp[i] == 0))) {
+ || (fp[i] == 0)))
i++;
- }
buf[j] = 0;
- //printk("comedi_: buf=%s\n",buf);
+ /* printk("comedi_: buf=%s\n",buf); */
- /* EXTENSION: "# comment-till-end-of-line", for copyrights etc */
+ /*
+ * EXTENSION: "# comment-till-end-of-line",
+ * for copyrights etc
+ */
if (buf[0] == '#')
continue;
if (buf[0] != ':') {
- printk("comedi_: usbduxfast: upload: not an ihex record: %s", buf);
+ printk(KERN_ERR "comedi_: usbduxfast: upload: not an "
+ "ihex record: %s", buf);
+ kfree(firmwareBinary);
return -EFAULT;
}
@@ -1349,266 +1451,309 @@ static int read_firmware(usbduxfastsub_t * usbduxfastsub, void *firmwarePtr,
/* Read the target offset */
off = (hex2unsigned(buf + 3) * 0x0100) + hex2unsigned(buf + 5);
- if ((off + len) > maxAddr) {
+ if ((off + len) > maxAddr)
maxAddr = off + len;
- }
if (maxAddr >= FIRMWARE_MAX_LEN) {
- printk("comedi_: usbduxfast: firmware upload goes beyond FX2 RAM boundaries.");
+ printk(KERN_ERR "comedi_: usbduxfast: firmware upload "
+ "goes beyond FX2 RAM boundaries.");
+ kfree(firmwareBinary);
return -EFAULT;
}
- //printk("comedi_: usbduxfast: off=%x, len=%x:",off,len);
+ /* printk("comedi_: usbduxfast: off=%x, len=%x:",off,len); */
/* Read the record type */
type = hex2unsigned(buf + 7);
/* If this is an EOF record, then make it so. */
- if (type == 1) {
+ if (type == 1)
break;
- }
if (type != 0) {
- printk("comedi_: usbduxfast: unsupported record type: %u\n", type);
+ printk(KERN_ERR "comedi_: usbduxfast: unsupported "
+ "record type: %u\n", type);
+ kfree(firmwareBinary);
return -EFAULT;
}
for (idx = 0, cp = buf + 9; idx < len; idx += 1, cp += 2) {
firmwareBinary[idx + off] = hex2unsigned(cp);
- //printk("%02x ",firmwareBinary[idx+off]);
+ /* printk("%02x ",firmwareBinary[idx+off]); */
}
- //printk("\n");
+
+ /* printk("\n"); */
if (i >= size) {
- printk("comedi_: usbduxfast: unexpected end of hex file\n");
+ printk(KERN_ERR "comedi_: usbduxfast: unexpected end "
+ "of hex file\n");
break;
}
}
- res = firmwareUpload(usbduxfastsub, firmwareBinary, maxAddr + 1);
+ res = firmwareUpload(udfs, firmwareBinary, maxAddr + 1);
kfree(firmwareBinary);
return res;
}
-static void tidy_up(usbduxfastsub_t * usbduxfastsub_tmp)
+static void tidy_up(struct usbduxfastsub_s *udfs)
{
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi_: usbduxfast: tiding up\n");
+ printk(KERN_DEBUG "comedi_: usbduxfast: tiding up\n");
#endif
- if (!usbduxfastsub_tmp) {
+
+ if (!udfs)
return;
- }
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
- // shows the usb subsystem that the driver is down
- if (usbduxfastsub_tmp->interface) {
- usb_set_intfdata(usbduxfastsub_tmp->interface, NULL);
- }
-#endif
- usbduxfastsub_tmp->probed = 0;
+ /* shows the usb subsystem that the driver is down */
+ if (udfs->interface)
+ usb_set_intfdata(udfs->interface, NULL);
- if (usbduxfastsub_tmp->urbIn) {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,8)
- // waits until a running transfer is over
- // thus, under 2.4 hotplugging while a command
- // is running is not safe
- usb_kill_urb(usbduxfastsub_tmp->urbIn);
-#endif
- if (usbduxfastsub_tmp->transfer_buffer) {
- kfree(usbduxfastsub_tmp->transfer_buffer);
- usbduxfastsub_tmp->transfer_buffer = NULL;
- }
- usb_free_urb(usbduxfastsub_tmp->urbIn);
- usbduxfastsub_tmp->urbIn = NULL;
- }
- if (usbduxfastsub_tmp->insnBuffer) {
- kfree(usbduxfastsub_tmp->insnBuffer);
- usbduxfastsub_tmp->insnBuffer = NULL;
- }
- if (usbduxfastsub_tmp->dux_commands) {
- kfree(usbduxfastsub_tmp->dux_commands);
- usbduxfastsub_tmp->dux_commands = NULL;
+ udfs->probed = 0;
+
+ if (udfs->urbIn) {
+ /* waits until a running transfer is over */
+ usb_kill_urb(udfs->urbIn);
+
+ kfree(udfs->transfer_buffer);
+ udfs->transfer_buffer = NULL;
+
+ usb_free_urb(udfs->urbIn);
+ udfs->urbIn = NULL;
}
- usbduxfastsub_tmp->ai_cmd_running = 0;
+
+ kfree(udfs->insnBuffer);
+ udfs->insnBuffer = NULL;
+
+ kfree(udfs->dux_commands);
+ udfs->dux_commands = NULL;
+
+ udfs->ai_cmd_running = 0;
}
-// allocate memory for the urbs and initialise them
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
-static void *usbduxfastsub_probe(struct usb_device *udev,
- unsigned int interfnum, const struct usb_device_id *id)
+static void usbduxfast_firmware_request_complete_handler(const struct firmware *fw,
+ void *context)
{
-#else
+ struct usbduxfastsub_s *usbduxfastsub_tmp = context;
+ struct usb_device *usbdev = usbduxfastsub_tmp->usbdev;
+ int ret;
+
+ if (fw == NULL)
+ return;
+
+ /*
+ * we need to upload the firmware here because fw will be
+ * freed once we've left this function
+ */
+ ret = read_firmware(usbduxfastsub_tmp, fw->data, fw->size);
+
+ if (ret) {
+ dev_err(&usbdev->dev,
+ "Could not upload firmware (err=%d)\n",
+ ret);
+ return;
+ }
+
+ comedi_usb_auto_config(usbdev, BOARDNAME);
+}
+
+/*
+ * allocate memory for the urbs and initialise them
+ */
static int usbduxfastsub_probe(struct usb_interface *uinterf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(uinterf);
-#endif
int i;
int index;
+ int ret;
if (udev->speed != USB_SPEED_HIGH) {
- printk("comedi_: usbduxfast_: This driver needs USB 2.0 to operate. Aborting...\n");
+ printk(KERN_ERR "comedi_: usbduxfast_: This driver needs"
+ "USB 2.0 to operate. Aborting...\n");
return -ENODEV;
}
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi_: usbduxfast_: finding a free structure for the usb-device\n");
+ printk(KERN_DEBUG "comedi_: usbduxfast_: finding a free structure for "
+ "the usb-device\n");
#endif
down(&start_stop_sem);
- // look for a free place in the usbduxfast array
+ /* look for a free place in the usbduxfast array */
index = -1;
for (i = 0; i < NUMUSBDUXFAST; i++) {
- if (!(usbduxfastsub[i].probed)) {
+ if (!usbduxfastsub[i].probed) {
index = i;
break;
}
}
- // no more space
+ /* no more space */
if (index == -1) {
- printk("Too many usbduxfast-devices connected.\n");
+ printk(KERN_ERR "Too many usbduxfast-devices connected.\n");
up(&start_stop_sem);
return -EMFILE;
}
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi_: usbduxfast: usbduxfastsub[%d] is ready to connect to comedi.\n", index);
+ printk(KERN_DEBUG "comedi_: usbduxfast: usbduxfastsub[%d] is ready to "
+ "connect to comedi.\n", index);
#endif
init_MUTEX(&(usbduxfastsub[index].sem));
- // save a pointer to the usb device
+ /* save a pointer to the usb device */
usbduxfastsub[index].usbdev = udev;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
- // save the interface number
- usbduxfastsub[index].ifnum = interfnum;
-#else
- // 2.6: save the interface itself
+ /* save the interface itself */
usbduxfastsub[index].interface = uinterf;
- // get the interface number from the interface
+ /* get the interface number from the interface */
usbduxfastsub[index].ifnum = uinterf->altsetting->desc.bInterfaceNumber;
- // hand the private data over to the usb subsystem
- // will be needed for disconnect
+ /*
+ * hand the private data over to the usb subsystem
+ * will be needed for disconnect
+ */
usb_set_intfdata(uinterf, &(usbduxfastsub[index]));
-#endif
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi_: usbduxfast: ifnum=%d\n", usbduxfastsub[index].ifnum);
+ printk(KERN_DEBUG "comedi_: usbduxfast: ifnum=%d\n",
+ usbduxfastsub[index].ifnum);
#endif
- // create space for the commands going to the usb device
+ /* create space for the commands going to the usb device */
usbduxfastsub[index].dux_commands = kmalloc(SIZEOFDUXBUFFER,
- GFP_KERNEL);
+ GFP_KERNEL);
if (!usbduxfastsub[index].dux_commands) {
- printk("comedi_: usbduxfast: error alloc space for dac commands\n");
+ printk(KERN_ERR "comedi_: usbduxfast: error alloc space for "
+ "dac commands\n");
tidy_up(&(usbduxfastsub[index]));
up(&start_stop_sem);
return -ENOMEM;
}
- // create space of the instruction buffer
+ /* create space of the instruction buffer */
usbduxfastsub[index].insnBuffer = kmalloc(SIZEINSNBUF, GFP_KERNEL);
- if (!(usbduxfastsub[index].insnBuffer)) {
- printk("comedi_: usbduxfast: could not alloc space for insnBuffer\n");
+ if (!usbduxfastsub[index].insnBuffer) {
+ printk(KERN_ERR "comedi_: usbduxfast: could not alloc space "
+ "for insnBuffer\n");
tidy_up(&(usbduxfastsub[index]));
up(&start_stop_sem);
return -ENOMEM;
}
- // setting to alternate setting 1: enabling bulk ep
+ /* setting to alternate setting 1: enabling bulk ep */
i = usb_set_interface(usbduxfastsub[index].usbdev,
usbduxfastsub[index].ifnum, 1);
if (i < 0) {
- printk("comedi_: usbduxfast%d: could not switch to alternate setting 1.\n", index);
+ printk(KERN_ERR "comedi_: usbduxfast%d: could not switch to "
+ "alternate setting 1.\n", index);
tidy_up(&(usbduxfastsub[index]));
up(&start_stop_sem);
return -ENODEV;
}
usbduxfastsub[index].urbIn = usb_alloc_urb(0, GFP_KERNEL);
- if (usbduxfastsub[index].urbIn == NULL) {
- printk("comedi_: usbduxfast%d: Could not alloc. urb\n", index);
+ if (!usbduxfastsub[index].urbIn) {
+ printk(KERN_ERR "comedi_: usbduxfast%d: Could not alloc."
+ "urb\n", index);
tidy_up(&(usbduxfastsub[index]));
up(&start_stop_sem);
return -ENOMEM;
}
usbduxfastsub[index].transfer_buffer = kmalloc(SIZEINBUF, GFP_KERNEL);
- if (!(usbduxfastsub[index].transfer_buffer)) {
- printk("comedi_: usbduxfast%d: could not alloc. transb.\n",
- index);
+ if (!usbduxfastsub[index].transfer_buffer) {
+ printk(KERN_ERR "comedi_: usbduxfast%d: could not alloc. "
+ "transb.\n", index);
tidy_up(&(usbduxfastsub[index]));
up(&start_stop_sem);
return -ENOMEM;
}
- // we've reached the bottom of the function
+ /* we've reached the bottom of the function */
usbduxfastsub[index].probed = 1;
up(&start_stop_sem);
- printk("comedi_: usbduxfast%d has been successfully initialized.\n",
- index);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
- return (void *)(&usbduxfastsub[index]);
-#else
- // success
+
+ ret = request_firmware_nowait(THIS_MODULE,
+ FW_ACTION_HOTPLUG,
+ "usbduxfast_firmware.hex",
+ &udev->dev,
+ usbduxfastsub + index,
+ usbduxfast_firmware_request_complete_handler);
+
+ if (ret) {
+ dev_err(&udev->dev, "could not load firmware (err=%d)\n",
+ ret);
+ return ret;
+ }
+
+ printk(KERN_INFO "comedi_: usbduxfast%d has been successfully "
+ "initialized.\n", index);
+ /* success */
return 0;
-#endif
}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
-static void usbduxfastsub_disconnect(struct usb_device *udev, void *ptr)
-{
- usbduxfastsub_t *usbduxfastsub_tmp = (usbduxfastsub_t *) ptr;
-#else
static void usbduxfastsub_disconnect(struct usb_interface *intf)
{
- usbduxfastsub_t *usbduxfastsub_tmp = usb_get_intfdata(intf);
+ struct usbduxfastsub_s *udfs = usb_get_intfdata(intf);
struct usb_device *udev = interface_to_usbdev(intf);
-#endif
- if (!usbduxfastsub_tmp) {
- printk("comedi_: usbduxfast: disconnect called with null pointer.\n");
+
+ if (!udfs) {
+ printk(KERN_ERR "comedi_: usbduxfast: disconnect called with "
+ "null pointer.\n");
return;
}
- if (usbduxfastsub_tmp->usbdev != udev) {
- printk("comedi_: usbduxfast: BUG! called with wrong ptr!!!\n");
+ if (udfs->usbdev != udev) {
+ printk(KERN_ERR "comedi_: usbduxfast: BUG! called with wrong "
+ "ptr!!!\n");
return;
}
+
+ comedi_usb_auto_unconfig(udev);
+
down(&start_stop_sem);
- down(&usbduxfastsub_tmp->sem);
- tidy_up(usbduxfastsub_tmp);
- up(&usbduxfastsub_tmp->sem);
+ down(&udfs->sem);
+ tidy_up(udfs);
+ up(&udfs->sem);
up(&start_stop_sem);
+
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi_: usbduxfast: disconnected from the usb\n");
+ printk(KERN_DEBUG "comedi_: usbduxfast: disconnected from the usb\n");
#endif
}
-// is called when comedi-config is called
-static int usbduxfast_attach(comedi_device * dev, comedi_devconfig * it)
+/*
+ * is called when comedi-config is called
+ */
+static int usbduxfast_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
int ret;
int index;
int i;
- comedi_subdevice *s = NULL;
+ struct comedi_subdevice *s = NULL;
dev->private = NULL;
down(&start_stop_sem);
- // find a valid device which has been detected by the probe function of the usb
+ /*
+ * find a valid device which has been detected by the
+ * probe function of the usb
+ */
index = -1;
for (i = 0; i < NUMUSBDUXFAST; i++) {
- if ((usbduxfastsub[i].probed) && (!usbduxfastsub[i].attached)) {
+ if (usbduxfastsub[i].probed && !usbduxfastsub[i].attached) {
index = i;
break;
}
}
if (index < 0) {
- printk("comedi%d: usbduxfast: error: attach failed, no usbduxfast devs connected to the usb bus.\n", dev->minor);
+ printk(KERN_ERR "comedi%d: usbduxfast: error: attach failed, "
+ "no usbduxfast devs connected to the usb bus.\n",
+ dev->minor);
up(&start_stop_sem);
return -ENODEV;
}
down(&(usbduxfastsub[index].sem));
- // pointer back to the corresponding comedi device
+ /* pointer back to the corresponding comedi device */
usbduxfastsub[index].comedidev = dev;
- // trying to upload the firmware into the chip
+ /* trying to upload the firmware into the chip */
if (comedi_aux_data(it->options, 0) &&
- it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
- read_firmware(usbduxfastsub,
- comedi_aux_data(it->options, 0),
- it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]);
+ it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
+ read_firmware(&usbduxfastsub[index],
+ comedi_aux_data(it->options, 0),
+ it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH]);
}
dev->board_name = BOARDNAME;
@@ -1616,155 +1761,150 @@ static int usbduxfast_attach(comedi_device * dev, comedi_devconfig * it)
/* set number of subdevices */
dev->n_subdevices = N_SUBDEVICES;
- // allocate space for the subdevices
- if ((ret = alloc_subdevices(dev, N_SUBDEVICES)) < 0) {
- printk("comedi%d: usbduxfast: error alloc space for subdev\n",
- dev->minor);
+ /* allocate space for the subdevices */
+ ret = alloc_subdevices(dev, N_SUBDEVICES);
+ if (ret < 0) {
+ printk(KERN_ERR "comedi%d: usbduxfast: error alloc space for "
+ "subdev\n", dev->minor);
+ up(&(usbduxfastsub[index].sem));
up(&start_stop_sem);
return ret;
}
- printk("comedi%d: usbduxfast: usb-device %d is attached to comedi.\n",
- dev->minor, index);
- // private structure is also simply the usb-structure
+ printk(KERN_INFO "comedi%d: usbduxfast: usb-device %d is attached to "
+ "comedi.\n", dev->minor, index);
+ /* private structure is also simply the usb-structure */
dev->private = usbduxfastsub + index;
- // the first subdevice is the A/D converter
+ /* the first subdevice is the A/D converter */
s = dev->subdevices + SUBDEV_AD;
- // the URBs get the comedi subdevice
- // which is responsible for reading
- // this is the subdevice which reads data
+ /*
+ * the URBs get the comedi subdevice which is responsible for reading
+ * this is the subdevice which reads data
+ */
dev->read_subdev = s;
- // the subdevice receives as private structure the
- // usb-structure
+ /* the subdevice receives as private structure the usb-structure */
s->private = NULL;
- // analog input
+ /* analog input */
s->type = COMEDI_SUBD_AI;
- // readable and ref is to ground
+ /* readable and ref is to ground */
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
- // 16 channels
+ /* 16 channels */
s->n_chan = 16;
- // length of the channellist
+ /* length of the channellist */
s->len_chanlist = 16;
- // callback functions
+ /* callback functions */
s->insn_read = usbduxfast_ai_insn_read;
s->do_cmdtest = usbduxfast_ai_cmdtest;
s->do_cmd = usbduxfast_ai_cmd;
s->cancel = usbduxfast_ai_cancel;
- // max value from the A/D converter (12bit+1 bit for overflow)
+ /* max value from the A/D converter (12bit+1 bit for overflow) */
s->maxdata = 0x1000;
- // range table to convert to physical units
+ /* range table to convert to physical units */
s->range_table = &range_usbduxfast_ai_range;
- // finally decide that it's attached
+ /* finally decide that it's attached */
usbduxfastsub[index].attached = 1;
up(&(usbduxfastsub[index].sem));
-
up(&start_stop_sem);
-
- printk("comedi%d: successfully attached to usbduxfast.\n", dev->minor);
+ printk(KERN_INFO "comedi%d: successfully attached to usbduxfast.\n",
+ dev->minor);
return 0;
}
-static int usbduxfast_detach(comedi_device * dev)
+static int usbduxfast_detach(struct comedi_device *dev)
{
- usbduxfastsub_t *usbduxfastsub_tmp;
-
-#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi%d: usbduxfast: detach usb device\n", dev->minor);
-#endif
+ struct usbduxfastsub_s *udfs;
if (!dev) {
- printk("comedi?: usbduxfast: detach without dev variable...\n");
+ printk(KERN_ERR "comedi?: usbduxfast: detach without dev "
+ "variable...\n");
return -EFAULT;
}
- usbduxfastsub_tmp = dev->private;
- if (!usbduxfastsub_tmp) {
- printk("comedi?: usbduxfast: detach without ptr to usbduxfastsub[]\n");
+#ifdef CONFIG_COMEDI_DEBUG
+ printk(KERN_DEBUG "comedi%d: usbduxfast: detach usb device\n",
+ dev->minor);
+#endif
+
+ udfs = dev->private;
+ if (!udfs) {
+ printk(KERN_ERR "comedi?: usbduxfast: detach without ptr to "
+ "usbduxfastsub[]\n");
return -EFAULT;
}
- down(&usbduxfastsub_tmp->sem);
+ down(&udfs->sem);
down(&start_stop_sem);
- // Don't allow detach to free the private structure
- // It's one entry of of usbduxfastsub[]
+ /*
+ * Don't allow detach to free the private structure
+ * It's one entry of of usbduxfastsub[]
+ */
dev->private = NULL;
- usbduxfastsub_tmp->attached = 0;
- usbduxfastsub_tmp->comedidev = NULL;
+ udfs->attached = 0;
+ udfs->comedidev = NULL;
#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi%d: usbduxfast: detach: successfully removed\n",
- dev->minor);
+ printk(KERN_DEBUG "comedi%d: usbduxfast: detach: successfully "
+ "removed\n", dev->minor);
#endif
up(&start_stop_sem);
- up(&usbduxfastsub_tmp->sem);
+ up(&udfs->sem);
return 0;
}
-/* main driver struct */
-static comedi_driver driver_usbduxfast = {
- driver_name:"usbduxfast",
- module:THIS_MODULE,
- attach:usbduxfast_attach,
- detach:usbduxfast_detach,
+/*
+ * main driver struct
+ */
+static struct comedi_driver driver_usbduxfast = {
+ .driver_name = "usbduxfast",
+ .module = THIS_MODULE,
+ .attach = usbduxfast_attach,
+ .detach = usbduxfast_detach
};
-static void init_usb_devices(void)
-{
- int index;
-#ifdef CONFIG_COMEDI_DEBUG
- printk("comedi_: usbduxfast: setting all possible devs to invalid\n");
-#endif
- // all devices entries are invalid to begin with
- // they will become valid by the probe function
- // and then finally by the attach-function
- for (index = 0; index < NUMUSBDUXFAST; index++) {
- memset(&(usbduxfastsub[index]), 0x00,
- sizeof(usbduxfastsub[index]));
- init_MUTEX(&(usbduxfastsub[index].sem));
- }
-}
-
-// Table with the USB-devices: just now only testing IDs
+/*
+ * Table with the USB-devices: just now only testing IDs
+ */
static struct usb_device_id usbduxfastsub_table[] = {
- // { USB_DEVICE(0x4b4, 0x8613), //testing
- // },
- {USB_DEVICE(0x13d8, 0x0010) //real ID
- },
- {USB_DEVICE(0x13d8, 0x0011) //real ID
- },
- {} /* Terminating entry */
+ /* { USB_DEVICE(0x4b4, 0x8613) }, testing */
+ { USB_DEVICE(0x13d8, 0x0010) }, /* real ID */
+ { USB_DEVICE(0x13d8, 0x0011) }, /* real ID */
+ { } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, usbduxfastsub_table);
-// The usbduxfastsub-driver
+/*
+ * The usbduxfastsub-driver
+ */
static struct usb_driver usbduxfastsub_driver = {
#ifdef COMEDI_HAVE_USB_DRIVER_OWNER
- owner:THIS_MODULE,
+ .owner = THIS_MODULE,
#endif
- name:BOARDNAME,
- probe:usbduxfastsub_probe,
- disconnect:usbduxfastsub_disconnect,
- id_table:usbduxfastsub_table,
+ .name = BOARDNAME,
+ .probe = usbduxfastsub_probe,
+ .disconnect = usbduxfastsub_disconnect,
+ .id_table = usbduxfastsub_table
};
-// Can't use the nice macro as I have also to initialise the USB
-// subsystem:
-// registering the usb-system _and_ the comedi-driver
-static int init_usbduxfast(void)
+/*
+ * Can't use the nice macro as I have also to initialise the USB subsystem:
+ * registering the usb-system _and_ the comedi-driver
+ */
+static int __init init_usbduxfast(void)
{
- printk(KERN_INFO KBUILD_MODNAME ": "
- DRIVER_VERSION ":" DRIVER_DESC "\n");
- init_usb_devices();
+ printk(KERN_INFO
+ KBUILD_MODNAME ": " DRIVER_VERSION ":" DRIVER_DESC "\n");
usb_register(&usbduxfastsub_driver);
comedi_driver_register(&driver_usbduxfast);
return 0;
}
-// deregistering the comedi driver and the usb-subsystem
-static void exit_usbduxfast(void)
+/*
+ * deregistering the comedi driver and the usb-subsystem
+ */
+static void __exit exit_usbduxfast(void)
{
comedi_driver_unregister(&driver_usbduxfast);
usb_deregister(&usbduxfastsub_driver);