/* Some comments on the code.. - it shouldn't be necessary to use outb_p(). - ignoreirq creates a race condition. It needs to be fixed. */ /* comedi/drivers/das6402.c An experimental driver for Computerboards' DAS6402 I/O card Copyright (C) 1999 Oystein Svendsen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Driver: das6402 Description: Keithley Metrabyte DAS6402 (& compatibles) Author: Oystein Svendsen Status: bitrotten Devices: [Keithley Metrabyte] DAS6402 (das6402) This driver has suffered bitrot. */ #include #include "../comedidev.h" #include #define DAS6402_SIZE 16 #define N_WORDS (3000*64) #define STOP 0 #define START 1 #define SCANL 0x3f00 #define BYTE unsigned char #define WORD unsigned short /*----- register 8 ----*/ #define CLRINT 0x01 #define CLRXTR 0x02 #define CLRXIN 0x04 #define EXTEND 0x10 #define ARMED 0x20 /* enable conting of post sample conv */ #define POSTMODE 0x40 #define MHZ 0x80 /* 10 MHz clock */ /*---------------------*/ /*----- register 9 ----*/ #define IRQ (0x04 << 4) /* these two are */ #define IRQV 10 /* dependent on each other */ #define CONVSRC 0x03 /* trig src is Intarnal pacer */ #define BURSTEN 0x04 /* enable burst */ #define XINTE 0x08 /* use external int. trig */ #define INTE 0x80 /* enable analog interrupts */ /*---------------------*/ /*----- register 10 ---*/ #define TGEN 0x01 /* Use pin DI1 for externl trigging? */ #define TGSEL 0x02 /* Use edge triggering */ #define TGPOL 0x04 /* active edge is falling */ #define PRETRIG 0x08 /* pretrig */ /*---------------------*/ /*----- register 11 ---*/ #define EOB 0x0c #define FIFOHFULL 0x08 #define GAIN 0x01 #define FIFONEPTY 0x04 #define MODE 0x10 #define SEM 0x20 #define BIP 0x40 /*---------------------*/ #define M0 0x00 #define M2 0x04 #define C0 0x00 #define C1 0x40 #define C2 0x80 #define RWLH 0x30 struct das6402_private { int ai_bytes_to_read; int das6402_ignoreirq; }; static void das6402_ai_fifo_dregs(struct comedi_device *dev, struct comedi_subdevice *s) { while (1) { if (!(inb(dev->iobase + 8) & 0x01)) return; comedi_buf_put(s->async, inw(dev->iobase)); } } static void das6402_setcounter(struct comedi_device *dev) { BYTE p; unsigned short ctrlwrd; /* set up counter0 first, mode 0 */ p = M0 | C0 | RWLH; outb_p(p, dev->iobase + 15); ctrlwrd = 2000; p = (BYTE) (0xff & ctrlwrd); outb_p(p, dev->iobase + 12); p = (BYTE) (0xff & (ctrlwrd >> 8)); outb_p(p, dev->iobase + 12); /* set up counter1, mode 2 */ p = M2 | C1 | RWLH; outb_p(p, dev->iobase + 15); ctrlwrd = 10; p = (BYTE) (0xff & ctrlwrd); outb_p(p, dev->iobase + 13); p = (BYTE) (0xff & (ctrlwrd >> 8)); outb_p(p, dev->iobase + 13); /* set up counter1, mode 2 */ p = M2 | C2 | RWLH; outb_p(p, dev->iobase + 15); ctrlwrd = 1000; p = (BYTE) (0xff & ctrlwrd); outb_p(p, dev->iobase + 14); p = (BYTE) (0xff & (ctrlwrd >> 8)); outb_p(p, dev->iobase + 14); } static irqreturn_t intr_handler(int irq, void *d) { struct comedi_device *dev = d; struct das6402_private *devpriv = dev->private; struct comedi_subdevice *s = &dev->subdevices[0]; if (!dev->attached || devpriv->das6402_ignoreirq) { dev_warn(dev->class_dev, "BUG: spurious interrupt\n"); return IRQ_HANDLED; } #ifdef DEBUG printk("das6402: interrupt! das6402_irqcount=%i\n", devpriv->das6402_irqcount); printk("das6402: iobase+2=%i\n", inw_p(dev->iobase + 2)); #endif das6402_ai_fifo_dregs(dev, s); if (s->async->buf_write_count >= devpriv->ai_bytes_to_read) { outw_p(SCANL, dev->iobase + 2); /* clears the fifo */ outb(0x07, dev->iobase + 8); /* clears all flip-flops */ #ifdef DEBUG printk("das6402: Got %i samples\n\n", devpriv->das6402_wordsread - diff); #endif s->async->events |= COMEDI_CB_EOA; comedi_event(dev, s); } outb(0x01, dev->iobase + 8); /* clear only the interrupt flip-flop */ comedi_event(dev, s); return IRQ_HANDLED; } #if 0 static void das6402_ai_fifo_read(struct comedi_device *dev, short *data, int n) { int i; for (i = 0; i < n; i++) data[i] = inw(dev->iobase); } #endif static int das6402_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s) { struct das6402_private *devpriv = dev->private; /* * This function should reset the board from whatever condition it * is in (i.e., acquiring data), to a non-active state. */ devpriv->das6402_ignoreirq = 1; dev_dbg(dev->class_dev, "Stopping acquisition\n"); devpriv->das6402_ignoreirq = 1; outb_p(0x02, dev->iobase + 10); /* disable external trigging */ outw_p(SCANL, dev->iobase + 2); /* resets the card fifo */ outb_p(0, dev->iobase + 9); /* disables interrupts */ outw_p(SCANL, dev->iobase + 2); return 0; } #ifdef unused static int das6402_ai_mode2(struct comedi_device *dev, struct comedi_subdevice *s, comedi_trig * it) { struct das6402_private *devpriv = dev->private; devpriv->das6402_ignoreirq = 1; dev_dbg(dev->class_dev, "Starting acquisition\n"); outb_p(0x03, dev->iobase + 10); /* enable external trigging */ outw_p(SCANL, dev->iobase + 2); /* resets the card fifo */ outb_p(IRQ | CONVSRC | BURSTEN | INTE, dev->iobase + 9); devpriv->ai_bytes_to_read = it->n * sizeof(short); /* um... ignoreirq is a nasty race condition */ devpriv->das6402_ignoreirq = 0; outw_p(SCANL, dev->iobase + 2); return 0; } #endif static int board_init(struct comedi_device *dev) { struct das6402_private *devpriv = dev->private; BYTE b; devpriv->das6402_ignoreirq = 1; outb(0x07, dev->iobase + 8); /* register 11 */ outb_p(MODE, dev->iobase + 11); b = BIP | SEM | MODE | GAIN | FIFOHFULL; outb_p(b, dev->iobase + 11); /* register 8 */ outb_p(EXTEND, dev->iobase + 8); b = EXTEND | MHZ; outb_p(b, dev->iobase + 8); b = MHZ | CLRINT | CLRXTR | CLRXIN; outb_p(b, dev->iobase + 8); /* register 9 */ b = IRQ | CONVSRC | BURSTEN | INTE; outb_p(b, dev->iobase + 9); /* register 10 */ b = TGSEL | TGEN; outb_p(b, dev->iobase + 10); b = 0x07; outb_p(b, dev->iobase + 8); das6402_setcounter(dev); outw_p(SCANL, dev->iobase + 2); /* reset card fifo */ devpriv->das6402_ignoreirq = 0; return 0; } static int das6402_attach(struct comedi_device *dev, struct comedi_devconfig *it) { struct das6402_private *devpriv; unsigned int irq; int ret; struct comedi_subdevice *s; ret = comedi_request_region(dev, it->options[0], DAS6402_SIZE); if (ret) return ret; irq = it->options[0]; dev_dbg(dev->class_dev, "( irq = %u )\n", irq); ret = request_irq(irq, intr_handler, 0, "das6402", dev); if (ret < 0) return ret; dev->irq = irq; devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); if (!devpriv) return -ENOMEM; dev->private = devpriv; ret = comedi_alloc_subdevices(dev, 1); if (ret) return ret; /* ai subdevice */ s = &dev->subdevices[0]; s->type = COMEDI_SUBD_AI; s->subdev_flags = SDF_READABLE | SDF_GROUND; s->n_chan = 8; /* s->trig[2]=das6402_ai_mode2; */ s->cancel = das6402_ai_cancel; s->maxdata = (1 << 12) - 1; s->len_chanlist = 16; /* ? */ s->range_table = &range_unknown; board_init(dev); return 0; } static struct comedi_driver das6402_driver = { .driver_name = "das6402", .module = THIS_MODULE, .attach = das6402_attach, .detach = comedi_legacy_detach, }; module_comedi_driver(das6402_driver) MODULE_AUTHOR("Comedi http://www.comedi.org"); MODULE_DESCRIPTION("Comedi low-level driver"); MODULE_LICENSE("GPL");