From cd555c96a7aca37a069e381aa8388f7e21ee8c8f Mon Sep 17 00:00:00 2001 From: David Schleef Date: Wed, 29 Nov 2000 23:18:10 +0000 Subject: [PATCH] update from frank --- comedi/drivers/das800.c | 319 ++++++++++++++++++++++++++++------------ 1 file changed, 225 insertions(+), 94 deletions(-) diff --git a/comedi/drivers/das800.c b/comedi/drivers/das800.c index ad1c4e7f..a06925ee 100644 --- a/comedi/drivers/das800.c +++ b/comedi/drivers/das800.c @@ -19,6 +19,28 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +************************************************************************ + +This driver supports the Keithley DAS-800, DAS-801, and DAS-802 +boards. It will also work with Measurement Computing (Computer Boards) +boards CIO-DAS800, CIO-DAS801, and CIO-DAS802 boards although the +CIO-DAS801 has a slightly different gain list than the DAS-801. + +Options: + [0] - base io address + [1] - irq + +cmd triggers supported: + start_src: TRIG_NOW + scan_begin_src: TRIG_FOLLOW + scan_end_src: TRIG_COUNT + convert_src: TRIG_TIMER | TRIG_EXT + scan_end_src: TRIG_COUNT + stop_src: TRIG_END | TRIG_COUNT + +The number of channels scanned is determined from chanlist_len. The +starting channel to scan and the gain is determined from chanlist[0]. + */ #include @@ -33,16 +55,15 @@ #include #include #include -#include +#include #define DAS800_SIZE 8 -#define TIMER_BASE 1000 /* Registers for the das800 */ -#define DAS800_AI_LSB_READ 0 +#define DAS800_LSB 0 #define FIFO_EMPTY 0x1 #define FIFO_OVF 0x2 -#define DAS800_AI_MSB_READ 1 +#define DAS800_MSB 1 #define DAS800_CONTROL1 2 #define CONRTOL1_INTE 0x8 #define DAS800_CONV_CONTROL 2 @@ -52,6 +73,8 @@ #define EACS 0x10 #define CONV_HCEN 0x80 #define DAS800_SCAN_LIMITS 2 +#define DAS800_STATUS 2 +#define BUSY 0x80 #define DAS800_GAIN 3 #define CONTROL1 0x80 #define CONV_CONTROL 0xa0 @@ -62,11 +85,6 @@ #define STATUS2_INTE 0X20 #define DAS800_ID 7 -/* - * Board descriptions for two imaginary boards. Describing the - * boards in this way is optional, and completely driver-dependent. - * Some drivers use arrays such as this, other do not. - */ typedef struct das800_board_struct{ char *name; int ai_speed; @@ -92,17 +110,11 @@ das800_board das800_boards[] = */ #define thisboard ((das800_board *)dev->board_ptr) -/* this structure is for data unique to this hardware driver. If - several hardware drivers keep similar information in this structure, - feel free to suggest moving the variable to the comedi_device struct. */ typedef struct{ unsigned long count; /* number of data points left to be taken */ int forever; /* flag indicating whether we should take data forever */ }das800_private; -/* - * most drivers define the following macro to make it easy to - * access the private structure. - */ + #define devpriv ((das800_private *)dev->private) static comedi_lrange range_das800_ai = { @@ -156,6 +168,7 @@ static comedi_lrange *das800_range_lkup[] = { */ static int das800_attach(comedi_device *dev,comedi_devconfig *it); static int das800_detach(comedi_device *dev); +static int das800_cancel(comedi_device *dev, comedi_subdevice *s); comedi_driver driver_das800={ driver_name: "das800", @@ -164,24 +177,21 @@ comedi_driver driver_das800={ detach: das800_detach, }; -/* static int das800_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); - * static int das800_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); - */ - static void das800_interrupt(int irq, void *d, struct pt_regs *regs); void enableDAS800(comedi_device *dev); void disableDAS800(comedi_device *dev); -static int das800_do_cmd(comedi_device *dev, comedi_subdevice *s); +static int das800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd); +static int das800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s); +static int das800_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); int das800_probe(comedi_device *dev); -int das800SetFrequency( unsigned int period, comedi_device *dev); -int das800LoadCounter(unsigned int counterNumber, int counterValue, comedi_device *dev); +int das800_set_frequency( unsigned int period, comedi_device *dev); +int das800_load_counter(unsigned int counterNumber, int counterValue, comedi_device *dev); int das800_probe(comedi_device *dev) { int id; outb(ID, dev->iobase + DAS800_GAIN); /* select base address + 7 to be ID register */ - id = inb(DAS800_ID); - outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select base address +7 to be status register 2 */ + id = inb(dev->iobase + DAS800_ID) & 0x3; switch(id) { case 0x0: @@ -197,8 +207,8 @@ int das800_probe(comedi_device *dev) return 2; break; default : - printk(" Board model: probe failed\n"); - return dev->board; + printk(" Board model: probe returned 0x%x (unknown)\n", id); + return 0; break; } return -1; @@ -213,33 +223,35 @@ COMEDI_INITCLEANUP(driver_das800); static void das800_interrupt(int irq, void *d, struct pt_regs *regs) { short i; /* loop index */ - sampl_t dataPoint; + sampl_t dataPoint; comedi_device *dev = d; comedi_subdevice *s = dev->subdevices + 0; /* analog input subdevice */ /* read 16 bits from dev->iobase and dev->iobase + 1 */ - dataPoint = inb(dev->iobase + DAS800_AI_LSB_READ); - dataPoint += inb(dev->iobase + DAS800_AI_MSB_READ) << 8; - /* loop while card's fifo is not empty (and make sure loop terminates by limiting to 10 iterations) */ - for(i = 0; (dataPoint & FIFO_EMPTY) == 0 && i < 10; i++) + dataPoint = inb(dev->iobase + DAS800_LSB); + dataPoint += inb(dev->iobase + DAS800_MSB) << 8; + /* loop while card's fifo is not empty (and make sure loop terminates by limiting to 256 iterations) */ + for(i = 0; (dataPoint & FIFO_EMPTY) == 0 && i < 256; i++) { if( dataPoint & FIFO_OVF ) { - comedi_error(dev, "DAS800 FIFO overflow"); + comedi_error(dev, "DAS800 FIFO overflow"); comedi_error_done(dev, s); + devpriv->forever = 0; + devpriv->count = 0; + disableDAS800(dev); return; } dataPoint = (dataPoint >> 4) & 0xfff; /* strip off extraneous bits */ if(devpriv->count > 0 || devpriv->forever == 1) { /* write data point to buffer */ - if(s->buf_int_ptr + sizeof(sampl_t) >= s->cur_trig.data_len) + if(s->buf_int_ptr >= s->cur_trig.data_len ) { s->buf_int_ptr=0; comedi_eobuf(dev,s); - break; } - *(s->cur_trig.data + s->buf_int_ptr) = dataPoint; + *((sampl_t *)((void *)s->cur_trig.data + s->buf_int_ptr)) = dataPoint; s->cur_chan++; if( s->cur_chan >= s->cur_chanlist_len ) { @@ -251,8 +263,8 @@ static void das800_interrupt(int irq, void *d, struct pt_regs *regs) if(devpriv->count > 0) devpriv->count--; } /* read 16 bits from dev->iobase and dev->iobase + 1 */ - dataPoint = inb(dev->iobase + DAS800_AI_LSB_READ); - dataPoint += inb(dev->iobase + DAS800_AI_MSB_READ) << 8; + dataPoint = inb(dev->iobase + DAS800_LSB); + dataPoint += inb(dev->iobase + DAS800_MSB) << 8; } comedi_bufcheck(dev,s); if(devpriv->count > 0 || devpriv->forever == 1) @@ -276,7 +288,7 @@ static int das800_attach(comedi_device *dev, comedi_devconfig *it) int iobase = it->options[0]; int irq = it->options[1]; - printk("comedi%d: das800: io 0x%x, irq %i",dev->minor, iobase, irq); + printk("comedi%d: das800: io 0x%x, irq %i\n",dev->minor, iobase, irq); /* check if io addresses are available */ if (check_region(iobase, DAS800_SIZE) < 0) @@ -284,6 +296,7 @@ static int das800_attach(comedi_device *dev, comedi_devconfig *it) printk("I/O port conflict\n"); return -EIO; } + request_region(iobase, DAS800_SIZE, "das800"); dev->iobase = iobase; dev->iosize = DAS800_SIZE; @@ -300,14 +313,6 @@ static int das800_attach(comedi_device *dev, comedi_devconfig *it) } dev->irq = irq; - /* wait until after irq check before allocating io addresses */ - request_region(dev->iobase, DAS800_SIZE, "das800"); - -/* - * If you can probe the device to determine what device in a series - * it is, this is the place to do it. Otherwise, you should use the - * _recognize method, and use the value in dev->board. - */ dev->board = das800_probe(dev); /* @@ -340,32 +345,37 @@ static int das800_attach(comedi_device *dev, comedi_devconfig *it) s->type = COMEDI_SUBD_AI; s->subdev_flags = SDF_READABLE; s->n_chan = 8; + s->len_chanlist = 8; s->maxdata=(1 << 12) - 1; s->range_table = das800_range_lkup[dev->board]; - s->do_cmd = &das800_do_cmd; - s->do_cmdtest = &das800_do_cmdtest; - - return 1; + s->do_cmd = das800_ai_do_cmd; + s->do_cmdtest = das800_ai_do_cmdtest; + s->insn_read = das800_ai_rinsn; + s->cancel = das800_cancel; + return 0; }; -/* - * _detach is called to deconfigure a device. It should deallocate - * resources. - * This function is also called when _attach() fails, so it should be - * careful not to release resources that were not necessarily - * allocated by _attach(). dev->private and dev->subdevices are - * deallocated automatically by the core. - */ static int das800_detach(comedi_device *dev) { printk("comedi%d: das800: remove\n", dev->minor); - release_region(dev->iobase, DAS800_SIZE); /* free general status and control registers' i/o ports */ - comedi_free_irq(dev->irq,dev); /* free irq */ + /* only free stuff if it has been allocated by _attach */ + if(dev->iobase) + release_region(dev->iobase, DAS800_SIZE); + if(dev->irq) + comedi_free_irq(dev->irq,dev); return 0; }; +static int das800_cancel(comedi_device *dev, comedi_subdevice *s) +{ + devpriv->forever = 0; + devpriv->count = 0; + disableDAS800(dev); + return 0; +} + /* enableDAS800 makes the card start taking hardware triggered conversions */ void enableDAS800(comedi_device *dev) { @@ -378,23 +388,54 @@ void enableDAS800(comedi_device *dev) /* disableDAS800 stops hardware triggered conversions */ void disableDAS800(comedi_device *dev) { - outb(0x0, dev->iobase + DAS800_CONTROL1); /* disable card's interrupt */ outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */ outb(0x0, dev->iobase + DAS800_CONV_CONTROL); /* disable hardware triggering of conversions */ - outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */ } -static int das800_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd) +static int das800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd) { int err = 0; + int tmp, chan, range; + + /* step 1: make sure trigger sources are trivially valid */ + + tmp = cmd->start_src; + cmd->start_src &= TRIG_NOW; + if(!cmd->start_src && tmp != cmd->start_src) err++; + + tmp = cmd->scan_begin_src; + cmd->scan_begin_src &= TRIG_FOLLOW; + if(!cmd->scan_begin_src && tmp != cmd->scan_begin_src) err++; + + tmp = cmd->convert_src; + cmd->convert_src &= TRIG_TIMER | TRIG_EXT; + if(!cmd->convert_src && tmp != cmd->convert_src) err++; + + tmp = cmd->scan_end_src; + cmd->scan_end_src &= TRIG_COUNT; + if(!cmd->scan_end_src && tmp != cmd->scan_end_src) err++; + + tmp=cmd->stop_src; + cmd->stop_src &= TRIG_COUNT | TRIG_NONE; + if(!cmd->stop_src && tmp!=cmd->stop_src) err++; - /* check for supported triggers */ - if(cmd->start_src != TRIG_NOW) err++; - if(cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT) err++; - if(cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE) err++; if(err) return 1; - /* check arguments */ + /* step 2: make sure trigger sources are unique and mutually compatible */ + + if(cmd->scan_begin_src != TRIG_FOLLOW) err++; + if(cmd->convert_src != TRIG_TIMER && + cmd->convert_src != TRIG_EXT) err++; + + if(err)return 2; + + /* step 3: make sure arguments are trivially compatible */ + + if(cmd->start_arg!=0) + { + cmd->start_arg = 0; + err++; + } if(cmd->convert_src == TRIG_TIMER) { if(cmd->convert_arg < thisboard->ai_speed) @@ -403,43 +444,82 @@ static int das800_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd * err++; } } + if(!cmd->chanlist_len) + { + cmd->chanlist_len=1; + err++; + } + if(cmd->chanlist_len > 8) + { + cmd->chanlist_len = 8; + err++; + } + if(cmd->scan_end_arg != cmd->chanlist_len) + { + cmd->scan_end_arg = cmd->chanlist_len; + err++; + } if(cmd->stop_src == TRIG_COUNT) { - if(cmd->stop_arg > 0x00ffffff) + if(!cmd->stop_arg) { - cmd->stop_arg = 0x00ffffff; + cmd->stop_arg = 1; + err++; + } + } else + { /* TRIG_NONE */ + if(cmd->stop_arg != 0) + { + cmd->stop_arg = 0; err++; } } - if(cmd->chanlist_len <= 0 || cmd->chanlist_len >8) + + if(err)return 3; + + /* step 4: fix up any arguments */ + + chan = CR_CHAN(cmd->chanlist[0]); + range = CR_RANGE(cmd->chanlist[0]); + if(chan >= 8) { - cmd->chanlist_len = 1; + cmd->chanlist[0] = CR_PACK(chan % 8, range, 0); err++; } - if(err)return 3; + if(cmd->convert_src == TRIG_TIMER) + { + tmp = cmd->convert_arg; +/* todo: add code to figure out what actual convert_arg card will use + and store it in tmp + + if(tmp != cmd->convert_arg) err++; +*/ + } + + if(err)return 4; return 0; } -static int das800_do_cmd(comedi_device *dev, comedi_subdevice *s) +static int das800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s) { - int gain; - unsigned int numChannels = s->cmd.chanlist_len; + int startChan, endChan, scan, gain; disableDAS800(dev); /* set channel scan limits */ outb(SCAN_LIMITS, dev->iobase + DAS800_GAIN); /* select base address + 2 to be scan limits register */ - outb((numChannels - 1) << 3, dev->iobase + DAS800_SCAN_LIMITS); /* set scan limits */ - outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */ + startChan = CR_CHAN(s->cmd.chanlist[0]); + endChan = (startChan + s->cmd.chanlist_len - 1) % 8; + scan = (endChan << 3) | startChan; + outb(scan, dev->iobase + DAS800_SCAN_LIMITS); /* set scan limits */ /* set gain */ - gain = s->cmd.chanlist[0]; + gain = CR_RANGE(s->cmd.chanlist[0]); if( gain > 0) gain += 0x7; - gain = gain & 0xf; - outb(gain, DAS800_GAIN); - + gain &= 0xf; + outb(gain, dev->iobase + DAS800_GAIN); switch(s->cmd.stop_src) { @@ -461,15 +541,17 @@ static int das800_do_cmd(comedi_device *dev, comedi_subdevice *s) switch(s->cmd.convert_src) { case TRIG_TIMER: + outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */ outb(EACS | IEOC | CASC| ITE, dev->iobase + DAS800_CONV_CONTROL); /* set conversion frequency */ - if( das800SetFrequency( s->cmd.convert_arg, dev) < 0) + if( das800_set_frequency( s->cmd.convert_arg, dev) < 0) { comedi_error(dev, "Error setting up counters"); return -1; } break; case TRIG_EXT: + outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */ outb(EACS | IEOC, dev->iobase + DAS800_CONV_CONTROL); break; default: @@ -480,21 +562,71 @@ static int das800_do_cmd(comedi_device *dev, comedi_subdevice *s) return 0; } +static int das800_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data) +{ + int i,n; + int chan; + int range; + int lsb,msb; + + disableDAS800(dev); + + /* set multiplexer */ + chan = CR_CHAN(insn->chanspec); + outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */ + outb(chan & 0x7, dev->iobase + DAS800_CONTROL1); + + /* set gain / range */ + range = CR_RANGE(insn->chanspec); + if(range) + range += 0x7; + range &= 0xf; + outb(range, dev->iobase + DAS800_GAIN); + + udelay(5); + + for(n = 0; n < insn->n; n++) + { + /* trigger conversion */ + outb(0, dev->iobase); + udelay(25); +#define TIMEOUT 1000 + for(i = 0; i < TIMEOUT; i++) + { + if(!(inb(DAS800_STATUS) & BUSY)) + break; + } + if(i == TIMEOUT) + { + comedi_error(dev, "timeout"); + return -ETIME; + } + lsb = inb(dev->iobase + DAS800_LSB); + msb = inb(dev->iobase + DAS800_MSB); + data[n] = (lsb >> 4) | (msb << 4); + } + + return n; +} + + /* finds best values for cascaded counters to obtain desired frequency, * and loads counters with calculated values */ -int das800SetFrequency( unsigned int period, comedi_device *dev) +int das800_set_frequency( unsigned int period, comedi_device *dev) { int clock = 1000; int countA = 0; int countB = 0; int temp, close; - unsigned int i, j, max, start; + unsigned int i, j, max, min; - max = (unsigned int) (period / (2 * clock)) + 1; - start = 1; + max = (unsigned int) (period / (2 * clock)); + if(max > (1 << 16)) + max = (1 << 16); + min = 2; close = period; - for(i = start; i <= max; i++) + for(i = min; i <= max; i++) { for(j = (period / (i * clock)); j <= (period / (i * clock)) + 1; j++) { @@ -508,20 +640,20 @@ int das800SetFrequency( unsigned int period, comedi_device *dev) } } - countA = das800LoadCounter(1, countA, dev); - countB = das800LoadCounter(2, countB, dev); + countA = das800_load_counter(1, countA, dev); + countB = das800_load_counter(2, countB, dev); if(countA < 0 || countB < 0) return -1; return clock * countA * countB; } -int das800LoadCounter(unsigned int counterNumber, int counterValue, comedi_device *dev) +int das800_load_counter(unsigned int counterNumber, int counterValue, comedi_device *dev) { unsigned char byte; if(counterNumber > 2) return -1; - if(counterValue < 1 || counterValue > 0xffff) return -1; + if(counterValue < 2 || counterValue > 0xffff) return -1; byte = counterNumber << 6; byte = byte | 0x30; // load low then high byte @@ -531,6 +663,5 @@ int das800LoadCounter(unsigned int counterNumber, int counterValue, comedi_devic outb(byte, dev->iobase + 0x4 + counterNumber); byte = counterValue >> 8; // msb of counter value outb(byte, dev->iobase + 0x4 + counterNumber); - return counterValue; } -- 2.26.2