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.
20 Description: IOTech DAQBoard/2000
21 Author: Anders Blomdell <anders.blomdell@control.lth.se>
23 Updated: Mon, 14 Apr 2008 15:28:52 +0100
24 Devices: [IOTech] DAQBoard/2000 (daqboard2000)
26 Much of the functionality of this driver was determined from reading
27 the source code for the Windows driver.
29 The FPGA on the board requires fimware, which is available from
30 http://www.comedi.org in the comedi_nonfree_firmware tarball.
32 Configuration options: not applicable, uses PCI auto config
35 This card was obviously never intended to leave the Windows world,
36 since it lacked all kind of hardware documentation (except for cable
37 pinouts, plug and pray has something to catch up with yet).
39 With some help from our swedish distributor, we got the Windows sourcecode
40 for the card, and here are the findings so far.
42 1. A good document that describes the PCI interface chip is 9080db-106.pdf
43 available from http://www.plxtech.com/products/io/pci9080
45 2. The initialization done so far is:
46 a. program the FPGA (windows code sans a lot of error messages)
49 3. Analog out seems to work OK with DAC's disabled, if DAC's are enabled,
50 you have to output values to all enabled DAC's until result appears, I
51 guess that it has something to do with pacer clocks, but the source
52 gives me no clues. I'll keep it simple so far.
55 Each channel in the scanlist seems to be controlled by four
59 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60 ! | | | ! | | | ! | | | ! | | | !
61 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
64 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65 ! | | | ! | | | ! | | | ! | | | !
66 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
68 +------+------+ | | | | +-- Digital input (??)
69 | | | | +---- 10 us settling time
70 | | | +------ Suspend acquisition (last to scan)
71 | | +-------- Simultaneous sample and hold
72 | +---------- Signed data format
73 +------------------------- Correction offset low
76 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
77 ! | | | ! | | | ! | | | ! | | | !
78 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
80 +-----+ +--+--+ +++ +++ +--+--+
81 | | | | +----- Expansion channel
82 | | | +----------- Expansion gain
83 | | +--------------- Channel (low)
84 | +--------------------- Correction offset high
85 +----------------------------- Correction gain low
87 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
88 ! | | | ! | | | ! | | | ! | | | !
89 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
91 +------+------+ | | +-+-+ | | +-- Low bank enable
92 | | | | | +---- High bank enable
93 | | | | +------ Hi/low select
94 | | | +---------- Gain (1,?,2,4,8,16,32,64)
95 | | +-------------- differential/single ended
96 | +---------------- Unipolar
97 +------------------------- Correction gain high
99 999. The card seems to have an incredible amount of capabilities, but
100 trying to reverse engineer them from the Windows source is beyond my
105 #include <linux/module.h>
106 #include <linux/pci.h>
107 #include <linux/delay.h>
108 #include <linux/interrupt.h>
110 #include "../comedidev.h"
114 #define DAQBOARD2000_FIRMWARE "daqboard2000_firmware.bin"
116 #define DAQBOARD2000_SUBSYSTEM_IDS2 0x0002 /* Daqboard/2000 - 2 Dacs */
117 #define DAQBOARD2000_SUBSYSTEM_IDS4 0x0004 /* Daqboard/2000 - 4 Dacs */
119 /* Initialization bits for the Serial EEPROM Control Register */
120 #define DAQBOARD2000_SECRProgPinHi 0x8001767e
121 #define DAQBOARD2000_SECRProgPinLo 0x8000767e
122 #define DAQBOARD2000_SECRLocalBusHi 0xc000767e
123 #define DAQBOARD2000_SECRLocalBusLo 0x8000767e
124 #define DAQBOARD2000_SECRReloadHi 0xa000767e
125 #define DAQBOARD2000_SECRReloadLo 0x8000767e
127 /* SECR status bits */
128 #define DAQBOARD2000_EEPROM_PRESENT 0x10000000
130 /* CPLD status bits */
131 #define DAQBOARD2000_CPLD_INIT 0x0002
132 #define DAQBOARD2000_CPLD_DONE 0x0004
134 static const struct comedi_lrange range_daqboard2000_ai = {
153 * Register Memory Map
155 #define acqControl 0x00 /* u16 */
156 #define acqScanListFIFO 0x02 /* u16 */
157 #define acqPacerClockDivLow 0x04 /* u32 */
158 #define acqScanCounter 0x08 /* u16 */
159 #define acqPacerClockDivHigh 0x0a /* u16 */
160 #define acqTriggerCount 0x0c /* u16 */
161 #define acqResultsFIFO 0x10 /* u16 */
162 #define acqResultsShadow 0x14 /* u16 */
163 #define acqAdcResult 0x18 /* u16 */
164 #define dacScanCounter 0x1c /* u16 */
165 #define dacControl 0x20 /* u16 */
166 #define dacFIFO 0x24 /* s16 */
167 #define dacPacerClockDiv 0x2a /* u16 */
168 #define refDacs 0x2c /* u16 */
169 #define dioControl 0x30 /* u16 */
170 #define dioP3hsioData 0x32 /* s16 */
171 #define dioP3Control 0x34 /* u16 */
172 #define calEepromControl 0x36 /* u16 */
173 #define dacSetting(x) (0x38 + (x)*2) /* s16 */
174 #define dioP2ExpansionIO8Bit 0x40 /* s16 */
175 #define ctrTmrControl 0x80 /* u16 */
176 #define ctrInput(x) (0x88 + (x)*2) /* s16 */
177 #define timerDivisor(x) (0xa0 + (x)*2) /* u16 */
178 #define dmaControl 0xb0 /* u16 */
179 #define trigControl 0xb2 /* u16 */
180 #define calEeprom 0xb8 /* u16 */
181 #define acqDigitalMark 0xba /* u16 */
182 #define trigDacs 0xbc /* u16 */
183 #define dioP2ExpansionIO16Bit(x) (0xc0 + (x)*2) /* s16 */
185 /* Scan Sequencer programming */
186 #define DAQBOARD2000_SeqStartScanList 0x0011
187 #define DAQBOARD2000_SeqStopScanList 0x0010
189 /* Prepare for acquisition */
190 #define DAQBOARD2000_AcqResetScanListFifo 0x0004
191 #define DAQBOARD2000_AcqResetResultsFifo 0x0002
192 #define DAQBOARD2000_AcqResetConfigPipe 0x0001
194 /* Acqusition status bits */
195 #define DAQBOARD2000_AcqResultsFIFOMore1Sample 0x0001
196 #define DAQBOARD2000_AcqResultsFIFOHasValidData 0x0002
197 #define DAQBOARD2000_AcqResultsFIFOOverrun 0x0004
198 #define DAQBOARD2000_AcqLogicScanning 0x0008
199 #define DAQBOARD2000_AcqConfigPipeFull 0x0010
200 #define DAQBOARD2000_AcqScanListFIFOEmpty 0x0020
201 #define DAQBOARD2000_AcqAdcNotReady 0x0040
202 #define DAQBOARD2000_ArbitrationFailure 0x0080
203 #define DAQBOARD2000_AcqPacerOverrun 0x0100
204 #define DAQBOARD2000_DacPacerOverrun 0x0200
205 #define DAQBOARD2000_AcqHardwareError 0x01c0
207 /* Scan Sequencer programming */
208 #define DAQBOARD2000_SeqStartScanList 0x0011
209 #define DAQBOARD2000_SeqStopScanList 0x0010
211 /* Pacer Clock Control */
212 #define DAQBOARD2000_AdcPacerInternal 0x0030
213 #define DAQBOARD2000_AdcPacerExternal 0x0032
214 #define DAQBOARD2000_AdcPacerEnable 0x0031
215 #define DAQBOARD2000_AdcPacerEnableDacPacer 0x0034
216 #define DAQBOARD2000_AdcPacerDisable 0x0030
217 #define DAQBOARD2000_AdcPacerNormalMode 0x0060
218 #define DAQBOARD2000_AdcPacerCompatibilityMode 0x0061
219 #define DAQBOARD2000_AdcPacerInternalOutEnable 0x0008
220 #define DAQBOARD2000_AdcPacerExternalRising 0x0100
223 #define DAQBOARD2000_DacFull 0x0001
224 #define DAQBOARD2000_RefBusy 0x0002
225 #define DAQBOARD2000_TrgBusy 0x0004
226 #define DAQBOARD2000_CalBusy 0x0008
227 #define DAQBOARD2000_Dac0Busy 0x0010
228 #define DAQBOARD2000_Dac1Busy 0x0020
229 #define DAQBOARD2000_Dac2Busy 0x0040
230 #define DAQBOARD2000_Dac3Busy 0x0080
233 #define DAQBOARD2000_Dac0Enable 0x0021
234 #define DAQBOARD2000_Dac1Enable 0x0031
235 #define DAQBOARD2000_Dac2Enable 0x0041
236 #define DAQBOARD2000_Dac3Enable 0x0051
237 #define DAQBOARD2000_DacEnableBit 0x0001
238 #define DAQBOARD2000_Dac0Disable 0x0020
239 #define DAQBOARD2000_Dac1Disable 0x0030
240 #define DAQBOARD2000_Dac2Disable 0x0040
241 #define DAQBOARD2000_Dac3Disable 0x0050
242 #define DAQBOARD2000_DacResetFifo 0x0004
243 #define DAQBOARD2000_DacPatternDisable 0x0060
244 #define DAQBOARD2000_DacPatternEnable 0x0061
245 #define DAQBOARD2000_DacSelectSignedData 0x0002
246 #define DAQBOARD2000_DacSelectUnsignedData 0x0000
248 /* Trigger Control */
249 #define DAQBOARD2000_TrigAnalog 0x0000
250 #define DAQBOARD2000_TrigTTL 0x0010
251 #define DAQBOARD2000_TrigTransHiLo 0x0004
252 #define DAQBOARD2000_TrigTransLoHi 0x0000
253 #define DAQBOARD2000_TrigAbove 0x0000
254 #define DAQBOARD2000_TrigBelow 0x0004
255 #define DAQBOARD2000_TrigLevelSense 0x0002
256 #define DAQBOARD2000_TrigEdgeSense 0x0000
257 #define DAQBOARD2000_TrigEnable 0x0001
258 #define DAQBOARD2000_TrigDisable 0x0000
260 /* Reference Dac Selection */
261 #define DAQBOARD2000_PosRefDacSelect 0x0100
262 #define DAQBOARD2000_NegRefDacSelect 0x0000
264 struct daq200_boardtype {
268 static const struct daq200_boardtype boardtypes[] = {
269 {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
270 {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
273 struct daqboard2000_private {
279 unsigned int ao_readback[2];
282 static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
284 struct daqboard2000_private *devpriv = dev->private;
287 writew(entry & 0x00ff, devpriv->daq + acqScanListFIFO);
289 writew((entry >> 8) & 0x00ff, devpriv->daq + acqScanListFIFO);
292 static void setup_sampling(struct comedi_device *dev, int chan, int gain)
294 u16 word0, word1, word2, word3;
296 /* Channel 0-7 diff, channel 8-23 single ended */
298 word1 = 0x0004; /* Last scan */
299 word2 = (chan << 6) & 0x00c0;
324 dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
325 dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
327 /* These should be read from EEPROM */
330 writeAcqScanListEntry(dev, word0);
331 writeAcqScanListEntry(dev, word1);
332 writeAcqScanListEntry(dev, word2);
333 writeAcqScanListEntry(dev, word3);
336 static int daqboard2000_ai_insn_read(struct comedi_device *dev,
337 struct comedi_subdevice *s,
338 struct comedi_insn *insn,
341 struct daqboard2000_private *devpriv = dev->private;
343 int gain, chan, timeout;
346 writew(DAQBOARD2000_AcqResetScanListFifo |
347 DAQBOARD2000_AcqResetResultsFifo |
348 DAQBOARD2000_AcqResetConfigPipe, devpriv->daq + acqControl);
351 * If pacer clock is not set to some high value (> 10 us), we
352 * risk multiple samples to be put into the result FIFO.
354 /* 1 second, should be long enough */
355 writel(1000000, devpriv->daq + acqPacerClockDivLow);
356 writew(0, devpriv->daq + acqPacerClockDivHigh);
358 gain = CR_RANGE(insn->chanspec);
359 chan = CR_CHAN(insn->chanspec);
361 /* This doesn't look efficient. I decided to take the conservative
362 * approach when I did the insn conversion. Perhaps it would be
363 * better to have broken it completely, then someone would have been
364 * forced to fix it. --ds */
365 for (i = 0; i < insn->n; i++) {
366 setup_sampling(dev, chan, gain);
367 /* Enable reading from the scanlist FIFO */
368 writew(DAQBOARD2000_SeqStartScanList,
369 devpriv->daq + acqControl);
370 for (timeout = 0; timeout < 20; timeout++) {
371 val = readw(devpriv->daq + acqControl);
372 if (val & DAQBOARD2000_AcqConfigPipeFull)
376 writew(DAQBOARD2000_AdcPacerEnable, devpriv->daq + acqControl);
377 for (timeout = 0; timeout < 20; timeout++) {
378 val = readw(devpriv->daq + acqControl);
379 if (val & DAQBOARD2000_AcqLogicScanning)
383 for (timeout = 0; timeout < 20; timeout++) {
384 val = readw(devpriv->daq + acqControl);
385 if (val & DAQBOARD2000_AcqResultsFIFOHasValidData)
389 data[i] = readw(devpriv->daq + acqResultsFIFO);
390 writew(DAQBOARD2000_AdcPacerDisable, devpriv->daq + acqControl);
391 writew(DAQBOARD2000_SeqStopScanList, devpriv->daq + acqControl);
397 static int daqboard2000_ao_insn_read(struct comedi_device *dev,
398 struct comedi_subdevice *s,
399 struct comedi_insn *insn,
402 struct daqboard2000_private *devpriv = dev->private;
403 int chan = CR_CHAN(insn->chanspec);
406 for (i = 0; i < insn->n; i++)
407 data[i] = devpriv->ao_readback[chan];
412 static int daqboard2000_ao_insn_write(struct comedi_device *dev,
413 struct comedi_subdevice *s,
414 struct comedi_insn *insn,
417 struct daqboard2000_private *devpriv = dev->private;
418 int chan = CR_CHAN(insn->chanspec);
423 for (i = 0; i < insn->n; i++) {
426 * OK, since it works OK without enabling the DAC's,
427 * let's keep it as simple as possible...
429 writew((chan + 2) * 0x0010 | 0x0001,
430 devpriv->daq + dacControl);
433 writew(data[i], devpriv->daq + dacSetting(chan));
434 for (timeout = 0; timeout < 20; timeout++) {
435 val = readw(devpriv->daq + dacControl);
436 if ((val & ((chan + 1) * 0x0010)) == 0)
440 devpriv->ao_readback[chan] = data[i];
443 * Since we never enabled the DAC's, we don't need
446 writew((chan + 2) * 0x0010 | 0x0000,
447 devpriv->daq + dacControl);
455 static void daqboard2000_resetLocalBus(struct comedi_device *dev)
457 struct daqboard2000_private *devpriv = dev->private;
459 writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
461 writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
465 static void daqboard2000_reloadPLX(struct comedi_device *dev)
467 struct daqboard2000_private *devpriv = dev->private;
469 writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
471 writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
473 writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
477 static void daqboard2000_pulseProgPin(struct comedi_device *dev)
479 struct daqboard2000_private *devpriv = dev->private;
481 writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
483 writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
484 mdelay(10); /* Not in the original code, but I like symmetry... */
487 static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
489 struct daqboard2000_private *devpriv = dev->private;
494 /* timeout after 50 tries -> 5ms */
495 for (i = 0; i < 50; i++) {
496 cpld = readw(devpriv->daq + 0x1000);
497 if ((cpld & mask) == mask) {
507 static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
509 struct daqboard2000_private *devpriv = dev->private;
513 writew(data, devpriv->daq + 0x1000);
514 if ((readw(devpriv->daq + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
515 DAQBOARD2000_CPLD_INIT) {
521 static int initialize_daqboard2000(struct comedi_device *dev,
522 const u8 *cpld_array, size_t len,
523 unsigned long context)
525 struct daqboard2000_private *devpriv = dev->private;
527 /* Read the serial EEPROM control register */
532 /* Check to make sure the serial eeprom is present on the board */
533 secr = readl(devpriv->plx + 0x6c);
534 if (!(secr & DAQBOARD2000_EEPROM_PRESENT))
537 for (retry = 0; retry < 3; retry++) {
538 daqboard2000_resetLocalBus(dev);
539 daqboard2000_reloadPLX(dev);
540 daqboard2000_pulseProgPin(dev);
541 if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
542 for (i = 0; i < len; i++) {
543 if (cpld_array[i] == 0xff &&
544 cpld_array[i + 1] == 0x20)
547 for (; i < len; i += 2) {
549 (cpld_array[i] << 8) + cpld_array[i + 1];
550 if (!daqboard2000_writeCPLD(dev, data))
554 daqboard2000_resetLocalBus(dev);
555 daqboard2000_reloadPLX(dev);
564 static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
568 static void daqboard2000_adcDisarm(struct comedi_device *dev)
570 struct daqboard2000_private *devpriv = dev->private;
572 /* Disable hardware triggers */
574 writew(DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable,
575 devpriv->daq + trigControl);
577 writew(DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable,
578 devpriv->daq + trigControl);
580 /* Stop the scan list FIFO from loading the configuration pipe */
582 writew(DAQBOARD2000_SeqStopScanList, devpriv->daq + acqControl);
584 /* Stop the pacer clock */
586 writew(DAQBOARD2000_AdcPacerDisable, devpriv->daq + acqControl);
588 /* Stop the input dma (abort channel 1) */
589 daqboard2000_adcStopDmaTransfer(dev);
592 static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
594 struct daqboard2000_private *devpriv = dev->private;
598 /* Set the + reference dac value in the FPGA */
599 writew(0x80 | DAQBOARD2000_PosRefDacSelect, devpriv->daq + refDacs);
600 for (timeout = 0; timeout < 20; timeout++) {
601 val = readw(devpriv->daq + dacControl);
602 if ((val & DAQBOARD2000_RefBusy) == 0)
607 /* Set the - reference dac value in the FPGA */
608 writew(0x80 | DAQBOARD2000_NegRefDacSelect, devpriv->daq + refDacs);
609 for (timeout = 0; timeout < 20; timeout++) {
610 val = readw(devpriv->daq + dacControl);
611 if ((val & DAQBOARD2000_RefBusy) == 0)
617 static void daqboard2000_initializeCtrs(struct comedi_device *dev)
621 static void daqboard2000_initializeTmrs(struct comedi_device *dev)
625 static void daqboard2000_dacDisarm(struct comedi_device *dev)
629 static void daqboard2000_initializeAdc(struct comedi_device *dev)
631 daqboard2000_adcDisarm(dev);
632 daqboard2000_activateReferenceDacs(dev);
633 daqboard2000_initializeCtrs(dev);
634 daqboard2000_initializeTmrs(dev);
637 static void daqboard2000_initializeDac(struct comedi_device *dev)
639 daqboard2000_dacDisarm(dev);
642 static int daqboard2000_8255_cb(int dir, int port, int data,
643 unsigned long ioaddr)
645 void __iomem *mmio_base = (void __iomem *)ioaddr;
648 writew(data, mmio_base + port * 2);
651 return readw(mmio_base + port * 2);
655 static const void *daqboard2000_find_boardinfo(struct comedi_device *dev,
656 struct pci_dev *pcidev)
658 const struct daq200_boardtype *board;
661 if (pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
664 for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
665 board = &boardtypes[i];
666 if (pcidev->subsystem_device == board->id)
672 static int daqboard2000_auto_attach(struct comedi_device *dev,
673 unsigned long context_unused)
675 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
676 const struct daq200_boardtype *board;
677 struct daqboard2000_private *devpriv;
678 struct comedi_subdevice *s;
681 board = daqboard2000_find_boardinfo(dev, pcidev);
684 dev->board_ptr = board;
685 dev->board_name = board->name;
687 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
691 result = comedi_pci_enable(dev);
695 devpriv->plx = pci_ioremap_bar(pcidev, 0);
696 devpriv->daq = pci_ioremap_bar(pcidev, 2);
697 if (!devpriv->plx || !devpriv->daq)
700 result = comedi_alloc_subdevices(dev, 3);
704 readl(devpriv->plx + 0x6c);
706 result = comedi_load_firmware(dev, &comedi_to_pci_dev(dev)->dev,
707 DAQBOARD2000_FIRMWARE,
708 initialize_daqboard2000, 0);
712 daqboard2000_initializeAdc(dev);
713 daqboard2000_initializeDac(dev);
715 s = &dev->subdevices[0];
717 s->type = COMEDI_SUBD_AI;
718 s->subdev_flags = SDF_READABLE | SDF_GROUND;
721 s->insn_read = daqboard2000_ai_insn_read;
722 s->range_table = &range_daqboard2000_ai;
724 s = &dev->subdevices[1];
726 s->type = COMEDI_SUBD_AO;
727 s->subdev_flags = SDF_WRITABLE;
730 s->insn_read = daqboard2000_ao_insn_read;
731 s->insn_write = daqboard2000_ao_insn_write;
732 s->range_table = &range_bipolar10;
734 s = &dev->subdevices[2];
735 result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
736 (unsigned long)(devpriv->daq + dioP2ExpansionIO8Bit));
740 dev_info(dev->class_dev, "%s: %s attached\n",
741 dev->driver->driver_name, dev->board_name);
746 static void daqboard2000_detach(struct comedi_device *dev)
748 struct daqboard2000_private *devpriv = dev->private;
751 free_irq(dev->irq, dev);
754 iounmap(devpriv->daq);
756 iounmap(devpriv->plx);
758 comedi_pci_disable(dev);
761 static struct comedi_driver daqboard2000_driver = {
762 .driver_name = "daqboard2000",
763 .module = THIS_MODULE,
764 .auto_attach = daqboard2000_auto_attach,
765 .detach = daqboard2000_detach,
768 static int daqboard2000_pci_probe(struct pci_dev *dev,
769 const struct pci_device_id *id)
771 return comedi_pci_auto_config(dev, &daqboard2000_driver,
775 static DEFINE_PCI_DEVICE_TABLE(daqboard2000_pci_table) = {
776 { PCI_DEVICE(PCI_VENDOR_ID_IOTECH, 0x0409) },
779 MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
781 static struct pci_driver daqboard2000_pci_driver = {
782 .name = "daqboard2000",
783 .id_table = daqboard2000_pci_table,
784 .probe = daqboard2000_pci_probe,
785 .remove = comedi_pci_auto_unconfig,
787 module_comedi_pci_driver(daqboard2000_driver, daqboard2000_pci_driver);
789 MODULE_AUTHOR("Comedi http://www.comedi.org");
790 MODULE_DESCRIPTION("Comedi low-level driver");
791 MODULE_LICENSE("GPL");
792 MODULE_FIRMWARE(DAQBOARD2000_FIRMWARE);