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 #include "comedi_fc.h"
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 static void dt3k_ai_empty_fifo(struct comedi_device *dev,
259 struct comedi_subdevice *s);
260 static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *arg,
261 unsigned int round_mode);
262 static int dt3k_ai_cancel(struct comedi_device *dev,
263 struct comedi_subdevice *s);
265 static void debug_intr_flags(unsigned int flags);
270 static int dt3k_send_cmd(struct comedi_device *dev, unsigned int cmd)
272 struct dt3k_private *devpriv = dev->private;
274 unsigned int status = 0;
276 writew(cmd, devpriv->io_addr + DPR_Command_Mbx);
278 for (i = 0; i < TIMEOUT; i++) {
279 status = readw(devpriv->io_addr + DPR_Command_Mbx);
280 if ((status & DT3000_COMPLETION_MASK) != DT3000_NOTPROCESSED)
284 if ((status & DT3000_COMPLETION_MASK) == DT3000_NOERROR)
287 dev_dbg(dev->class_dev, "dt3k_send_cmd() timeout/error status=0x%04x\n",
293 static unsigned int dt3k_readsingle(struct comedi_device *dev,
294 unsigned int subsys, unsigned int chan,
297 struct dt3k_private *devpriv = dev->private;
299 writew(subsys, devpriv->io_addr + DPR_SubSys);
301 writew(chan, devpriv->io_addr + DPR_Params(0));
302 writew(gain, devpriv->io_addr + DPR_Params(1));
304 dt3k_send_cmd(dev, CMD_READSINGLE);
306 return readw(devpriv->io_addr + DPR_Params(2));
309 static void dt3k_writesingle(struct comedi_device *dev, unsigned int subsys,
310 unsigned int chan, unsigned int data)
312 struct dt3k_private *devpriv = dev->private;
314 writew(subsys, devpriv->io_addr + DPR_SubSys);
316 writew(chan, devpriv->io_addr + DPR_Params(0));
317 writew(0, devpriv->io_addr + DPR_Params(1));
318 writew(data, devpriv->io_addr + DPR_Params(2));
320 dt3k_send_cmd(dev, CMD_WRITESINGLE);
323 static int debug_n_ints;
325 /* FIXME! Assumes shared interrupt is for this card. */
326 /* What's this debug_n_ints stuff? Obviously needs some work... */
327 static irqreturn_t dt3k_interrupt(int irq, void *d)
329 struct comedi_device *dev = d;
330 struct dt3k_private *devpriv = dev->private;
331 struct comedi_subdevice *s;
337 s = &dev->subdevices[0];
338 status = readw(devpriv->io_addr + DPR_Intr_Flag);
340 debug_intr_flags(status);
343 if (status & DT3000_ADFULL) {
344 dt3k_ai_empty_fifo(dev, s);
345 s->async->events |= COMEDI_CB_BLOCK;
348 if (status & (DT3000_ADSWERR | DT3000_ADHWERR))
349 s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
352 if (debug_n_ints >= 10) {
353 dt3k_ai_cancel(dev, s);
354 s->async->events |= COMEDI_CB_EOA;
357 comedi_event(dev, s);
362 static char *intr_flags[] = {
363 "AdFull", "AdSwError", "AdHwError", "DaEmpty",
364 "DaSwError", "DaHwError", "CtDone", "CmDone",
367 static void debug_intr_flags(unsigned int flags)
370 printk(KERN_DEBUG "dt3k: intr_flags:");
371 for (i = 0; i < 8; i++) {
372 if (flags & (1 << i))
373 printk(KERN_CONT " %s", intr_flags[i]);
375 printk(KERN_CONT "\n");
379 static void dt3k_ai_empty_fifo(struct comedi_device *dev,
380 struct comedi_subdevice *s)
382 struct dt3k_private *devpriv = dev->private;
389 front = readw(devpriv->io_addr + DPR_AD_Buf_Front);
390 count = front - devpriv->ai_front;
392 count += AI_FIFO_DEPTH;
394 dev_dbg(dev->class_dev, "reading %d samples\n", count);
396 rear = devpriv->ai_rear;
398 for (i = 0; i < count; i++) {
399 data = readw(devpriv->io_addr + DPR_ADC_buffer + rear);
400 comedi_buf_put(s->async, data);
402 if (rear >= AI_FIFO_DEPTH)
406 devpriv->ai_rear = rear;
407 writew(rear, devpriv->io_addr + DPR_AD_Buf_Rear);
410 static int dt3k_ai_cmdtest(struct comedi_device *dev,
411 struct comedi_subdevice *s, struct comedi_cmd *cmd)
416 /* Step 1 : check if triggers are trivially valid */
418 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
419 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
420 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
421 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
422 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
427 /* Step 2a : make sure trigger sources are unique */
428 /* Step 2b : and mutually compatible */
433 /* step 3: make sure arguments are trivially compatible */
435 if (cmd->start_arg != 0) {
440 if (cmd->scan_begin_src == TRIG_TIMER) {
441 if (cmd->scan_begin_arg < this_board->ai_speed) {
442 cmd->scan_begin_arg = this_board->ai_speed;
445 if (cmd->scan_begin_arg > 100 * 16 * 65535) {
446 cmd->scan_begin_arg = 100 * 16 * 65535;
452 if (cmd->convert_src == TRIG_TIMER) {
453 if (cmd->convert_arg < this_board->ai_speed) {
454 cmd->convert_arg = this_board->ai_speed;
457 if (cmd->convert_arg > 50 * 16 * 65535) {
458 cmd->convert_arg = 50 * 16 * 65535;
465 if (cmd->scan_end_arg != cmd->chanlist_len) {
466 cmd->scan_end_arg = cmd->chanlist_len;
469 if (cmd->stop_src == TRIG_COUNT) {
470 if (cmd->stop_arg > 0x00ffffff) {
471 cmd->stop_arg = 0x00ffffff;
476 if (cmd->stop_arg != 0) {
485 /* step 4: fix up any arguments */
487 if (cmd->scan_begin_src == TRIG_TIMER) {
488 tmp = cmd->scan_begin_arg;
489 dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
490 cmd->flags & TRIG_ROUND_MASK);
491 if (tmp != cmd->scan_begin_arg)
496 if (cmd->convert_src == TRIG_TIMER) {
497 tmp = cmd->convert_arg;
498 dt3k_ns_to_timer(50, &cmd->convert_arg,
499 cmd->flags & TRIG_ROUND_MASK);
500 if (tmp != cmd->convert_arg)
502 if (cmd->scan_begin_src == TRIG_TIMER &&
503 cmd->scan_begin_arg <
504 cmd->convert_arg * cmd->scan_end_arg) {
505 cmd->scan_begin_arg =
506 cmd->convert_arg * cmd->scan_end_arg;
519 static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec,
520 unsigned int round_mode)
522 int divider, base, prescale;
524 /* This function needs improvment */
525 /* Don't know if divider==0 works. */
527 for (prescale = 0; prescale < 16; prescale++) {
528 base = timer_base * (prescale + 1);
529 switch (round_mode) {
530 case TRIG_ROUND_NEAREST:
532 divider = (*nanosec + base / 2) / base;
534 case TRIG_ROUND_DOWN:
535 divider = (*nanosec) / base;
538 divider = (*nanosec) / base;
541 if (divider < 65536) {
542 *nanosec = divider * base;
543 return (prescale << 16) | (divider);
548 base = timer_base * (1 << prescale);
550 *nanosec = divider * base;
551 return (prescale << 16) | (divider);
554 static int dt3k_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
556 struct dt3k_private *devpriv = dev->private;
557 struct comedi_cmd *cmd = &s->async->cmd;
559 unsigned int chan, range, aref;
560 unsigned int divider;
561 unsigned int tscandiv;
565 dev_dbg(dev->class_dev, "dt3k_ai_cmd:\n");
566 for (i = 0; i < cmd->chanlist_len; i++) {
567 chan = CR_CHAN(cmd->chanlist[i]);
568 range = CR_RANGE(cmd->chanlist[i]);
570 writew((range << 6) | chan,
571 devpriv->io_addr + DPR_ADC_buffer + i);
573 aref = CR_AREF(cmd->chanlist[0]);
575 writew(cmd->scan_end_arg, devpriv->io_addr + DPR_Params(0));
576 dev_dbg(dev->class_dev, "param[0]=0x%04x\n", cmd->scan_end_arg);
578 if (cmd->convert_src == TRIG_TIMER) {
579 divider = dt3k_ns_to_timer(50, &cmd->convert_arg,
580 cmd->flags & TRIG_ROUND_MASK);
581 writew((divider >> 16), devpriv->io_addr + DPR_Params(1));
582 dev_dbg(dev->class_dev, "param[1]=0x%04x\n", divider >> 16);
583 writew((divider & 0xffff), devpriv->io_addr + DPR_Params(2));
584 dev_dbg(dev->class_dev, "param[2]=0x%04x\n", divider & 0xffff);
589 if (cmd->scan_begin_src == TRIG_TIMER) {
590 tscandiv = dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
591 cmd->flags & TRIG_ROUND_MASK);
592 writew((tscandiv >> 16), devpriv->io_addr + DPR_Params(3));
593 dev_dbg(dev->class_dev, "param[3]=0x%04x\n", tscandiv >> 16);
594 writew((tscandiv & 0xffff), devpriv->io_addr + DPR_Params(4));
595 dev_dbg(dev->class_dev, "param[4]=0x%04x\n", tscandiv & 0xffff);
600 mode = DT3000_AD_RETRIG_INTERNAL | 0 | 0;
601 writew(mode, devpriv->io_addr + DPR_Params(5));
602 dev_dbg(dev->class_dev, "param[5]=0x%04x\n", mode);
603 writew(aref == AREF_DIFF, devpriv->io_addr + DPR_Params(6));
604 dev_dbg(dev->class_dev, "param[6]=0x%04x\n", aref == AREF_DIFF);
606 writew(AI_FIFO_DEPTH / 2, devpriv->io_addr + DPR_Params(7));
607 dev_dbg(dev->class_dev, "param[7]=0x%04x\n", AI_FIFO_DEPTH / 2);
609 writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
610 ret = dt3k_send_cmd(dev, CMD_CONFIG);
612 writew(DT3000_ADFULL | DT3000_ADSWERR | DT3000_ADHWERR,
613 devpriv->io_addr + DPR_Int_Mask);
617 writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
618 ret = dt3k_send_cmd(dev, CMD_START);
623 static int dt3k_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
625 struct dt3k_private *devpriv = dev->private;
628 writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
629 ret = dt3k_send_cmd(dev, CMD_STOP);
631 writew(0, devpriv->io_addr + DPR_Int_Mask);
636 static int dt3k_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
637 struct comedi_insn *insn, unsigned int *data)
640 unsigned int chan, gain, aref;
642 chan = CR_CHAN(insn->chanspec);
643 gain = CR_RANGE(insn->chanspec);
644 /* XXX docs don't explain how to select aref */
645 aref = CR_AREF(insn->chanspec);
647 for (i = 0; i < insn->n; i++)
648 data[i] = dt3k_readsingle(dev, SUBS_AI, chan, gain);
653 static int dt3k_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
654 struct comedi_insn *insn, unsigned int *data)
656 struct dt3k_private *devpriv = dev->private;
660 chan = CR_CHAN(insn->chanspec);
661 for (i = 0; i < insn->n; i++) {
662 dt3k_writesingle(dev, SUBS_AO, chan, data[i]);
663 devpriv->ao_readback[chan] = data[i];
669 static int dt3k_ao_insn_read(struct comedi_device *dev,
670 struct comedi_subdevice *s,
671 struct comedi_insn *insn, unsigned int *data)
673 struct dt3k_private *devpriv = dev->private;
677 chan = CR_CHAN(insn->chanspec);
678 for (i = 0; i < insn->n; i++)
679 data[i] = devpriv->ao_readback[chan];
684 static void dt3k_dio_config(struct comedi_device *dev, int bits)
686 struct dt3k_private *devpriv = dev->private;
689 writew(SUBS_DOUT, devpriv->io_addr + DPR_SubSys);
691 writew(bits, devpriv->io_addr + DPR_Params(0));
694 writew(0, devpriv->io_addr + DPR_Params(1));
695 writew(0, devpriv->io_addr + DPR_Params(2));
698 dt3k_send_cmd(dev, CMD_CONFIG);
701 static int dt3k_dio_insn_config(struct comedi_device *dev,
702 struct comedi_subdevice *s,
703 struct comedi_insn *insn, unsigned int *data)
707 mask = (CR_CHAN(insn->chanspec) < 4) ? 0x0f : 0xf0;
710 case INSN_CONFIG_DIO_OUTPUT:
713 case INSN_CONFIG_DIO_INPUT:
716 case INSN_CONFIG_DIO_QUERY:
719 io_bits & (1 << CR_CHAN(insn->chanspec))) ? COMEDI_OUTPUT :
727 mask = (s->io_bits & 0x01) | ((s->io_bits & 0x10) >> 3);
728 dt3k_dio_config(dev, mask);
733 static int dt3k_dio_insn_bits(struct comedi_device *dev,
734 struct comedi_subdevice *s,
735 struct comedi_insn *insn, unsigned int *data)
738 s->state &= ~data[0];
739 s->state |= data[1] & data[0];
740 dt3k_writesingle(dev, SUBS_DOUT, 0, s->state);
742 data[1] = dt3k_readsingle(dev, SUBS_DIN, 0, 0);
747 static int dt3k_mem_insn_read(struct comedi_device *dev,
748 struct comedi_subdevice *s,
749 struct comedi_insn *insn, unsigned int *data)
751 struct dt3k_private *devpriv = dev->private;
752 unsigned int addr = CR_CHAN(insn->chanspec);
755 for (i = 0; i < insn->n; i++) {
756 writew(SUBS_MEM, devpriv->io_addr + DPR_SubSys);
757 writew(addr, devpriv->io_addr + DPR_Params(0));
758 writew(1, devpriv->io_addr + DPR_Params(1));
760 dt3k_send_cmd(dev, CMD_READCODE);
762 data[i] = readw(devpriv->io_addr + DPR_Params(2));
768 static struct pci_dev *dt3000_find_pci_dev(struct comedi_device *dev,
769 struct comedi_devconfig *it)
771 struct pci_dev *pcidev = NULL;
772 int bus = it->options[0];
773 int slot = it->options[1];
776 for_each_pci_dev(pcidev) {
778 if (bus != pcidev->bus->number ||
779 slot != PCI_SLOT(pcidev->devfn))
782 if (pcidev->vendor != PCI_VENDOR_ID_DT)
784 for (i = 0; i < ARRAY_SIZE(dt3k_boardtypes); i++) {
785 if (dt3k_boardtypes[i].device_id != pcidev->device)
787 dev->board_ptr = dt3k_boardtypes + i;
791 dev_err(dev->class_dev,
792 "No supported board found! (req. bus %d, slot %d)\n",
797 static int dt3000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
799 struct dt3k_private *devpriv;
800 struct pci_dev *pcidev;
801 struct comedi_subdevice *s;
802 resource_size_t pci_base;
805 dev_dbg(dev->class_dev, "dt3000:\n");
807 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
810 dev->private = devpriv;
812 pcidev = dt3000_find_pci_dev(dev, it);
815 comedi_set_hw_dev(dev, &pcidev->dev);
817 ret = comedi_pci_enable(pcidev, "dt3000");
820 dev->iobase = 1; /* the "detach" needs this */
822 pci_base = pci_resource_start(pcidev, 0);
823 devpriv->io_addr = ioremap(pci_base, DT3000_SIZE);
824 if (!devpriv->io_addr)
827 dev->board_name = this_board->name;
829 if (request_irq(pcidev->irq, dt3k_interrupt, IRQF_SHARED,
831 dev_err(dev->class_dev, "unable to allocate IRQ %u\n",
835 dev->irq = pcidev->irq;
837 ret = comedi_alloc_subdevices(dev, 4);
841 s = &dev->subdevices[0];
842 dev->read_subdev = s;
845 s->type = COMEDI_SUBD_AI;
846 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
847 s->n_chan = this_board->adchan;
848 s->insn_read = dt3k_ai_insn;
849 s->maxdata = (1 << this_board->adbits) - 1;
850 s->len_chanlist = 512;
851 s->range_table = &range_dt3000_ai; /* XXX */
852 s->do_cmd = dt3k_ai_cmd;
853 s->do_cmdtest = dt3k_ai_cmdtest;
854 s->cancel = dt3k_ai_cancel;
856 s = &dev->subdevices[1];
858 s->type = COMEDI_SUBD_AO;
859 s->subdev_flags = SDF_WRITABLE;
861 s->insn_read = dt3k_ao_insn_read;
862 s->insn_write = dt3k_ao_insn;
863 s->maxdata = (1 << this_board->dabits) - 1;
865 s->range_table = &range_bipolar10;
867 s = &dev->subdevices[2];
869 s->type = COMEDI_SUBD_DIO;
870 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
872 s->insn_config = dt3k_dio_insn_config;
873 s->insn_bits = dt3k_dio_insn_bits;
876 s->range_table = &range_digital;
878 s = &dev->subdevices[3];
880 s->type = COMEDI_SUBD_MEMORY;
881 s->subdev_flags = SDF_READABLE;
883 s->insn_read = dt3k_mem_insn_read;
886 s->range_table = &range_unknown;
889 s = &dev->subdevices[4];
891 s->type = COMEDI_SUBD_PROC;
897 static void dt3000_detach(struct comedi_device *dev)
899 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
900 struct dt3k_private *devpriv = dev->private;
903 free_irq(dev->irq, dev);
905 if (devpriv->io_addr)
906 iounmap(devpriv->io_addr);
910 comedi_pci_disable(pcidev);
915 static struct comedi_driver dt3000_driver = {
916 .driver_name = "dt3000",
917 .module = THIS_MODULE,
918 .attach = dt3000_attach,
919 .detach = dt3000_detach,
922 static int __devinit dt3000_pci_probe(struct pci_dev *dev,
923 const struct pci_device_id *ent)
925 return comedi_pci_auto_config(dev, &dt3000_driver);
928 static void __devexit dt3000_pci_remove(struct pci_dev *dev)
930 comedi_pci_auto_unconfig(dev);
933 static DEFINE_PCI_DEVICE_TABLE(dt3000_pci_table) = {
934 { PCI_DEVICE(PCI_VENDOR_ID_DT, 0x0022) },
935 { PCI_DEVICE(PCI_VENDOR_ID_DT, 0x0027) },
936 { PCI_DEVICE(PCI_VENDOR_ID_DT, 0x0023) },
937 { PCI_DEVICE(PCI_VENDOR_ID_DT, 0x0024) },
938 { PCI_DEVICE(PCI_VENDOR_ID_DT, 0x0028) },
939 { PCI_DEVICE(PCI_VENDOR_ID_DT, 0x0025) },
940 { PCI_DEVICE(PCI_VENDOR_ID_DT, 0x0026) },
943 MODULE_DEVICE_TABLE(pci, dt3000_pci_table);
945 static struct pci_driver dt3000_pci_driver = {
947 .id_table = dt3000_pci_table,
948 .probe = dt3000_pci_probe,
949 .remove = __devexit_p(dt3000_pci_remove),
951 module_comedi_pci_driver(dt3000_driver, dt3000_pci_driver);
953 MODULE_AUTHOR("Comedi http://www.comedi.org");
954 MODULE_DESCRIPTION("Comedi low-level driver");
955 MODULE_LICENSE("GPL");