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>
122 #include <linux/interrupt.h>
124 #include "comedi_pci.h"
127 #define DAQBOARD2000_SUBSYSTEM_IDS2 0x00021616 /* Daqboard/2000 - 2 Dacs */
128 #define DAQBOARD2000_SUBSYSTEM_IDS4 0x00041616 /* Daqboard/2000 - 4 Dacs */
130 #define DAQBOARD2000_DAQ_SIZE 0x1002
131 #define DAQBOARD2000_PLX_SIZE 0x100
133 /* Initialization bits for the Serial EEPROM Control Register */
134 #define DAQBOARD2000_SECRProgPinHi 0x8001767e
135 #define DAQBOARD2000_SECRProgPinLo 0x8000767e
136 #define DAQBOARD2000_SECRLocalBusHi 0xc000767e
137 #define DAQBOARD2000_SECRLocalBusLo 0x8000767e
138 #define DAQBOARD2000_SECRReloadHi 0xa000767e
139 #define DAQBOARD2000_SECRReloadLo 0x8000767e
141 /* SECR status bits */
142 #define DAQBOARD2000_EEPROM_PRESENT 0x10000000
144 /* CPLD status bits */
145 #define DAQBOARD2000_CPLD_INIT 0x0002
146 #define DAQBOARD2000_CPLD_DONE 0x0004
148 /* Available ranges */
149 static const struct comedi_lrange range_daqboard2000_ai = { 13, {
154 RANGE(-0.625, 0.625),
155 RANGE(-0.3125, 0.3125),
156 RANGE(-0.156, 0.156),
166 static const struct comedi_lrange range_daqboard2000_ao = { 1, {
171 struct daqboard2000_hw {
172 volatile u16 acqControl; /* 0x00 */
173 volatile u16 acqScanListFIFO; /* 0x02 */
174 volatile u32 acqPacerClockDivLow; /* 0x04 */
176 volatile u16 acqScanCounter; /* 0x08 */
177 volatile u16 acqPacerClockDivHigh; /* 0x0a */
178 volatile u16 acqTriggerCount; /* 0x0c */
179 volatile u16 fill2; /* 0x0e */
180 volatile u16 acqResultsFIFO; /* 0x10 */
181 volatile u16 fill3; /* 0x12 */
182 volatile u16 acqResultsShadow; /* 0x14 */
183 volatile u16 fill4; /* 0x16 */
184 volatile u16 acqAdcResult; /* 0x18 */
185 volatile u16 fill5; /* 0x1a */
186 volatile u16 dacScanCounter; /* 0x1c */
187 volatile u16 fill6; /* 0x1e */
189 volatile u16 dacControl; /* 0x20 */
190 volatile u16 fill7; /* 0x22 */
191 volatile s16 dacFIFO; /* 0x24 */
192 volatile u16 fill8[2]; /* 0x26 */
193 volatile u16 dacPacerClockDiv; /* 0x2a */
194 volatile u16 refDacs; /* 0x2c */
195 volatile u16 fill9; /* 0x2e */
197 volatile u16 dioControl; /* 0x30 */
198 volatile s16 dioP3hsioData; /* 0x32 */
199 volatile u16 dioP3Control; /* 0x34 */
200 volatile u16 calEepromControl; /* 0x36 */
201 volatile s16 dacSetting[4]; /* 0x38 */
202 volatile s16 dioP2ExpansionIO8Bit[32]; /* 0x40 */
204 volatile u16 ctrTmrControl; /* 0x80 */
205 volatile u16 fill10[3]; /* 0x82 */
206 volatile s16 ctrInput[4]; /* 0x88 */
207 volatile u16 fill11[8]; /* 0x90 */
208 volatile u16 timerDivisor[2]; /* 0xa0 */
209 volatile u16 fill12[6]; /* 0xa4 */
211 volatile u16 dmaControl; /* 0xb0 */
212 volatile u16 trigControl; /* 0xb2 */
213 volatile u16 fill13[2]; /* 0xb4 */
214 volatile u16 calEeprom; /* 0xb8 */
215 volatile u16 acqDigitalMark; /* 0xba */
216 volatile u16 trigDacs; /* 0xbc */
217 volatile u16 fill14; /* 0xbe */
218 volatile s16 dioP2ExpansionIO16Bit[32]; /* 0xc0 */
221 /* Scan Sequencer programming */
222 #define DAQBOARD2000_SeqStartScanList 0x0011
223 #define DAQBOARD2000_SeqStopScanList 0x0010
225 /* Prepare for acquisition */
226 #define DAQBOARD2000_AcqResetScanListFifo 0x0004
227 #define DAQBOARD2000_AcqResetResultsFifo 0x0002
228 #define DAQBOARD2000_AcqResetConfigPipe 0x0001
230 /* Acqusition status bits */
231 #define DAQBOARD2000_AcqResultsFIFOMore1Sample 0x0001
232 #define DAQBOARD2000_AcqResultsFIFOHasValidData 0x0002
233 #define DAQBOARD2000_AcqResultsFIFOOverrun 0x0004
234 #define DAQBOARD2000_AcqLogicScanning 0x0008
235 #define DAQBOARD2000_AcqConfigPipeFull 0x0010
236 #define DAQBOARD2000_AcqScanListFIFOEmpty 0x0020
237 #define DAQBOARD2000_AcqAdcNotReady 0x0040
238 #define DAQBOARD2000_ArbitrationFailure 0x0080
239 #define DAQBOARD2000_AcqPacerOverrun 0x0100
240 #define DAQBOARD2000_DacPacerOverrun 0x0200
241 #define DAQBOARD2000_AcqHardwareError 0x01c0
243 /* Scan Sequencer programming */
244 #define DAQBOARD2000_SeqStartScanList 0x0011
245 #define DAQBOARD2000_SeqStopScanList 0x0010
247 /* Pacer Clock Control */
248 #define DAQBOARD2000_AdcPacerInternal 0x0030
249 #define DAQBOARD2000_AdcPacerExternal 0x0032
250 #define DAQBOARD2000_AdcPacerEnable 0x0031
251 #define DAQBOARD2000_AdcPacerEnableDacPacer 0x0034
252 #define DAQBOARD2000_AdcPacerDisable 0x0030
253 #define DAQBOARD2000_AdcPacerNormalMode 0x0060
254 #define DAQBOARD2000_AdcPacerCompatibilityMode 0x0061
255 #define DAQBOARD2000_AdcPacerInternalOutEnable 0x0008
256 #define DAQBOARD2000_AdcPacerExternalRising 0x0100
259 #define DAQBOARD2000_DacFull 0x0001
260 #define DAQBOARD2000_RefBusy 0x0002
261 #define DAQBOARD2000_TrgBusy 0x0004
262 #define DAQBOARD2000_CalBusy 0x0008
263 #define DAQBOARD2000_Dac0Busy 0x0010
264 #define DAQBOARD2000_Dac1Busy 0x0020
265 #define DAQBOARD2000_Dac2Busy 0x0040
266 #define DAQBOARD2000_Dac3Busy 0x0080
269 #define DAQBOARD2000_Dac0Enable 0x0021
270 #define DAQBOARD2000_Dac1Enable 0x0031
271 #define DAQBOARD2000_Dac2Enable 0x0041
272 #define DAQBOARD2000_Dac3Enable 0x0051
273 #define DAQBOARD2000_DacEnableBit 0x0001
274 #define DAQBOARD2000_Dac0Disable 0x0020
275 #define DAQBOARD2000_Dac1Disable 0x0030
276 #define DAQBOARD2000_Dac2Disable 0x0040
277 #define DAQBOARD2000_Dac3Disable 0x0050
278 #define DAQBOARD2000_DacResetFifo 0x0004
279 #define DAQBOARD2000_DacPatternDisable 0x0060
280 #define DAQBOARD2000_DacPatternEnable 0x0061
281 #define DAQBOARD2000_DacSelectSignedData 0x0002
282 #define DAQBOARD2000_DacSelectUnsignedData 0x0000
284 /* Trigger Control */
285 #define DAQBOARD2000_TrigAnalog 0x0000
286 #define DAQBOARD2000_TrigTTL 0x0010
287 #define DAQBOARD2000_TrigTransHiLo 0x0004
288 #define DAQBOARD2000_TrigTransLoHi 0x0000
289 #define DAQBOARD2000_TrigAbove 0x0000
290 #define DAQBOARD2000_TrigBelow 0x0004
291 #define DAQBOARD2000_TrigLevelSense 0x0002
292 #define DAQBOARD2000_TrigEdgeSense 0x0000
293 #define DAQBOARD2000_TrigEnable 0x0001
294 #define DAQBOARD2000_TrigDisable 0x0000
296 /* Reference Dac Selection */
297 #define DAQBOARD2000_PosRefDacSelect 0x0100
298 #define DAQBOARD2000_NegRefDacSelect 0x0000
300 static int daqboard2000_attach(struct comedi_device *dev, struct comedi_devconfig *it);
301 static int daqboard2000_detach(struct comedi_device *dev);
303 static struct comedi_driver driver_daqboard2000 = {
304 .driver_name = "daqboard2000",
305 .module = THIS_MODULE,
306 .attach = daqboard2000_attach,
307 .detach = daqboard2000_detach,
310 struct daq200_boardtype {
314 static const struct daq200_boardtype boardtypes[] = {
315 {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
316 {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
319 #define n_boardtypes (sizeof(boardtypes)/sizeof(struct daq200_boardtype))
320 #define this_board ((const struct daq200_boardtype *)dev->board_ptr)
322 static DEFINE_PCI_DEVICE_TABLE(daqboard2000_pci_table) = {
323 {0x1616, 0x0409, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
327 MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
329 struct daqboard2000_private {
333 struct pci_dev *pci_dev;
337 unsigned int ao_readback[2];
340 #define devpriv ((struct daqboard2000_private *)dev->private)
342 static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
344 struct daqboard2000_hw *fpga = devpriv->daq;
347 fpga->acqScanListFIFO = entry & 0x00ff;
349 fpga->acqScanListFIFO = (entry >> 8) & 0x00ff;
352 static void setup_sampling(struct comedi_device *dev, int chan, int gain)
354 u16 word0, word1, word2, word3;
356 /* Channel 0-7 diff, channel 8-23 single ended */
358 word1 = 0x0004; /* Last scan */
359 word2 = (chan << 6) & 0x00c0;
384 dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
385 dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
387 /* These should be read from EEPROM */
390 /* printk("%d %4.4x %4.4x %4.4x %4.4x\n", chan, word0, word1, word2, word3);*/
391 writeAcqScanListEntry(dev, word0);
392 writeAcqScanListEntry(dev, word1);
393 writeAcqScanListEntry(dev, word2);
394 writeAcqScanListEntry(dev, word3);
397 static int daqboard2000_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
398 struct comedi_insn *insn, unsigned int *data)
401 struct daqboard2000_hw *fpga = devpriv->daq;
402 int gain, chan, timeout;
405 DAQBOARD2000_AcqResetScanListFifo |
406 DAQBOARD2000_AcqResetResultsFifo |
407 DAQBOARD2000_AcqResetConfigPipe;
409 /* If pacer clock is not set to some high value (> 10 us), we
410 risk multiple samples to be put into the result FIFO. */
411 fpga->acqPacerClockDivLow = 1000000; /* 1 second, should be long enough */
412 fpga->acqPacerClockDivHigh = 0;
414 gain = CR_RANGE(insn->chanspec);
415 chan = CR_CHAN(insn->chanspec);
417 /* This doesn't look efficient. I decided to take the conservative
418 * approach when I did the insn conversion. Perhaps it would be
419 * better to have broken it completely, then someone would have been
420 * forced to fix it. --ds */
421 for (i = 0; i < insn->n; i++) {
422 setup_sampling(dev, chan, gain);
423 /* Enable reading from the scanlist FIFO */
424 fpga->acqControl = DAQBOARD2000_SeqStartScanList;
425 for (timeout = 0; timeout < 20; timeout++) {
426 if (fpga->acqControl & DAQBOARD2000_AcqConfigPipeFull) {
431 fpga->acqControl = DAQBOARD2000_AdcPacerEnable;
432 for (timeout = 0; timeout < 20; timeout++) {
433 if (fpga->acqControl & DAQBOARD2000_AcqLogicScanning) {
438 for (timeout = 0; timeout < 20; timeout++) {
441 DAQBOARD2000_AcqResultsFIFOHasValidData) {
446 data[i] = fpga->acqResultsFIFO;
447 fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
448 fpga->acqControl = DAQBOARD2000_SeqStopScanList;
454 static int daqboard2000_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
455 struct comedi_insn *insn, unsigned int *data)
458 int chan = CR_CHAN(insn->chanspec);
460 for (i = 0; i < insn->n; i++) {
461 data[i] = devpriv->ao_readback[chan];
467 static int daqboard2000_ao_insn_write(struct comedi_device *dev, struct comedi_subdevice *s,
468 struct comedi_insn *insn, unsigned int *data)
471 int chan = CR_CHAN(insn->chanspec);
472 struct daqboard2000_hw *fpga = devpriv->daq;
475 for (i = 0; i < insn->n; i++) {
477 * OK, since it works OK without enabling the DAC's, let's keep
478 * it as simple as possible...
480 /* fpga->dacControl = (chan + 2) * 0x0010 | 0x0001; udelay(1000); */
481 fpga->dacSetting[chan] = data[i];
482 for (timeout = 0; timeout < 20; timeout++) {
483 if ((fpga->dacControl & ((chan + 1) * 0x0010)) == 0) {
488 devpriv->ao_readback[chan] = data[i];
490 * Since we never enabled the DAC's, we don't need to disable it...
491 * fpga->dacControl = (chan + 2) * 0x0010 | 0x0000; udelay(1000);
498 static void daqboard2000_resetLocalBus(struct comedi_device *dev)
500 printk("daqboard2000_resetLocalBus\n");
501 writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
503 writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
507 static void daqboard2000_reloadPLX(struct comedi_device *dev)
509 printk("daqboard2000_reloadPLX\n");
510 writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
512 writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
514 writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
518 static void daqboard2000_pulseProgPin(struct comedi_device *dev)
520 printk("daqboard2000_pulseProgPin 1\n");
521 writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
523 writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
524 udelay(10000); /* Not in the original code, but I like symmetry... */
527 static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
533 /* timeout after 50 tries -> 5ms */
534 for (i = 0; i < 50; i++) {
535 cpld = readw(devpriv->daq + 0x1000);
536 if ((cpld & mask) == mask) {
546 static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
551 writew(data, devpriv->daq + 0x1000);
552 if ((readw(devpriv->daq + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
553 DAQBOARD2000_CPLD_INIT) {
559 static int initialize_daqboard2000(struct comedi_device *dev,
560 unsigned char *cpld_array, int len)
563 /* Read the serial EEPROM control register */
568 /* Check to make sure the serial eeprom is present on the board */
569 secr = readl(devpriv->plx + 0x6c);
570 if (!(secr & DAQBOARD2000_EEPROM_PRESENT)) {
572 printk("no serial eeprom\n");
577 for (retry = 0; retry < 3; retry++) {
579 printk("Programming EEPROM try %x\n", retry);
582 daqboard2000_resetLocalBus(dev);
583 daqboard2000_reloadPLX(dev);
584 daqboard2000_pulseProgPin(dev);
585 if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
586 for (i = 0; i < len; i++) {
587 if (cpld_array[i] == 0xff
588 && cpld_array[i + 1] == 0x20) {
590 printk("Preamble found at %d\n", i);
595 for (; i < len; i += 2) {
597 (cpld_array[i] << 8) + cpld_array[i +
599 if (!daqboard2000_writeCPLD(dev, data)) {
605 printk("Programmed\n");
607 daqboard2000_resetLocalBus(dev);
608 daqboard2000_reloadPLX(dev);
617 static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
619 /* printk("Implement: daqboard2000_adcStopDmaTransfer\n");*/
622 static void daqboard2000_adcDisarm(struct comedi_device *dev)
624 struct daqboard2000_hw *fpga = devpriv->daq;
626 /* Disable hardware triggers */
628 fpga->trigControl = DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable;
630 fpga->trigControl = DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable;
632 /* Stop the scan list FIFO from loading the configuration pipe */
634 fpga->acqControl = DAQBOARD2000_SeqStopScanList;
636 /* Stop the pacer clock */
638 fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
640 /* Stop the input dma (abort channel 1) */
641 daqboard2000_adcStopDmaTransfer(dev);
644 static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
646 struct daqboard2000_hw *fpga = devpriv->daq;
649 /* Set the + reference dac value in the FPGA */
650 fpga->refDacs = 0x80 | DAQBOARD2000_PosRefDacSelect;
651 for (timeout = 0; timeout < 20; timeout++) {
652 if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0) {
657 /* printk("DAQBOARD2000_PosRefDacSelect %d\n", timeout);*/
659 /* Set the - reference dac value in the FPGA */
660 fpga->refDacs = 0x80 | DAQBOARD2000_NegRefDacSelect;
661 for (timeout = 0; timeout < 20; timeout++) {
662 if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0) {
667 /* printk("DAQBOARD2000_NegRefDacSelect %d\n", timeout);*/
670 static void daqboard2000_initializeCtrs(struct comedi_device *dev)
672 /* printk("Implement: daqboard2000_initializeCtrs\n");*/
675 static void daqboard2000_initializeTmrs(struct comedi_device *dev)
677 /* printk("Implement: daqboard2000_initializeTmrs\n");*/
680 static void daqboard2000_dacDisarm(struct comedi_device *dev)
682 /* printk("Implement: daqboard2000_dacDisarm\n");*/
685 static void daqboard2000_initializeAdc(struct comedi_device *dev)
687 daqboard2000_adcDisarm(dev);
688 daqboard2000_activateReferenceDacs(dev);
689 daqboard2000_initializeCtrs(dev);
690 daqboard2000_initializeTmrs(dev);
693 static void daqboard2000_initializeDac(struct comedi_device *dev)
695 daqboard2000_dacDisarm(dev);
699 The test command, REMOVE!!:
701 rmmod daqboard2000 ; rmmod comedi; make install ; modprobe daqboard2000; /usr/sbin/comedi_config /dev/comedi0 daqboard/2000 ; tail -40 /var/log/messages
704 static int daqboard2000_8255_cb(int dir, int port, int data,
705 unsigned long ioaddr)
709 writew(data, ((void *)ioaddr) + port * 2);
712 result = readw(((void *)ioaddr) + port * 2);
715 printk("daqboard2000_8255_cb %x %d %d %2.2x -> %2.2x\n",
716 arg, dir, port, data, result);
721 static int daqboard2000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
724 struct comedi_subdevice *s;
725 struct pci_dev *card = NULL;
727 unsigned int aux_len;
730 printk("comedi%d: daqboard2000:", dev->minor);
732 bus = it->options[0];
733 slot = it->options[1];
735 result = alloc_private(dev, sizeof(struct daqboard2000_private));
739 for (card = pci_get_device(0x1616, 0x0409, NULL);
741 card = pci_get_device(0x1616, 0x0409, card)) {
743 /* requested particular bus/slot */
744 if (card->bus->number != bus ||
745 PCI_SLOT(card->devfn) != slot) {
749 break; /* found one */
753 printk(" no daqboard2000 found at bus/slot: %d/%d\n",
756 printk(" no daqboard2000 found\n");
761 devpriv->pci_dev = card;
762 id = ((u32) card->subsystem_device << 16) | card->
764 for (i = 0; i < n_boardtypes; i++) {
765 if (boardtypes[i].id == id) {
766 printk(" %s", boardtypes[i].name);
767 dev->board_ptr = boardtypes + i;
770 if (!dev->board_ptr) {
771 printk(" unknown subsystem id %08x (pretend it is an ids2)", id);
772 dev->board_ptr = boardtypes;
776 result = comedi_pci_enable(card, "daqboard2000");
778 printk(" failed to enable PCI device and request regions\n");
781 devpriv->got_regions = 1;
783 ioremap(pci_resource_start(card, 0), DAQBOARD2000_PLX_SIZE);
785 ioremap(pci_resource_start(card, 2), DAQBOARD2000_DAQ_SIZE);
786 if (!devpriv->plx || !devpriv->daq) {
790 result = alloc_subdevices(dev, 3);
794 readl(devpriv->plx + 0x6c);
798 Windows code does restore interrupts, but since we don't use them...
799 pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
800 printk("Interrupt before is: %x\n", interrupt);
803 aux_data = comedi_aux_data(it->options, 0);
804 aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
806 if (aux_data && aux_len) {
807 result = initialize_daqboard2000(dev, aux_data, aux_len);
809 printk("no FPGA initialization code, aborting\n");
814 daqboard2000_initializeAdc(dev);
815 daqboard2000_initializeDac(dev);
817 Windows code does restore interrupts, but since we don't use them...
818 pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
819 printk("Interrupt after is: %x\n", interrupt);
822 dev->iobase = (unsigned long)devpriv->daq;
824 dev->board_name = this_board->name;
826 s = dev->subdevices + 0;
828 s->type = COMEDI_SUBD_AI;
829 s->subdev_flags = SDF_READABLE | SDF_GROUND;
832 s->insn_read = daqboard2000_ai_insn_read;
833 s->range_table = &range_daqboard2000_ai;
835 s = dev->subdevices + 1;
837 s->type = COMEDI_SUBD_AO;
838 s->subdev_flags = SDF_WRITABLE;
841 s->insn_read = daqboard2000_ao_insn_read;
842 s->insn_write = daqboard2000_ao_insn_write;
843 s->range_table = &range_daqboard2000_ao;
845 s = dev->subdevices + 2;
846 result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
847 (unsigned long)(dev->iobase + 0x40));
854 static int daqboard2000_detach(struct comedi_device *dev)
856 printk("comedi%d: daqboard2000: remove\n", dev->minor);
859 subdev_8255_cleanup(dev, dev->subdevices + 2);
862 free_irq(dev->irq, dev);
866 iounmap(devpriv->daq);
868 iounmap(devpriv->plx);
869 if (devpriv->pci_dev) {
870 if (devpriv->got_regions) {
871 comedi_pci_disable(devpriv->pci_dev);
873 pci_dev_put(devpriv->pci_dev);
879 COMEDI_PCI_INITCLEANUP(driver_daqboard2000, daqboard2000_pci_table);