2 comedi/drivers/daqboard2000.c
3 hardware driver for IOtech DAQboard/2000
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 Description: IOTech DAQBoard/2000
26 Author: Anders Blomdell <anders.blomdell@control.lth.se>
28 Updated: Mon, 14 Apr 2008 15:28:52 +0100
29 Devices: [IOTech] DAQBoard/2000 (daqboard2000)
31 Much of the functionality of this driver was determined from reading
32 the source code for the Windows driver.
34 The FPGA on the board requires initialization code, which can
35 be loaded by comedi_config using the -i
36 option. The initialization code is available from http://www.comedi.org
37 in the comedi_nonfree_firmware tarball.
39 Configuration options:
40 [0] - PCI bus of device (optional)
41 [1] - PCI slot of device (optional)
42 If bus/slot is not specified, the first supported
43 PCI device found will be used.
46 This card was obviously never intended to leave the Windows world,
47 since it lacked all kind of hardware documentation (except for cable
48 pinouts, plug and pray has something to catch up with yet).
50 With some help from our swedish distributor, we got the Windows sourcecode
51 for the card, and here are the findings so far.
53 1. A good document that describes the PCI interface chip is found at:
54 http://plx.plxtech.com/download/9080/databook/9080db-106.pdf
56 2. The initialization done so far is:
57 a. program the FPGA (windows code sans a lot of error messages)
60 3. Analog out seems to work OK with DAC's disabled, if DAC's are enabled,
61 you have to output values to all enabled DAC's until result appears, I
62 guess that it has something to do with pacer clocks, but the source
63 gives me no clues. I'll keep it simple so far.
66 Each channel in the scanlist seems to be controlled by four
70 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
71 ! | | | ! | | | ! | | | ! | | | !
72 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
75 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
76 ! | | | ! | | | ! | | | ! | | | !
77 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
79 +------+------+ | | | | +-- Digital input (??)
80 | | | | +---- 10 us settling time
81 | | | +------ Suspend acquisition (last to scan)
82 | | +-------- Simultaneous sample and hold
83 | +---------- Signed data format
84 +------------------------- Correction offset low
87 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
88 ! | | | ! | | | ! | | | ! | | | !
89 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
91 +-----+ +--+--+ +++ +++ +--+--+
92 | | | | +----- Expansion channel
93 | | | +----------- Expansion gain
94 | | +--------------- Channel (low)
95 | +--------------------- Correction offset high
96 +----------------------------- Correction gain low
98 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
99 ! | | | ! | | | ! | | | ! | | | !
100 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
102 +------+------+ | | +-+-+ | | +-- Low bank enable
103 | | | | | +---- High bank enable
104 | | | | +------ Hi/low select
105 | | | +---------- Gain (1,?,2,4,8,16,32,64)
106 | | +-------------- differential/single ended
107 | +---------------- Unipolar
108 +------------------------- Correction gain high
112 999. The card seems to have an incredible amount of capabilities, but
113 trying to reverse engineer them from the Windows source is beyond my
119 #include "../comedidev.h"
121 #include <linux/delay.h>
123 #include "comedi_pci.h"
126 #define DAQBOARD2000_SUBSYSTEM_IDS2 0x00021616 /* Daqboard/2000 - 2 Dacs */
127 #define DAQBOARD2000_SUBSYSTEM_IDS4 0x00041616 /* Daqboard/2000 - 4 Dacs */
129 #define DAQBOARD2000_DAQ_SIZE 0x1002
130 #define DAQBOARD2000_PLX_SIZE 0x100
132 // Initialization bits for the Serial EEPROM Control Register
133 #define DAQBOARD2000_SECRProgPinHi 0x8001767e
134 #define DAQBOARD2000_SECRProgPinLo 0x8000767e
135 #define DAQBOARD2000_SECRLocalBusHi 0xc000767e
136 #define DAQBOARD2000_SECRLocalBusLo 0x8000767e
137 #define DAQBOARD2000_SECRReloadHi 0xa000767e
138 #define DAQBOARD2000_SECRReloadLo 0x8000767e
141 #define DAQBOARD2000_EEPROM_PRESENT 0x10000000
144 #define DAQBOARD2000_CPLD_INIT 0x0002
145 #define DAQBOARD2000_CPLD_DONE 0x0004
148 static const struct comedi_lrange range_daqboard2000_ai = { 13, {
153 RANGE(-0.625, 0.625),
154 RANGE(-0.3125, 0.3125),
155 RANGE(-0.156, 0.156),
165 static const struct comedi_lrange range_daqboard2000_ao = { 1, {
170 struct daqboard2000_hw {
171 volatile u16 acqControl; // 0x00
172 volatile u16 acqScanListFIFO; // 0x02
173 volatile u32 acqPacerClockDivLow; // 0x04
175 volatile u16 acqScanCounter; // 0x08
176 volatile u16 acqPacerClockDivHigh; // 0x0a
177 volatile u16 acqTriggerCount; // 0x0c
178 volatile u16 fill2; // 0x0e
179 volatile u16 acqResultsFIFO; // 0x10
180 volatile u16 fill3; // 0x12
181 volatile u16 acqResultsShadow; // 0x14
182 volatile u16 fill4; // 0x16
183 volatile u16 acqAdcResult; // 0x18
184 volatile u16 fill5; // 0x1a
185 volatile u16 dacScanCounter; // 0x1c
186 volatile u16 fill6; // 0x1e
188 volatile u16 dacControl; // 0x20
189 volatile u16 fill7; // 0x22
190 volatile s16 dacFIFO; // 0x24
191 volatile u16 fill8[2]; // 0x26
192 volatile u16 dacPacerClockDiv; // 0x2a
193 volatile u16 refDacs; // 0x2c
194 volatile u16 fill9; // 0x2e
196 volatile u16 dioControl; // 0x30
197 volatile s16 dioP3hsioData; // 0x32
198 volatile u16 dioP3Control; // 0x34
199 volatile u16 calEepromControl; // 0x36
200 volatile s16 dacSetting[4]; // 0x38
201 volatile s16 dioP2ExpansionIO8Bit[32]; // 0x40
203 volatile u16 ctrTmrControl; // 0x80
204 volatile u16 fill10[3]; // 0x82
205 volatile s16 ctrInput[4]; // 0x88
206 volatile u16 fill11[8]; // 0x90
207 volatile u16 timerDivisor[2]; // 0xa0
208 volatile u16 fill12[6]; // 0xa4
210 volatile u16 dmaControl; // 0xb0
211 volatile u16 trigControl; // 0xb2
212 volatile u16 fill13[2]; // 0xb4
213 volatile u16 calEeprom; // 0xb8
214 volatile u16 acqDigitalMark; // 0xba
215 volatile u16 trigDacs; // 0xbc
216 volatile u16 fill14; // 0xbe
217 volatile s16 dioP2ExpansionIO16Bit[32]; // 0xc0
220 /* Scan Sequencer programming */
221 #define DAQBOARD2000_SeqStartScanList 0x0011
222 #define DAQBOARD2000_SeqStopScanList 0x0010
224 // Prepare for acquisition
225 #define DAQBOARD2000_AcqResetScanListFifo 0x0004
226 #define DAQBOARD2000_AcqResetResultsFifo 0x0002
227 #define DAQBOARD2000_AcqResetConfigPipe 0x0001
229 // Acqusition status bits
230 #define DAQBOARD2000_AcqResultsFIFOMore1Sample 0x0001
231 #define DAQBOARD2000_AcqResultsFIFOHasValidData 0x0002
232 #define DAQBOARD2000_AcqResultsFIFOOverrun 0x0004
233 #define DAQBOARD2000_AcqLogicScanning 0x0008
234 #define DAQBOARD2000_AcqConfigPipeFull 0x0010
235 #define DAQBOARD2000_AcqScanListFIFOEmpty 0x0020
236 #define DAQBOARD2000_AcqAdcNotReady 0x0040
237 #define DAQBOARD2000_ArbitrationFailure 0x0080
238 #define DAQBOARD2000_AcqPacerOverrun 0x0100
239 #define DAQBOARD2000_DacPacerOverrun 0x0200
240 #define DAQBOARD2000_AcqHardwareError 0x01c0
242 // Scan Sequencer programming
243 #define DAQBOARD2000_SeqStartScanList 0x0011
244 #define DAQBOARD2000_SeqStopScanList 0x0010
246 /* Pacer Clock Control */
247 #define DAQBOARD2000_AdcPacerInternal 0x0030
248 #define DAQBOARD2000_AdcPacerExternal 0x0032
249 #define DAQBOARD2000_AdcPacerEnable 0x0031
250 #define DAQBOARD2000_AdcPacerEnableDacPacer 0x0034
251 #define DAQBOARD2000_AdcPacerDisable 0x0030
252 #define DAQBOARD2000_AdcPacerNormalMode 0x0060
253 #define DAQBOARD2000_AdcPacerCompatibilityMode 0x0061
254 #define DAQBOARD2000_AdcPacerInternalOutEnable 0x0008
255 #define DAQBOARD2000_AdcPacerExternalRising 0x0100
258 #define DAQBOARD2000_DacFull 0x0001
259 #define DAQBOARD2000_RefBusy 0x0002
260 #define DAQBOARD2000_TrgBusy 0x0004
261 #define DAQBOARD2000_CalBusy 0x0008
262 #define DAQBOARD2000_Dac0Busy 0x0010
263 #define DAQBOARD2000_Dac1Busy 0x0020
264 #define DAQBOARD2000_Dac2Busy 0x0040
265 #define DAQBOARD2000_Dac3Busy 0x0080
268 #define DAQBOARD2000_Dac0Enable 0x0021
269 #define DAQBOARD2000_Dac1Enable 0x0031
270 #define DAQBOARD2000_Dac2Enable 0x0041
271 #define DAQBOARD2000_Dac3Enable 0x0051
272 #define DAQBOARD2000_DacEnableBit 0x0001
273 #define DAQBOARD2000_Dac0Disable 0x0020
274 #define DAQBOARD2000_Dac1Disable 0x0030
275 #define DAQBOARD2000_Dac2Disable 0x0040
276 #define DAQBOARD2000_Dac3Disable 0x0050
277 #define DAQBOARD2000_DacResetFifo 0x0004
278 #define DAQBOARD2000_DacPatternDisable 0x0060
279 #define DAQBOARD2000_DacPatternEnable 0x0061
280 #define DAQBOARD2000_DacSelectSignedData 0x0002
281 #define DAQBOARD2000_DacSelectUnsignedData 0x0000
283 /* Trigger Control */
284 #define DAQBOARD2000_TrigAnalog 0x0000
285 #define DAQBOARD2000_TrigTTL 0x0010
286 #define DAQBOARD2000_TrigTransHiLo 0x0004
287 #define DAQBOARD2000_TrigTransLoHi 0x0000
288 #define DAQBOARD2000_TrigAbove 0x0000
289 #define DAQBOARD2000_TrigBelow 0x0004
290 #define DAQBOARD2000_TrigLevelSense 0x0002
291 #define DAQBOARD2000_TrigEdgeSense 0x0000
292 #define DAQBOARD2000_TrigEnable 0x0001
293 #define DAQBOARD2000_TrigDisable 0x0000
295 // Reference Dac Selection
296 #define DAQBOARD2000_PosRefDacSelect 0x0100
297 #define DAQBOARD2000_NegRefDacSelect 0x0000
299 static int daqboard2000_attach(struct comedi_device * dev, struct comedi_devconfig * it);
300 static int daqboard2000_detach(struct comedi_device * dev);
302 static struct comedi_driver driver_daqboard2000 = {
303 driver_name:"daqboard2000",
305 attach:daqboard2000_attach,
306 detach:daqboard2000_detach,
309 struct daq200_boardtype {
313 static const struct daq200_boardtype boardtypes[] = {
314 {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
315 {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
318 #define n_boardtypes (sizeof(boardtypes)/sizeof(struct daq200_boardtype))
319 #define this_board ((const struct daq200_boardtype *)dev->board_ptr)
321 static DEFINE_PCI_DEVICE_TABLE(daqboard2000_pci_table) = {
322 {0x1616, 0x0409, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
326 MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
328 struct daqboard2000_private {
332 struct pci_dev *pci_dev;
336 unsigned int ao_readback[2];
339 #define devpriv ((struct daqboard2000_private *)dev->private)
341 static void writeAcqScanListEntry(struct comedi_device * dev, u16 entry)
343 struct daqboard2000_hw *fpga = devpriv->daq;
346 fpga->acqScanListFIFO = entry & 0x00ff;
348 fpga->acqScanListFIFO = (entry >> 8) & 0x00ff;
351 static void setup_sampling(struct comedi_device * dev, int chan, int gain)
353 u16 word0, word1, word2, word3;
355 /* Channel 0-7 diff, channel 8-23 single ended */
357 word1 = 0x0004; /* Last scan */
358 word2 = (chan << 6) & 0x00c0;
383 dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
384 dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
386 /* These should be read from EEPROM */
389 /* printk("%d %4.4x %4.4x %4.4x %4.4x\n", chan, word0, word1, word2, word3);*/
390 writeAcqScanListEntry(dev, word0);
391 writeAcqScanListEntry(dev, word1);
392 writeAcqScanListEntry(dev, word2);
393 writeAcqScanListEntry(dev, word3);
396 static int daqboard2000_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
397 struct comedi_insn * insn, unsigned int * data)
400 struct daqboard2000_hw *fpga = devpriv->daq;
401 int gain, chan, timeout;
404 DAQBOARD2000_AcqResetScanListFifo |
405 DAQBOARD2000_AcqResetResultsFifo |
406 DAQBOARD2000_AcqResetConfigPipe;
408 /* If pacer clock is not set to some high value (> 10 us), we
409 risk multiple samples to be put into the result FIFO. */
410 fpga->acqPacerClockDivLow = 1000000; /* 1 second, should be long enough */
411 fpga->acqPacerClockDivHigh = 0;
413 gain = CR_RANGE(insn->chanspec);
414 chan = CR_CHAN(insn->chanspec);
416 /* This doesn't look efficient. I decided to take the conservative
417 * approach when I did the insn conversion. Perhaps it would be
418 * better to have broken it completely, then someone would have been
419 * forced to fix it. --ds */
420 for (i = 0; i < insn->n; i++) {
421 setup_sampling(dev, chan, gain);
422 /* Enable reading from the scanlist FIFO */
423 fpga->acqControl = DAQBOARD2000_SeqStartScanList;
424 for (timeout = 0; timeout < 20; timeout++) {
425 if (fpga->acqControl & DAQBOARD2000_AcqConfigPipeFull) {
430 fpga->acqControl = DAQBOARD2000_AdcPacerEnable;
431 for (timeout = 0; timeout < 20; timeout++) {
432 if (fpga->acqControl & DAQBOARD2000_AcqLogicScanning) {
437 for (timeout = 0; timeout < 20; timeout++) {
440 DAQBOARD2000_AcqResultsFIFOHasValidData) {
445 data[i] = fpga->acqResultsFIFO;
446 fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
447 fpga->acqControl = DAQBOARD2000_SeqStopScanList;
453 static int daqboard2000_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
454 struct comedi_insn * insn, unsigned int * data)
457 int chan = CR_CHAN(insn->chanspec);
459 for (i = 0; i < insn->n; i++) {
460 data[i] = devpriv->ao_readback[chan];
466 static int daqboard2000_ao_insn_write(struct comedi_device * dev, struct comedi_subdevice * s,
467 struct comedi_insn * insn, unsigned int * data)
470 int chan = CR_CHAN(insn->chanspec);
471 struct daqboard2000_hw *fpga = devpriv->daq;
474 for (i = 0; i < insn->n; i++) {
476 * OK, since it works OK without enabling the DAC's, let's keep
477 * it as simple as possible...
479 //fpga->dacControl = (chan + 2) * 0x0010 | 0x0001; comedi_udelay(1000);
480 fpga->dacSetting[chan] = data[i];
481 for (timeout = 0; timeout < 20; timeout++) {
482 if ((fpga->dacControl & ((chan + 1) * 0x0010)) == 0) {
487 devpriv->ao_readback[chan] = data[i];
489 * Since we never enabled the DAC's, we don't need to disable it...
490 * fpga->dacControl = (chan + 2) * 0x0010 | 0x0000; comedi_udelay(1000);
497 static void daqboard2000_resetLocalBus(struct comedi_device * dev)
499 printk("daqboard2000_resetLocalBus\n");
500 writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
501 comedi_udelay(10000);
502 writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
503 comedi_udelay(10000);
506 static void daqboard2000_reloadPLX(struct comedi_device * dev)
508 printk("daqboard2000_reloadPLX\n");
509 writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
510 comedi_udelay(10000);
511 writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
512 comedi_udelay(10000);
513 writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
514 comedi_udelay(10000);
517 static void daqboard2000_pulseProgPin(struct comedi_device * dev)
519 printk("daqboard2000_pulseProgPin 1\n");
520 writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
521 comedi_udelay(10000);
522 writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
523 comedi_udelay(10000); /* Not in the original code, but I like symmetry... */
526 static int daqboard2000_pollCPLD(struct comedi_device * dev, int mask)
532 /* timeout after 50 tries -> 5ms */
533 for (i = 0; i < 50; i++) {
534 cpld = readw(devpriv->daq + 0x1000);
535 if ((cpld & mask) == mask) {
545 static int daqboard2000_writeCPLD(struct comedi_device * dev, int data)
550 writew(data, devpriv->daq + 0x1000);
551 if ((readw(devpriv->daq + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
552 DAQBOARD2000_CPLD_INIT) {
558 static int initialize_daqboard2000(struct comedi_device * dev,
559 unsigned char *cpld_array, int len)
562 /* Read the serial EEPROM control register */
567 /* Check to make sure the serial eeprom is present on the board */
568 secr = readl(devpriv->plx + 0x6c);
569 if (!(secr & DAQBOARD2000_EEPROM_PRESENT)) {
571 printk("no serial eeprom\n");
576 for (retry = 0; retry < 3; retry++) {
578 printk("Programming EEPROM try %x\n", retry);
581 daqboard2000_resetLocalBus(dev);
582 daqboard2000_reloadPLX(dev);
583 daqboard2000_pulseProgPin(dev);
584 if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
585 for (i = 0; i < len; i++) {
586 if (cpld_array[i] == 0xff
587 && cpld_array[i + 1] == 0x20) {
589 printk("Preamble found at %d\n", i);
594 for (; i < len; i += 2) {
596 (cpld_array[i] << 8) + cpld_array[i +
598 if (!daqboard2000_writeCPLD(dev, data)) {
604 printk("Programmed\n");
606 daqboard2000_resetLocalBus(dev);
607 daqboard2000_reloadPLX(dev);
616 static void daqboard2000_adcStopDmaTransfer(struct comedi_device * dev)
618 /* printk("Implement: daqboard2000_adcStopDmaTransfer\n");*/
621 static void daqboard2000_adcDisarm(struct comedi_device * dev)
623 struct daqboard2000_hw *fpga = devpriv->daq;
625 /* Disable hardware triggers */
627 fpga->trigControl = DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable;
629 fpga->trigControl = DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable;
631 /* Stop the scan list FIFO from loading the configuration pipe */
633 fpga->acqControl = DAQBOARD2000_SeqStopScanList;
635 /* Stop the pacer clock */
637 fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
639 /* Stop the input dma (abort channel 1) */
640 daqboard2000_adcStopDmaTransfer(dev);
643 static void daqboard2000_activateReferenceDacs(struct comedi_device * dev)
645 struct daqboard2000_hw *fpga = devpriv->daq;
648 // Set the + reference dac value in the FPGA
649 fpga->refDacs = 0x80 | DAQBOARD2000_PosRefDacSelect;
650 for (timeout = 0; timeout < 20; timeout++) {
651 if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0) {
656 /* printk("DAQBOARD2000_PosRefDacSelect %d\n", timeout);*/
658 // Set the - reference dac value in the FPGA
659 fpga->refDacs = 0x80 | DAQBOARD2000_NegRefDacSelect;
660 for (timeout = 0; timeout < 20; timeout++) {
661 if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0) {
666 /* printk("DAQBOARD2000_NegRefDacSelect %d\n", timeout);*/
669 static void daqboard2000_initializeCtrs(struct comedi_device * dev)
671 /* printk("Implement: daqboard2000_initializeCtrs\n");*/
674 static void daqboard2000_initializeTmrs(struct comedi_device * dev)
676 /* printk("Implement: daqboard2000_initializeTmrs\n");*/
679 static void daqboard2000_dacDisarm(struct comedi_device * dev)
681 /* printk("Implement: daqboard2000_dacDisarm\n");*/
684 static void daqboard2000_initializeAdc(struct comedi_device * dev)
686 daqboard2000_adcDisarm(dev);
687 daqboard2000_activateReferenceDacs(dev);
688 daqboard2000_initializeCtrs(dev);
689 daqboard2000_initializeTmrs(dev);
692 static void daqboard2000_initializeDac(struct comedi_device * dev)
694 daqboard2000_dacDisarm(dev);
698 The test command, REMOVE!!:
700 rmmod daqboard2000 ; rmmod comedi; make install ; modprobe daqboard2000; /usr/sbin/comedi_config /dev/comedi0 daqboard/2000 ; tail -40 /var/log/messages
703 static int daqboard2000_8255_cb(int dir, int port, int data,
704 unsigned long ioaddr)
708 writew(data, ((void *)ioaddr) + port * 2);
711 result = readw(((void *)ioaddr) + port * 2);
714 printk("daqboard2000_8255_cb %x %d %d %2.2x -> %2.2x\n",
715 arg, dir, port, data, result);
720 static int daqboard2000_attach(struct comedi_device * dev, struct comedi_devconfig * it)
723 struct comedi_subdevice *s;
724 struct pci_dev *card = NULL;
726 unsigned int aux_len;
729 printk("comedi%d: daqboard2000:", dev->minor);
731 bus = it->options[0];
732 slot = it->options[1];
734 result = alloc_private(dev, sizeof(struct daqboard2000_private));
738 for (card = pci_get_device(0x1616, 0x0409, NULL);
740 card = pci_get_device(0x1616, 0x0409, card)) {
742 /* requested particular bus/slot */
743 if (card->bus->number != bus ||
744 PCI_SLOT(card->devfn) != slot) {
748 break; /* found one */
752 printk(" no daqboard2000 found at bus/slot: %d/%d\n",
755 printk(" no daqboard2000 found\n");
760 devpriv->pci_dev = card;
761 id = ((u32) card->subsystem_device << 16) | card->
763 for (i = 0; i < n_boardtypes; i++) {
764 if (boardtypes[i].id == id) {
765 printk(" %s", boardtypes[i].name);
766 dev->board_ptr = boardtypes + i;
769 if (!dev->board_ptr) {
770 printk(" unknown subsystem id %08x (pretend it is an ids2)", id);
771 dev->board_ptr = boardtypes;
775 if ((result = comedi_pci_enable(card, "daqboard2000")) < 0) {
776 printk(" failed to enable PCI device and request regions\n");
779 devpriv->got_regions = 1;
781 ioremap(pci_resource_start(card, 0), DAQBOARD2000_PLX_SIZE);
783 ioremap(pci_resource_start(card, 2), DAQBOARD2000_DAQ_SIZE);
784 if (!devpriv->plx || !devpriv->daq) {
788 result = alloc_subdevices(dev, 3);
792 readl(devpriv->plx + 0x6c);
796 Windows code does restore interrupts, but since we don't use them...
797 pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
798 printk("Interrupt before is: %x\n", interrupt);
801 aux_data = comedi_aux_data(it->options, 0);
802 aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
804 if (aux_data && aux_len) {
805 result = initialize_daqboard2000(dev, aux_data, aux_len);
807 printk("no FPGA initialization code, aborting\n");
812 daqboard2000_initializeAdc(dev);
813 daqboard2000_initializeDac(dev);
815 Windows code does restore interrupts, but since we don't use them...
816 pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
817 printk("Interrupt after is: %x\n", interrupt);
820 dev->iobase = (unsigned long)devpriv->daq;
822 dev->board_name = this_board->name;
824 s = dev->subdevices + 0;
826 s->type = COMEDI_SUBD_AI;
827 s->subdev_flags = SDF_READABLE | SDF_GROUND;
830 s->insn_read = daqboard2000_ai_insn_read;
831 s->range_table = &range_daqboard2000_ai;
833 s = dev->subdevices + 1;
835 s->type = COMEDI_SUBD_AO;
836 s->subdev_flags = SDF_WRITABLE;
839 s->insn_read = daqboard2000_ao_insn_read;
840 s->insn_write = daqboard2000_ao_insn_write;
841 s->range_table = &range_daqboard2000_ao;
843 s = dev->subdevices + 2;
844 result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
845 (unsigned long)(dev->iobase + 0x40));
852 static int daqboard2000_detach(struct comedi_device * dev)
854 printk("comedi%d: daqboard2000: remove\n", dev->minor);
857 subdev_8255_cleanup(dev, dev->subdevices + 2);
860 free_irq(dev->irq, dev);
864 iounmap(devpriv->daq);
866 iounmap(devpriv->plx);
867 if (devpriv->pci_dev) {
868 if (devpriv->got_regions) {
869 comedi_pci_disable(devpriv->pci_dev);
871 pci_dev_put(devpriv->pci_dev);
877 COMEDI_PCI_INITCLEANUP(driver_daqboard2000, daqboard2000_pci_table);