2 comedi/drivers/dt3000.c
3 Data Translation DT3000 series driver
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 1999 David A. Schleef <ds@schleef.org>
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: Data Translation DT3000 series
27 Devices: [Data Translation] DT3001 (dt3000), DT3001-PGL, DT3002, DT3003,
28 DT3003-PGL, DT3004, DT3005, DT3004-200
29 Updated: Mon, 14 Apr 2008 15:41:24 +0100
32 Configuration Options:
33 [0] - PCI bus of device (optional)
34 [1] - PCI slot of device (optional)
35 If bus/slot is not specified, the first supported
36 PCI device found will be used.
38 There is code to support AI commands, but it may not work.
40 AO commands are not supported.
44 The DT3000 series is Data Translation's attempt to make a PCI
45 data acquisition board. The design of this series is very nice,
46 since each board has an on-board DSP (Texas Instruments TMS320C52).
47 However, a few details are a little annoying. The boards lack
48 bus-mastering DMA, which eliminates them from serious work.
49 They also are not capable of autocalibration, which is a common
50 feature in modern hardware. The default firmware is pretty bad,
51 making it nearly impossible to write an RT compatible driver.
52 It would make an interesting project to write a decent firmware
55 Data Translation originally wanted an NDA for the documentation
56 for the 3k series. However, if you ask nicely, they might send
57 you the docs without one, also.
62 #include <linux/interrupt.h>
63 #include "../comedidev.h"
64 #include <linux/delay.h>
66 #define PCI_VENDOR_ID_DT 0x1116
68 static const struct comedi_lrange range_dt3000_ai = { 4, {
76 static const struct comedi_lrange range_dt3000_ai_pgl = { 4, {
84 struct dt3k_boardtype {
87 unsigned int device_id;
91 const struct comedi_lrange *adrange;
96 static const struct dt3k_boardtype dt3k_boardtypes[] = {
101 .adrange = &range_dt3000_ai,
106 {.name = "dt3001-pgl",
110 .adrange = &range_dt3000_ai_pgl,
119 .adrange = &range_dt3000_ai,
128 .adrange = &range_dt3000_ai,
133 {.name = "dt3003-pgl",
137 .adrange = &range_dt3000_ai_pgl,
146 .adrange = &range_dt3000_ai,
151 {.name = "dt3005", /* a.k.a. 3004-200 */
155 .adrange = &range_dt3000_ai,
162 #define this_board ((const struct dt3k_boardtype *)dev->board_ptr)
164 #define DT3000_SIZE (4*0x1000)
166 /* dual-ported RAM location definitions */
168 #define DPR_DAC_buffer (4*0x000)
169 #define DPR_ADC_buffer (4*0x800)
170 #define DPR_Command (4*0xfd3)
171 #define DPR_SubSys (4*0xfd3)
172 #define DPR_Encode (4*0xfd4)
173 #define DPR_Params(a) (4*(0xfd5+(a)))
174 #define DPR_Tick_Reg_Lo (4*0xff5)
175 #define DPR_Tick_Reg_Hi (4*0xff6)
176 #define DPR_DA_Buf_Front (4*0xff7)
177 #define DPR_DA_Buf_Rear (4*0xff8)
178 #define DPR_AD_Buf_Front (4*0xff9)
179 #define DPR_AD_Buf_Rear (4*0xffa)
180 #define DPR_Int_Mask (4*0xffb)
181 #define DPR_Intr_Flag (4*0xffc)
182 #define DPR_Response_Mbx (4*0xffe)
183 #define DPR_Command_Mbx (4*0xfff)
185 #define AI_FIFO_DEPTH 2003
186 #define AO_FIFO_DEPTH 2048
190 #define CMD_GETBRDINFO 0
192 #define CMD_GETCONFIG 2
195 #define CMD_READSINGLE 5
196 #define CMD_WRITESINGLE 6
197 #define CMD_CALCCLOCK 7
198 #define CMD_READEVENTS 8
199 #define CMD_WRITECTCTRL 16
200 #define CMD_READCTCTRL 17
201 #define CMD_WRITECT 18
202 #define CMD_READCT 19
203 #define CMD_WRITEDATA 32
204 #define CMD_READDATA 33
205 #define CMD_WRITEIO 34
206 #define CMD_READIO 35
207 #define CMD_WRITECODE 36
208 #define CMD_READCODE 37
209 #define CMD_EXECUTE 38
219 /* interrupt flags */
220 #define DT3000_CMDONE 0x80
221 #define DT3000_CTDONE 0x40
222 #define DT3000_DAHWERR 0x20
223 #define DT3000_DASWERR 0x10
224 #define DT3000_DAEMPTY 0x08
225 #define DT3000_ADHWERR 0x04
226 #define DT3000_ADSWERR 0x02
227 #define DT3000_ADFULL 0x01
229 #define DT3000_COMPLETION_MASK 0xff00
230 #define DT3000_COMMAND_MASK 0x00ff
231 #define DT3000_NOTPROCESSED 0x0000
232 #define DT3000_NOERROR 0x5500
233 #define DT3000_ERROR 0xaa00
234 #define DT3000_NOTSUPPORTED 0xff00
236 #define DT3000_EXTERNAL_CLOCK 1
237 #define DT3000_RISING_EDGE 2
239 #define TMODE_MASK 0x1c
241 #define DT3000_AD_TRIG_INTERNAL (0<<2)
242 #define DT3000_AD_TRIG_EXTERNAL (1<<2)
243 #define DT3000_AD_RETRIG_INTERNAL (2<<2)
244 #define DT3000_AD_RETRIG_EXTERNAL (3<<2)
245 #define DT3000_AD_EXTRETRIG (4<<2)
247 #define DT3000_CHANNEL_MODE_SE 0
248 #define DT3000_CHANNEL_MODE_DI 1
250 struct dt3k_private {
251 void __iomem *io_addr;
253 unsigned int ao_readback[2];
254 unsigned int ai_front;
255 unsigned int ai_rear;
258 #define devpriv ((struct dt3k_private *)dev->private)
260 static void dt3k_ai_empty_fifo(struct comedi_device *dev,
261 struct comedi_subdevice *s);
262 static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *arg,
263 unsigned int round_mode);
264 static int dt3k_ai_cancel(struct comedi_device *dev,
265 struct comedi_subdevice *s);
267 static void debug_intr_flags(unsigned int flags);
272 static int dt3k_send_cmd(struct comedi_device *dev, unsigned int cmd)
275 unsigned int status = 0;
277 writew(cmd, devpriv->io_addr + DPR_Command_Mbx);
279 for (i = 0; i < TIMEOUT; i++) {
280 status = readw(devpriv->io_addr + DPR_Command_Mbx);
281 if ((status & DT3000_COMPLETION_MASK) != DT3000_NOTPROCESSED)
285 if ((status & DT3000_COMPLETION_MASK) == DT3000_NOERROR)
288 dev_dbg(dev->class_dev, "dt3k_send_cmd() timeout/error status=0x%04x\n",
294 static unsigned int dt3k_readsingle(struct comedi_device *dev,
295 unsigned int subsys, unsigned int chan,
298 writew(subsys, devpriv->io_addr + DPR_SubSys);
300 writew(chan, devpriv->io_addr + DPR_Params(0));
301 writew(gain, devpriv->io_addr + DPR_Params(1));
303 dt3k_send_cmd(dev, CMD_READSINGLE);
305 return readw(devpriv->io_addr + DPR_Params(2));
308 static void dt3k_writesingle(struct comedi_device *dev, unsigned int subsys,
309 unsigned int chan, unsigned int data)
311 writew(subsys, devpriv->io_addr + DPR_SubSys);
313 writew(chan, devpriv->io_addr + DPR_Params(0));
314 writew(0, devpriv->io_addr + DPR_Params(1));
315 writew(data, devpriv->io_addr + DPR_Params(2));
317 dt3k_send_cmd(dev, CMD_WRITESINGLE);
320 static int debug_n_ints;
322 /* FIXME! Assumes shared interrupt is for this card. */
323 /* What's this debug_n_ints stuff? Obviously needs some work... */
324 static irqreturn_t dt3k_interrupt(int irq, void *d)
326 struct comedi_device *dev = d;
327 struct comedi_subdevice *s;
333 s = dev->subdevices + 0;
334 status = readw(devpriv->io_addr + DPR_Intr_Flag);
336 debug_intr_flags(status);
339 if (status & DT3000_ADFULL) {
340 dt3k_ai_empty_fifo(dev, s);
341 s->async->events |= COMEDI_CB_BLOCK;
344 if (status & (DT3000_ADSWERR | DT3000_ADHWERR))
345 s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
348 if (debug_n_ints >= 10) {
349 dt3k_ai_cancel(dev, s);
350 s->async->events |= COMEDI_CB_EOA;
353 comedi_event(dev, s);
358 static char *intr_flags[] = {
359 "AdFull", "AdSwError", "AdHwError", "DaEmpty",
360 "DaSwError", "DaHwError", "CtDone", "CmDone",
363 static void debug_intr_flags(unsigned int flags)
366 printk(KERN_DEBUG "dt3k: intr_flags:");
367 for (i = 0; i < 8; i++) {
368 if (flags & (1 << i))
369 printk(KERN_CONT " %s", intr_flags[i]);
371 printk(KERN_CONT "\n");
375 static void dt3k_ai_empty_fifo(struct comedi_device *dev,
376 struct comedi_subdevice *s)
384 front = readw(devpriv->io_addr + DPR_AD_Buf_Front);
385 count = front - devpriv->ai_front;
387 count += AI_FIFO_DEPTH;
389 dev_dbg(dev->class_dev, "reading %d samples\n", count);
391 rear = devpriv->ai_rear;
393 for (i = 0; i < count; i++) {
394 data = readw(devpriv->io_addr + DPR_ADC_buffer + rear);
395 comedi_buf_put(s->async, data);
397 if (rear >= AI_FIFO_DEPTH)
401 devpriv->ai_rear = rear;
402 writew(rear, devpriv->io_addr + DPR_AD_Buf_Rear);
405 static int dt3k_ai_cmdtest(struct comedi_device *dev,
406 struct comedi_subdevice *s, struct comedi_cmd *cmd)
411 /* step 1: make sure trigger sources are trivially valid */
413 tmp = cmd->start_src;
414 cmd->start_src &= TRIG_NOW;
415 if (!cmd->start_src || tmp != cmd->start_src)
418 tmp = cmd->scan_begin_src;
419 cmd->scan_begin_src &= TRIG_TIMER;
420 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
423 tmp = cmd->convert_src;
424 cmd->convert_src &= TRIG_TIMER;
425 if (!cmd->convert_src || tmp != cmd->convert_src)
428 tmp = cmd->scan_end_src;
429 cmd->scan_end_src &= TRIG_COUNT;
430 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
434 cmd->stop_src &= TRIG_COUNT;
435 if (!cmd->stop_src || tmp != cmd->stop_src)
441 /* step 2: make sure trigger sources are unique and mutually compatible */
446 /* step 3: make sure arguments are trivially compatible */
448 if (cmd->start_arg != 0) {
453 if (cmd->scan_begin_src == TRIG_TIMER) {
454 if (cmd->scan_begin_arg < this_board->ai_speed) {
455 cmd->scan_begin_arg = this_board->ai_speed;
458 if (cmd->scan_begin_arg > 100 * 16 * 65535) {
459 cmd->scan_begin_arg = 100 * 16 * 65535;
465 if (cmd->convert_src == TRIG_TIMER) {
466 if (cmd->convert_arg < this_board->ai_speed) {
467 cmd->convert_arg = this_board->ai_speed;
470 if (cmd->convert_arg > 50 * 16 * 65535) {
471 cmd->convert_arg = 50 * 16 * 65535;
478 if (cmd->scan_end_arg != cmd->chanlist_len) {
479 cmd->scan_end_arg = cmd->chanlist_len;
482 if (cmd->stop_src == TRIG_COUNT) {
483 if (cmd->stop_arg > 0x00ffffff) {
484 cmd->stop_arg = 0x00ffffff;
489 if (cmd->stop_arg != 0) {
498 /* step 4: fix up any arguments */
500 if (cmd->scan_begin_src == TRIG_TIMER) {
501 tmp = cmd->scan_begin_arg;
502 dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
503 cmd->flags & TRIG_ROUND_MASK);
504 if (tmp != cmd->scan_begin_arg)
509 if (cmd->convert_src == TRIG_TIMER) {
510 tmp = cmd->convert_arg;
511 dt3k_ns_to_timer(50, &cmd->convert_arg,
512 cmd->flags & TRIG_ROUND_MASK);
513 if (tmp != cmd->convert_arg)
515 if (cmd->scan_begin_src == TRIG_TIMER &&
516 cmd->scan_begin_arg <
517 cmd->convert_arg * cmd->scan_end_arg) {
518 cmd->scan_begin_arg =
519 cmd->convert_arg * cmd->scan_end_arg;
532 static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec,
533 unsigned int round_mode)
535 int divider, base, prescale;
537 /* This function needs improvment */
538 /* Don't know if divider==0 works. */
540 for (prescale = 0; prescale < 16; prescale++) {
541 base = timer_base * (prescale + 1);
542 switch (round_mode) {
543 case TRIG_ROUND_NEAREST:
545 divider = (*nanosec + base / 2) / base;
547 case TRIG_ROUND_DOWN:
548 divider = (*nanosec) / base;
551 divider = (*nanosec) / base;
554 if (divider < 65536) {
555 *nanosec = divider * base;
556 return (prescale << 16) | (divider);
561 base = timer_base * (1 << prescale);
563 *nanosec = divider * base;
564 return (prescale << 16) | (divider);
567 static int dt3k_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
569 struct comedi_cmd *cmd = &s->async->cmd;
571 unsigned int chan, range, aref;
572 unsigned int divider;
573 unsigned int tscandiv;
577 dev_dbg(dev->class_dev, "dt3k_ai_cmd:\n");
578 for (i = 0; i < cmd->chanlist_len; i++) {
579 chan = CR_CHAN(cmd->chanlist[i]);
580 range = CR_RANGE(cmd->chanlist[i]);
582 writew((range << 6) | chan,
583 devpriv->io_addr + DPR_ADC_buffer + i);
585 aref = CR_AREF(cmd->chanlist[0]);
587 writew(cmd->scan_end_arg, devpriv->io_addr + DPR_Params(0));
588 dev_dbg(dev->class_dev, "param[0]=0x%04x\n", cmd->scan_end_arg);
590 if (cmd->convert_src == TRIG_TIMER) {
591 divider = dt3k_ns_to_timer(50, &cmd->convert_arg,
592 cmd->flags & TRIG_ROUND_MASK);
593 writew((divider >> 16), devpriv->io_addr + DPR_Params(1));
594 dev_dbg(dev->class_dev, "param[1]=0x%04x\n", divider >> 16);
595 writew((divider & 0xffff), devpriv->io_addr + DPR_Params(2));
596 dev_dbg(dev->class_dev, "param[2]=0x%04x\n", divider & 0xffff);
601 if (cmd->scan_begin_src == TRIG_TIMER) {
602 tscandiv = dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
603 cmd->flags & TRIG_ROUND_MASK);
604 writew((tscandiv >> 16), devpriv->io_addr + DPR_Params(3));
605 dev_dbg(dev->class_dev, "param[3]=0x%04x\n", tscandiv >> 16);
606 writew((tscandiv & 0xffff), devpriv->io_addr + DPR_Params(4));
607 dev_dbg(dev->class_dev, "param[4]=0x%04x\n", tscandiv & 0xffff);
612 mode = DT3000_AD_RETRIG_INTERNAL | 0 | 0;
613 writew(mode, devpriv->io_addr + DPR_Params(5));
614 dev_dbg(dev->class_dev, "param[5]=0x%04x\n", mode);
615 writew(aref == AREF_DIFF, devpriv->io_addr + DPR_Params(6));
616 dev_dbg(dev->class_dev, "param[6]=0x%04x\n", aref == AREF_DIFF);
618 writew(AI_FIFO_DEPTH / 2, devpriv->io_addr + DPR_Params(7));
619 dev_dbg(dev->class_dev, "param[7]=0x%04x\n", AI_FIFO_DEPTH / 2);
621 writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
622 ret = dt3k_send_cmd(dev, CMD_CONFIG);
624 writew(DT3000_ADFULL | DT3000_ADSWERR | DT3000_ADHWERR,
625 devpriv->io_addr + DPR_Int_Mask);
629 writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
630 ret = dt3k_send_cmd(dev, CMD_START);
635 static int dt3k_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
639 writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
640 ret = dt3k_send_cmd(dev, CMD_STOP);
642 writew(0, devpriv->io_addr + DPR_Int_Mask);
647 static int dt3k_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
648 struct comedi_insn *insn, unsigned int *data)
651 unsigned int chan, gain, aref;
653 chan = CR_CHAN(insn->chanspec);
654 gain = CR_RANGE(insn->chanspec);
655 /* XXX docs don't explain how to select aref */
656 aref = CR_AREF(insn->chanspec);
658 for (i = 0; i < insn->n; i++)
659 data[i] = dt3k_readsingle(dev, SUBS_AI, chan, gain);
664 static int dt3k_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
665 struct comedi_insn *insn, unsigned int *data)
670 chan = CR_CHAN(insn->chanspec);
671 for (i = 0; i < insn->n; i++) {
672 dt3k_writesingle(dev, SUBS_AO, chan, data[i]);
673 devpriv->ao_readback[chan] = data[i];
679 static int dt3k_ao_insn_read(struct comedi_device *dev,
680 struct comedi_subdevice *s,
681 struct comedi_insn *insn, unsigned int *data)
686 chan = CR_CHAN(insn->chanspec);
687 for (i = 0; i < insn->n; i++)
688 data[i] = devpriv->ao_readback[chan];
693 static void dt3k_dio_config(struct comedi_device *dev, int bits)
696 writew(SUBS_DOUT, devpriv->io_addr + DPR_SubSys);
698 writew(bits, devpriv->io_addr + DPR_Params(0));
701 writew(0, devpriv->io_addr + DPR_Params(1));
702 writew(0, devpriv->io_addr + DPR_Params(2));
705 dt3k_send_cmd(dev, CMD_CONFIG);
708 static int dt3k_dio_insn_config(struct comedi_device *dev,
709 struct comedi_subdevice *s,
710 struct comedi_insn *insn, unsigned int *data)
714 mask = (CR_CHAN(insn->chanspec) < 4) ? 0x0f : 0xf0;
717 case INSN_CONFIG_DIO_OUTPUT:
720 case INSN_CONFIG_DIO_INPUT:
723 case INSN_CONFIG_DIO_QUERY:
726 io_bits & (1 << CR_CHAN(insn->chanspec))) ? COMEDI_OUTPUT :
734 mask = (s->io_bits & 0x01) | ((s->io_bits & 0x10) >> 3);
735 dt3k_dio_config(dev, mask);
740 static int dt3k_dio_insn_bits(struct comedi_device *dev,
741 struct comedi_subdevice *s,
742 struct comedi_insn *insn, unsigned int *data)
745 s->state &= ~data[0];
746 s->state |= data[1] & data[0];
747 dt3k_writesingle(dev, SUBS_DOUT, 0, s->state);
749 data[1] = dt3k_readsingle(dev, SUBS_DIN, 0, 0);
754 static int dt3k_mem_insn_read(struct comedi_device *dev,
755 struct comedi_subdevice *s,
756 struct comedi_insn *insn, unsigned int *data)
758 unsigned int addr = CR_CHAN(insn->chanspec);
761 for (i = 0; i < insn->n; i++) {
762 writew(SUBS_MEM, devpriv->io_addr + DPR_SubSys);
763 writew(addr, devpriv->io_addr + DPR_Params(0));
764 writew(1, devpriv->io_addr + DPR_Params(1));
766 dt3k_send_cmd(dev, CMD_READCODE);
768 data[i] = readw(devpriv->io_addr + DPR_Params(2));
774 static struct pci_dev *dt3000_find_pci_dev(struct comedi_device *dev,
775 struct comedi_devconfig *it)
777 struct pci_dev *pcidev = NULL;
778 int bus = it->options[0];
779 int slot = it->options[1];
782 for_each_pci_dev(pcidev) {
784 if (bus != pcidev->bus->number ||
785 slot != PCI_SLOT(pcidev->devfn))
788 if (pcidev->vendor != PCI_VENDOR_ID_DT)
790 for (i = 0; i < ARRAY_SIZE(dt3k_boardtypes); i++) {
791 if (dt3k_boardtypes[i].device_id != pcidev->device)
793 dev->board_ptr = dt3k_boardtypes + i;
797 dev_err(dev->class_dev,
798 "No supported board found! (req. bus %d, slot %d)\n",
803 static int dt3000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
805 struct pci_dev *pcidev;
806 struct comedi_subdevice *s;
809 dev_dbg(dev->class_dev, "dt3000:\n");
811 ret = alloc_private(dev, sizeof(struct dt3k_private));
815 pcidev = dt3000_find_pci_dev(dev, it);
818 comedi_set_hw_dev(dev, &pcidev->dev);
820 ret = comedi_pci_enable(pcidev, "dt3000");
824 dev->iobase = pci_resource_start(pcidev, 0);
825 devpriv->io_addr = ioremap(dev->iobase, DT3000_SIZE);
826 if (!devpriv->io_addr)
829 dev->board_name = this_board->name;
831 if (request_irq(pcidev->irq, dt3k_interrupt, IRQF_SHARED,
833 dev_err(dev->class_dev, "unable to allocate IRQ %u\n",
837 dev->irq = pcidev->irq;
839 ret = comedi_alloc_subdevices(dev, 4);
844 dev->read_subdev = s;
847 s->type = COMEDI_SUBD_AI;
848 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
849 s->n_chan = this_board->adchan;
850 s->insn_read = dt3k_ai_insn;
851 s->maxdata = (1 << this_board->adbits) - 1;
852 s->len_chanlist = 512;
853 s->range_table = &range_dt3000_ai; /* XXX */
854 s->do_cmd = dt3k_ai_cmd;
855 s->do_cmdtest = dt3k_ai_cmdtest;
856 s->cancel = dt3k_ai_cancel;
860 s->type = COMEDI_SUBD_AO;
861 s->subdev_flags = SDF_WRITABLE;
863 s->insn_read = dt3k_ao_insn_read;
864 s->insn_write = dt3k_ao_insn;
865 s->maxdata = (1 << this_board->dabits) - 1;
867 s->range_table = &range_bipolar10;
871 s->type = COMEDI_SUBD_DIO;
872 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
874 s->insn_config = dt3k_dio_insn_config;
875 s->insn_bits = dt3k_dio_insn_bits;
878 s->range_table = &range_digital;
882 s->type = COMEDI_SUBD_MEMORY;
883 s->subdev_flags = SDF_READABLE;
885 s->insn_read = dt3k_mem_insn_read;
888 s->range_table = &range_unknown;
893 s->type = COMEDI_SUBD_PROC;
899 static void dt3000_detach(struct comedi_device *dev)
901 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
904 free_irq(dev->irq, dev);
906 if (devpriv->io_addr)
907 iounmap(devpriv->io_addr);
911 comedi_pci_disable(pcidev);
916 static struct comedi_driver dt3000_driver = {
917 .driver_name = "dt3000",
918 .module = THIS_MODULE,
919 .attach = dt3000_attach,
920 .detach = dt3000_detach,
923 static int __devinit dt3000_pci_probe(struct pci_dev *dev,
924 const struct pci_device_id *ent)
926 return comedi_pci_auto_config(dev, &dt3000_driver);
929 static void __devexit dt3000_pci_remove(struct pci_dev *dev)
931 comedi_pci_auto_unconfig(dev);
934 static DEFINE_PCI_DEVICE_TABLE(dt3000_pci_table) = {
935 { PCI_DEVICE(PCI_VENDOR_ID_DT, 0x0022) },
936 { PCI_DEVICE(PCI_VENDOR_ID_DT, 0x0027) },
937 { PCI_DEVICE(PCI_VENDOR_ID_DT, 0x0023) },
938 { PCI_DEVICE(PCI_VENDOR_ID_DT, 0x0024) },
939 { PCI_DEVICE(PCI_VENDOR_ID_DT, 0x0028) },
940 { PCI_DEVICE(PCI_VENDOR_ID_DT, 0x0025) },
941 { PCI_DEVICE(PCI_VENDOR_ID_DT, 0x0026) },
944 MODULE_DEVICE_TABLE(pci, dt3000_pci_table);
946 static struct pci_driver dt3000_pci_driver = {
948 .id_table = dt3000_pci_table,
949 .probe = dt3000_pci_probe,
950 .remove = __devexit_p(dt3000_pci_remove),
952 module_comedi_pci_driver(dt3000_driver, dt3000_pci_driver);
954 MODULE_AUTHOR("Comedi http://www.comedi.org");
955 MODULE_DESCRIPTION("Comedi low-level driver");
956 MODULE_LICENSE("GPL");