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 fimware, which is available from
35 http://www.comedi.org in the comedi_nonfree_firmware tarball.
37 Configuration options: not applicable, uses PCI auto config
40 This card was obviously never intended to leave the Windows world,
41 since it lacked all kind of hardware documentation (except for cable
42 pinouts, plug and pray has something to catch up with yet).
44 With some help from our swedish distributor, we got the Windows sourcecode
45 for the card, and here are the findings so far.
47 1. A good document that describes the PCI interface chip is 9080db-106.pdf
48 available from http://www.plxtech.com/products/io/pci9080
50 2. The initialization done so far is:
51 a. program the FPGA (windows code sans a lot of error messages)
54 3. Analog out seems to work OK with DAC's disabled, if DAC's are enabled,
55 you have to output values to all enabled DAC's until result appears, I
56 guess that it has something to do with pacer clocks, but the source
57 gives me no clues. I'll keep it simple so far.
60 Each channel in the scanlist seems to be controlled by four
64 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65 ! | | | ! | | | ! | | | ! | | | !
66 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
69 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
70 ! | | | ! | | | ! | | | ! | | | !
71 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
73 +------+------+ | | | | +-- Digital input (??)
74 | | | | +---- 10 us settling time
75 | | | +------ Suspend acquisition (last to scan)
76 | | +-------- Simultaneous sample and hold
77 | +---------- Signed data format
78 +------------------------- Correction offset low
81 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
82 ! | | | ! | | | ! | | | ! | | | !
83 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
85 +-----+ +--+--+ +++ +++ +--+--+
86 | | | | +----- Expansion channel
87 | | | +----------- Expansion gain
88 | | +--------------- Channel (low)
89 | +--------------------- Correction offset high
90 +----------------------------- Correction gain low
92 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
93 ! | | | ! | | | ! | | | ! | | | !
94 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
96 +------+------+ | | +-+-+ | | +-- Low bank enable
97 | | | | | +---- High bank enable
98 | | | | +------ Hi/low select
99 | | | +---------- Gain (1,?,2,4,8,16,32,64)
100 | | +-------------- differential/single ended
101 | +---------------- Unipolar
102 +------------------------- Correction gain high
104 999. The card seems to have an incredible amount of capabilities, but
105 trying to reverse engineer them from the Windows source is beyond my
110 #include "../comedidev.h"
112 #include <linux/delay.h>
113 #include <linux/interrupt.h>
114 #include <linux/firmware.h>
118 #define DAQBOARD2000_FIRMWARE "daqboard2000_firmware.bin"
120 #define DAQBOARD2000_SUBSYSTEM_IDS2 0x0002 /* Daqboard/2000 - 2 Dacs */
121 #define DAQBOARD2000_SUBSYSTEM_IDS4 0x0004 /* Daqboard/2000 - 4 Dacs */
123 /* Initialization bits for the Serial EEPROM Control Register */
124 #define DAQBOARD2000_SECRProgPinHi 0x8001767e
125 #define DAQBOARD2000_SECRProgPinLo 0x8000767e
126 #define DAQBOARD2000_SECRLocalBusHi 0xc000767e
127 #define DAQBOARD2000_SECRLocalBusLo 0x8000767e
128 #define DAQBOARD2000_SECRReloadHi 0xa000767e
129 #define DAQBOARD2000_SECRReloadLo 0x8000767e
131 /* SECR status bits */
132 #define DAQBOARD2000_EEPROM_PRESENT 0x10000000
134 /* CPLD status bits */
135 #define DAQBOARD2000_CPLD_INIT 0x0002
136 #define DAQBOARD2000_CPLD_DONE 0x0004
138 static const struct comedi_lrange range_daqboard2000_ai = {
157 * Register Memory Map
159 #define acqControl 0x00 /* u16 */
160 #define acqScanListFIFO 0x02 /* u16 */
161 #define acqPacerClockDivLow 0x04 /* u32 */
162 #define acqScanCounter 0x08 /* u16 */
163 #define acqPacerClockDivHigh 0x0a /* u16 */
164 #define acqTriggerCount 0x0c /* u16 */
165 #define acqResultsFIFO 0x10 /* u16 */
166 #define acqResultsShadow 0x14 /* u16 */
167 #define acqAdcResult 0x18 /* u16 */
168 #define dacScanCounter 0x1c /* u16 */
169 #define dacControl 0x20 /* u16 */
170 #define dacFIFO 0x24 /* s16 */
171 #define dacPacerClockDiv 0x2a /* u16 */
172 #define refDacs 0x2c /* u16 */
173 #define dioControl 0x30 /* u16 */
174 #define dioP3hsioData 0x32 /* s16 */
175 #define dioP3Control 0x34 /* u16 */
176 #define calEepromControl 0x36 /* u16 */
177 #define dacSetting(x) (0x38 + (x)*2) /* s16 */
178 #define dioP2ExpansionIO8Bit 0x40 /* s16 */
179 #define ctrTmrControl 0x80 /* u16 */
180 #define ctrInput(x) (0x88 + (x)*2) /* s16 */
181 #define timerDivisor(x) (0xa0 + (x)*2) /* u16 */
182 #define dmaControl 0xb0 /* u16 */
183 #define trigControl 0xb2 /* u16 */
184 #define calEeprom 0xb8 /* u16 */
185 #define acqDigitalMark 0xba /* u16 */
186 #define trigDacs 0xbc /* u16 */
187 #define dioP2ExpansionIO16Bit(x) (0xc0 + (x)*2) /* s16 */
189 /* Scan Sequencer programming */
190 #define DAQBOARD2000_SeqStartScanList 0x0011
191 #define DAQBOARD2000_SeqStopScanList 0x0010
193 /* Prepare for acquisition */
194 #define DAQBOARD2000_AcqResetScanListFifo 0x0004
195 #define DAQBOARD2000_AcqResetResultsFifo 0x0002
196 #define DAQBOARD2000_AcqResetConfigPipe 0x0001
198 /* Acqusition status bits */
199 #define DAQBOARD2000_AcqResultsFIFOMore1Sample 0x0001
200 #define DAQBOARD2000_AcqResultsFIFOHasValidData 0x0002
201 #define DAQBOARD2000_AcqResultsFIFOOverrun 0x0004
202 #define DAQBOARD2000_AcqLogicScanning 0x0008
203 #define DAQBOARD2000_AcqConfigPipeFull 0x0010
204 #define DAQBOARD2000_AcqScanListFIFOEmpty 0x0020
205 #define DAQBOARD2000_AcqAdcNotReady 0x0040
206 #define DAQBOARD2000_ArbitrationFailure 0x0080
207 #define DAQBOARD2000_AcqPacerOverrun 0x0100
208 #define DAQBOARD2000_DacPacerOverrun 0x0200
209 #define DAQBOARD2000_AcqHardwareError 0x01c0
211 /* Scan Sequencer programming */
212 #define DAQBOARD2000_SeqStartScanList 0x0011
213 #define DAQBOARD2000_SeqStopScanList 0x0010
215 /* Pacer Clock Control */
216 #define DAQBOARD2000_AdcPacerInternal 0x0030
217 #define DAQBOARD2000_AdcPacerExternal 0x0032
218 #define DAQBOARD2000_AdcPacerEnable 0x0031
219 #define DAQBOARD2000_AdcPacerEnableDacPacer 0x0034
220 #define DAQBOARD2000_AdcPacerDisable 0x0030
221 #define DAQBOARD2000_AdcPacerNormalMode 0x0060
222 #define DAQBOARD2000_AdcPacerCompatibilityMode 0x0061
223 #define DAQBOARD2000_AdcPacerInternalOutEnable 0x0008
224 #define DAQBOARD2000_AdcPacerExternalRising 0x0100
227 #define DAQBOARD2000_DacFull 0x0001
228 #define DAQBOARD2000_RefBusy 0x0002
229 #define DAQBOARD2000_TrgBusy 0x0004
230 #define DAQBOARD2000_CalBusy 0x0008
231 #define DAQBOARD2000_Dac0Busy 0x0010
232 #define DAQBOARD2000_Dac1Busy 0x0020
233 #define DAQBOARD2000_Dac2Busy 0x0040
234 #define DAQBOARD2000_Dac3Busy 0x0080
237 #define DAQBOARD2000_Dac0Enable 0x0021
238 #define DAQBOARD2000_Dac1Enable 0x0031
239 #define DAQBOARD2000_Dac2Enable 0x0041
240 #define DAQBOARD2000_Dac3Enable 0x0051
241 #define DAQBOARD2000_DacEnableBit 0x0001
242 #define DAQBOARD2000_Dac0Disable 0x0020
243 #define DAQBOARD2000_Dac1Disable 0x0030
244 #define DAQBOARD2000_Dac2Disable 0x0040
245 #define DAQBOARD2000_Dac3Disable 0x0050
246 #define DAQBOARD2000_DacResetFifo 0x0004
247 #define DAQBOARD2000_DacPatternDisable 0x0060
248 #define DAQBOARD2000_DacPatternEnable 0x0061
249 #define DAQBOARD2000_DacSelectSignedData 0x0002
250 #define DAQBOARD2000_DacSelectUnsignedData 0x0000
252 /* Trigger Control */
253 #define DAQBOARD2000_TrigAnalog 0x0000
254 #define DAQBOARD2000_TrigTTL 0x0010
255 #define DAQBOARD2000_TrigTransHiLo 0x0004
256 #define DAQBOARD2000_TrigTransLoHi 0x0000
257 #define DAQBOARD2000_TrigAbove 0x0000
258 #define DAQBOARD2000_TrigBelow 0x0004
259 #define DAQBOARD2000_TrigLevelSense 0x0002
260 #define DAQBOARD2000_TrigEdgeSense 0x0000
261 #define DAQBOARD2000_TrigEnable 0x0001
262 #define DAQBOARD2000_TrigDisable 0x0000
264 /* Reference Dac Selection */
265 #define DAQBOARD2000_PosRefDacSelect 0x0100
266 #define DAQBOARD2000_NegRefDacSelect 0x0000
268 struct daq200_boardtype {
272 static const struct daq200_boardtype boardtypes[] = {
273 {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
274 {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
277 struct daqboard2000_private {
283 unsigned int ao_readback[2];
286 static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
288 struct daqboard2000_private *devpriv = dev->private;
291 writew(entry & 0x00ff, devpriv->daq + acqScanListFIFO);
293 writew((entry >> 8) & 0x00ff, devpriv->daq + acqScanListFIFO);
296 static void setup_sampling(struct comedi_device *dev, int chan, int gain)
298 u16 word0, word1, word2, word3;
300 /* Channel 0-7 diff, channel 8-23 single ended */
302 word1 = 0x0004; /* Last scan */
303 word2 = (chan << 6) & 0x00c0;
328 dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
329 dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
331 /* These should be read from EEPROM */
334 writeAcqScanListEntry(dev, word0);
335 writeAcqScanListEntry(dev, word1);
336 writeAcqScanListEntry(dev, word2);
337 writeAcqScanListEntry(dev, word3);
340 static int daqboard2000_ai_insn_read(struct comedi_device *dev,
341 struct comedi_subdevice *s,
342 struct comedi_insn *insn,
345 struct daqboard2000_private *devpriv = dev->private;
347 int gain, chan, timeout;
350 writew(DAQBOARD2000_AcqResetScanListFifo |
351 DAQBOARD2000_AcqResetResultsFifo |
352 DAQBOARD2000_AcqResetConfigPipe, devpriv->daq + acqControl);
355 * If pacer clock is not set to some high value (> 10 us), we
356 * risk multiple samples to be put into the result FIFO.
358 /* 1 second, should be long enough */
359 writel(1000000, devpriv->daq + acqPacerClockDivLow);
360 writew(0, devpriv->daq + acqPacerClockDivHigh);
362 gain = CR_RANGE(insn->chanspec);
363 chan = CR_CHAN(insn->chanspec);
365 /* This doesn't look efficient. I decided to take the conservative
366 * approach when I did the insn conversion. Perhaps it would be
367 * better to have broken it completely, then someone would have been
368 * forced to fix it. --ds */
369 for (i = 0; i < insn->n; i++) {
370 setup_sampling(dev, chan, gain);
371 /* Enable reading from the scanlist FIFO */
372 writew(DAQBOARD2000_SeqStartScanList,
373 devpriv->daq + acqControl);
374 for (timeout = 0; timeout < 20; timeout++) {
375 val = readw(devpriv->daq + acqControl);
376 if (val & DAQBOARD2000_AcqConfigPipeFull)
380 writew(DAQBOARD2000_AdcPacerEnable, devpriv->daq + acqControl);
381 for (timeout = 0; timeout < 20; timeout++) {
382 val = readw(devpriv->daq + acqControl);
383 if (val & DAQBOARD2000_AcqLogicScanning)
387 for (timeout = 0; timeout < 20; timeout++) {
388 val = readw(devpriv->daq + acqControl);
389 if (val & DAQBOARD2000_AcqResultsFIFOHasValidData)
393 data[i] = readw(devpriv->daq + acqResultsFIFO);
394 writew(DAQBOARD2000_AdcPacerDisable, devpriv->daq + acqControl);
395 writew(DAQBOARD2000_SeqStopScanList, devpriv->daq + acqControl);
401 static int daqboard2000_ao_insn_read(struct comedi_device *dev,
402 struct comedi_subdevice *s,
403 struct comedi_insn *insn,
406 struct daqboard2000_private *devpriv = dev->private;
407 int chan = CR_CHAN(insn->chanspec);
410 for (i = 0; i < insn->n; i++)
411 data[i] = devpriv->ao_readback[chan];
416 static int daqboard2000_ao_insn_write(struct comedi_device *dev,
417 struct comedi_subdevice *s,
418 struct comedi_insn *insn,
421 struct daqboard2000_private *devpriv = dev->private;
422 int chan = CR_CHAN(insn->chanspec);
427 for (i = 0; i < insn->n; i++) {
430 * OK, since it works OK without enabling the DAC's,
431 * let's keep it as simple as possible...
433 writew((chan + 2) * 0x0010 | 0x0001,
434 devpriv->daq + dacControl);
437 writew(data[i], devpriv->daq + dacSetting(chan));
438 for (timeout = 0; timeout < 20; timeout++) {
439 val = readw(devpriv->daq + dacControl);
440 if ((val & ((chan + 1) * 0x0010)) == 0)
444 devpriv->ao_readback[chan] = data[i];
447 * Since we never enabled the DAC's, we don't need
450 writew((chan + 2) * 0x0010 | 0x0000,
451 devpriv->daq + dacControl);
459 static void daqboard2000_resetLocalBus(struct comedi_device *dev)
461 struct daqboard2000_private *devpriv = dev->private;
463 writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
465 writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
469 static void daqboard2000_reloadPLX(struct comedi_device *dev)
471 struct daqboard2000_private *devpriv = dev->private;
473 writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
475 writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
477 writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
481 static void daqboard2000_pulseProgPin(struct comedi_device *dev)
483 struct daqboard2000_private *devpriv = dev->private;
485 writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
487 writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
488 udelay(10000); /* Not in the original code, but I like symmetry... */
491 static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
493 struct daqboard2000_private *devpriv = dev->private;
498 /* timeout after 50 tries -> 5ms */
499 for (i = 0; i < 50; i++) {
500 cpld = readw(devpriv->daq + 0x1000);
501 if ((cpld & mask) == mask) {
511 static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
513 struct daqboard2000_private *devpriv = dev->private;
517 writew(data, devpriv->daq + 0x1000);
518 if ((readw(devpriv->daq + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
519 DAQBOARD2000_CPLD_INIT) {
525 static int initialize_daqboard2000(struct comedi_device *dev,
526 const u8 *cpld_array, size_t len)
528 struct daqboard2000_private *devpriv = dev->private;
530 /* Read the serial EEPROM control register */
535 /* Check to make sure the serial eeprom is present on the board */
536 secr = readl(devpriv->plx + 0x6c);
537 if (!(secr & DAQBOARD2000_EEPROM_PRESENT))
540 for (retry = 0; retry < 3; retry++) {
541 daqboard2000_resetLocalBus(dev);
542 daqboard2000_reloadPLX(dev);
543 daqboard2000_pulseProgPin(dev);
544 if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
545 for (i = 0; i < len; i++) {
546 if (cpld_array[i] == 0xff &&
547 cpld_array[i + 1] == 0x20)
550 for (; i < len; i += 2) {
552 (cpld_array[i] << 8) + cpld_array[i + 1];
553 if (!daqboard2000_writeCPLD(dev, data))
557 daqboard2000_resetLocalBus(dev);
558 daqboard2000_reloadPLX(dev);
567 static int daqboard2000_upload_firmware(struct comedi_device *dev)
569 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
570 const struct firmware *fw;
573 ret = request_firmware(&fw, DAQBOARD2000_FIRMWARE, &pcidev->dev);
577 ret = initialize_daqboard2000(dev, fw->data, fw->size);
578 release_firmware(fw);
583 static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
587 static void daqboard2000_adcDisarm(struct comedi_device *dev)
589 struct daqboard2000_private *devpriv = dev->private;
591 /* Disable hardware triggers */
593 writew(DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable,
594 devpriv->daq + trigControl);
596 writew(DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable,
597 devpriv->daq + trigControl);
599 /* Stop the scan list FIFO from loading the configuration pipe */
601 writew(DAQBOARD2000_SeqStopScanList, devpriv->daq + acqControl);
603 /* Stop the pacer clock */
605 writew(DAQBOARD2000_AdcPacerDisable, devpriv->daq + acqControl);
607 /* Stop the input dma (abort channel 1) */
608 daqboard2000_adcStopDmaTransfer(dev);
611 static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
613 struct daqboard2000_private *devpriv = dev->private;
617 /* Set the + reference dac value in the FPGA */
618 writew(0x80 | DAQBOARD2000_PosRefDacSelect, devpriv->daq + refDacs);
619 for (timeout = 0; timeout < 20; timeout++) {
620 val = readw(devpriv->daq + dacControl);
621 if ((val & DAQBOARD2000_RefBusy) == 0)
626 /* Set the - reference dac value in the FPGA */
627 writew(0x80 | DAQBOARD2000_NegRefDacSelect, devpriv->daq + refDacs);
628 for (timeout = 0; timeout < 20; timeout++) {
629 val = readw(devpriv->daq + dacControl);
630 if ((val & DAQBOARD2000_RefBusy) == 0)
636 static void daqboard2000_initializeCtrs(struct comedi_device *dev)
640 static void daqboard2000_initializeTmrs(struct comedi_device *dev)
644 static void daqboard2000_dacDisarm(struct comedi_device *dev)
648 static void daqboard2000_initializeAdc(struct comedi_device *dev)
650 daqboard2000_adcDisarm(dev);
651 daqboard2000_activateReferenceDacs(dev);
652 daqboard2000_initializeCtrs(dev);
653 daqboard2000_initializeTmrs(dev);
656 static void daqboard2000_initializeDac(struct comedi_device *dev)
658 daqboard2000_dacDisarm(dev);
661 static int daqboard2000_8255_cb(int dir, int port, int data,
662 unsigned long ioaddr)
664 void __iomem *mmio_base = (void __iomem *)ioaddr;
667 writew(data, mmio_base + port * 2);
670 return readw(mmio_base + port * 2);
674 static const void *daqboard2000_find_boardinfo(struct comedi_device *dev,
675 struct pci_dev *pcidev)
677 const struct daq200_boardtype *board;
680 if (pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
683 for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
684 board = &boardtypes[i];
685 if (pcidev->subsystem_device == board->id)
691 static int __devinit daqboard2000_auto_attach(struct comedi_device *dev,
692 unsigned long context_unused)
694 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
695 const struct daq200_boardtype *board;
696 struct daqboard2000_private *devpriv;
697 struct comedi_subdevice *s;
700 board = daqboard2000_find_boardinfo(dev, pcidev);
703 dev->board_ptr = board;
704 dev->board_name = board->name;
706 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
709 dev->private = devpriv;
711 result = comedi_pci_enable(pcidev, dev->driver->driver_name);
714 dev->iobase = 1; /* the "detach" needs this */
716 devpriv->plx = ioremap(pci_resource_start(pcidev, 0),
717 pci_resource_len(pcidev, 0));
718 devpriv->daq = ioremap(pci_resource_start(pcidev, 2),
719 pci_resource_len(pcidev, 2));
720 if (!devpriv->plx || !devpriv->daq)
723 result = comedi_alloc_subdevices(dev, 3);
727 readl(devpriv->plx + 0x6c);
729 result = daqboard2000_upload_firmware(dev);
733 daqboard2000_initializeAdc(dev);
734 daqboard2000_initializeDac(dev);
736 s = &dev->subdevices[0];
738 s->type = COMEDI_SUBD_AI;
739 s->subdev_flags = SDF_READABLE | SDF_GROUND;
742 s->insn_read = daqboard2000_ai_insn_read;
743 s->range_table = &range_daqboard2000_ai;
745 s = &dev->subdevices[1];
747 s->type = COMEDI_SUBD_AO;
748 s->subdev_flags = SDF_WRITABLE;
751 s->insn_read = daqboard2000_ao_insn_read;
752 s->insn_write = daqboard2000_ao_insn_write;
753 s->range_table = &range_bipolar10;
755 s = &dev->subdevices[2];
756 result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
757 (unsigned long)(devpriv->daq + dioP2ExpansionIO8Bit));
761 dev_info(dev->class_dev, "%s: %s attached\n",
762 dev->driver->driver_name, dev->board_name);
767 static void daqboard2000_detach(struct comedi_device *dev)
769 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
770 struct daqboard2000_private *devpriv = dev->private;
773 subdev_8255_cleanup(dev, &dev->subdevices[2]);
775 free_irq(dev->irq, dev);
778 iounmap(devpriv->daq);
780 iounmap(devpriv->plx);
784 comedi_pci_disable(pcidev);
789 static struct comedi_driver daqboard2000_driver = {
790 .driver_name = "daqboard2000",
791 .module = THIS_MODULE,
792 .auto_attach = daqboard2000_auto_attach,
793 .detach = daqboard2000_detach,
796 static int __devinit daqboard2000_pci_probe(struct pci_dev *dev,
797 const struct pci_device_id *ent)
799 return comedi_pci_auto_config(dev, &daqboard2000_driver);
802 static void __devexit daqboard2000_pci_remove(struct pci_dev *dev)
804 comedi_pci_auto_unconfig(dev);
807 static DEFINE_PCI_DEVICE_TABLE(daqboard2000_pci_table) = {
808 { PCI_DEVICE(PCI_VENDOR_ID_IOTECH, 0x0409) },
811 MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
813 static struct pci_driver daqboard2000_pci_driver = {
814 .name = "daqboard2000",
815 .id_table = daqboard2000_pci_table,
816 .probe = daqboard2000_pci_probe,
817 .remove = __devexit_p(daqboard2000_pci_remove),
819 module_comedi_pci_driver(daqboard2000_driver, daqboard2000_pci_driver);
821 MODULE_AUTHOR("Comedi http://www.comedi.org");
822 MODULE_DESCRIPTION("Comedi low-level driver");
823 MODULE_LICENSE("GPL");
824 MODULE_FIRMWARE(DAQBOARD2000_FIRMWARE);