From 0b260fe75a0eae8a3d8d6f76ce7034b6c8f796e9 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Tue, 26 Dec 2000 16:28:08 +0000 Subject: [PATCH] update from frank --- comedi/drivers/das1800.c | 929 +++++++++++++++++++++------------------ 1 file changed, 490 insertions(+), 439 deletions(-) diff --git a/comedi/drivers/das1800.c b/comedi/drivers/das1800.c index 428ab37c..150a9212 100644 --- a/comedi/drivers/das1800.c +++ b/comedi/drivers/das1800.c @@ -139,6 +139,27 @@ TODO: enum{das1701st, das1701st_da, das1702st, das1702st_da, das1702hr, das1702hr_da, das1701ao, das1702ao, das1801st, das1801st_da, das1802st, das1802st_da, das1802hr, das1802hr_da, das1801hc, das1802hc, das1801ao, das1802ao}; +static int das1800_attach(comedi_device *dev, comedi_devconfig *it); +static int das1800_detach(comedi_device *dev); +static int das1800_recognize(char *name); +int das1800_probe(comedi_device *dev); +static int das1800_cancel(comedi_device *dev, comedi_subdevice *s); +static void das1800_interrupt(int irq, void *d, struct pt_regs *regs); +static void das1800_handle_dma(comedi_device *dev, comedi_subdevice *s); +static void das1800_handle_fifo_half_full(comedi_device *dev, comedi_subdevice *s); +static void das1800_handle_fifo_not_empty(comedi_device *dev, comedi_subdevice *s); +inline void write_to_buffer(comedi_device *dev, comedi_subdevice *s, sampl_t +data_point); void disable_das1800(comedi_device *dev); +static int das1800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd); +static int das1800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s); +static int das1800_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); +static int das1800_ao_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); +static int das1800_di_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); +static int das1800_do_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); +int das1800_set_frequency(comedi_device *dev); +int das1800_load_counter(comedi_device *dev, unsigned int counterNumber, unsigned int counterValue, unsigned int mode); +unsigned int burst_convert_arg(unsigned int convert_arg, int round_mode); + typedef struct das1800_board_struct{ char *name; int ai_speed; /* max conversion period in nanoseconds */ @@ -356,7 +377,6 @@ typedef struct{ short *dma1_buf; short *dma_current_buf; /* pointer to dma buffer currently being used */ unsigned int dma_buf_size; /* size in bytes of dma buffers */ - int dual_dma; /* flag that indicates whether we have dual dma */ int iobase2; /* secondary io address used for analog out on 'ao' boards */ short ao_update_bits; /* remembers the last write to the 'update' dac */ }das1800_private; @@ -414,7 +434,7 @@ static comedi_lrange *das1800_ai_range_lkup[] = { &range_das1802_ai, }; -// analog output ranges +// analog out range for boards with basic analog out static comedi_lrange range_ao_1 = { 1, { @@ -422,6 +442,8 @@ static comedi_lrange range_ao_1 = { } }; +// analog out range for 'ao' boards +/* static comedi_lrange range_ao_2 = { 2, { @@ -429,11 +451,7 @@ static comedi_lrange range_ao_2 = { RANGE(-5, 5), } }; - -static int das1800_attach(comedi_device *dev, comedi_devconfig *it); -static int das1800_detach(comedi_device *dev); -static int das1800_recognize(char *name); -static int das1800_cancel(comedi_device *dev, comedi_subdevice *s); +*/ comedi_driver driver_das1800={ driver_name: "das1800", @@ -443,377 +461,49 @@ comedi_driver driver_das1800={ recognize: das1800_recognize, }; -static void das1800_interrupt(int irq, void *d, struct pt_regs *regs); -static void das1800_handle_dma(comedi_device *dev, comedi_subdevice *s); -static void das1800_handle_fifo_half_full(comedi_device *dev, comedi_subdevice *s); -static void das1800_handle_fifo_not_empty(comedi_device *dev, comedi_subdevice *s); -void write_to_buffer(comedi_device *dev, comedi_subdevice *s, sampl_t data_point); -void disable_das1800(comedi_device *dev); -static int das1800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd); -static int das1800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s); -static int das1800_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); -static int das1800_ao_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); -static int das1800_di_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); -static int das1800_do_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); -int das1800_probe(comedi_device *dev); -int das1800_set_frequency(comedi_device *dev); -int das1800_load_counter(comedi_device *dev, unsigned int counterNumber, unsigned int counterValue, unsigned int mode); -unsigned int burst_convert_arg(unsigned int convert_arg, int round_mode); - -static int das1800_recognize(char *name) -{ - if(!strcmp(name, "das-1701st")) - return das1701st; - if(!strcmp(name, "das-1701st-da")) - return das1701st_da; - if(!strcmp(name, "das-1702st")) - return das1702st; - if(!strcmp(name, "das-1702st-da")) - return das1702st_da; - if(!strcmp(name, "das-1702hr")) - return das1702hr; - if(!strcmp(name, "das-1702hr-da")) - return das1702hr_da; - if(!strcmp(name, "das-1701ao")) - return das1701ao; - if(!strcmp(name, "das-1702ao")) - return das1702ao; - if(!strcmp(name, "das-1801st")) - return das1801st; - if(!strcmp(name, "das-1801st-da")) - return das1801st_da; - if(!strcmp(name, "das-1802st")) - return das1802st; - if(!strcmp(name, "das-1802st-da")) - return das1802st_da; - if(!strcmp(name, "das-1802hr")) - return das1802hr; - if(!strcmp(name, "das-1802hr-da")) - return das1802hr_da; - if(!strcmp(name, "das-1801hc")) - return das1801hc; - if(!strcmp(name, "das-1802hc")) - return das1802hc; - if(!strcmp(name, "das-1801ao")) - return das1801ao; - if(!strcmp(name, "das-1802ao")) - return das1802ao; - - return -1; -} - -/* probes and checks das-1800 series board type - */ -int das1800_probe(comedi_device *dev) -{ - int id; - id = (inb(dev->iobase + DAS1800_DIGITAL) >> 4) & 0x7; /* get id bits */ - switch(id) - { - // das-1800st-da - case 0x3: - if(dev->board == das1801st_da || dev->board == das1802st_da || - dev->board == das1701st_da || dev->board == das1702st_da) - { - printk(" Board model: %s\n", (das1800_boards + dev->board)->name); - return dev->board; - } - printk(" Board model (probed, not recommended): DAS-1800ST-DA series\n"); - return das1801st; - break; - // das-1800hr-da - case 0x4: - if(dev->board == das1802hr_da || dev->board == das1702hr_da) - { - printk(" Board model: %s\n", (das1800_boards + dev->board)->name); - return dev->board; - } - printk(" Board model (probed, not recommended): DAS-1802HR-DA\n"); - return das1802hr; - break; - case 0x5: - if(dev->board == das1801ao || dev->board == das1802ao || - dev->board == das1701ao || dev->board == das1702ao) - { - printk(" Board model: %s\n", (das1800_boards + dev->board)->name); - return dev->board; - } - printk(" Board model (probed, not recommended): DAS-1800AO series\n"); - return das1801ao; - break; - case 0x6: - if(dev->board == das1802hr || dev->board == das1702hr) - { - printk(" Board model: %s\n", (das1800_boards + dev->board)->name); - return dev->board; - } - printk(" Board model (probed, not recommended): DAS-1802HR\n"); - return das1802hr; - break; - case 0x7: - if(dev->board == das1801st || dev->board == das1802st || - dev->board == das1701st || dev->board == das1702st) - { - printk(" Board model: %s\n", (das1800_boards + dev->board)->name); - return dev->board; - } - printk(" Board model (probed, not recommended): DAS-1800ST series\n"); - return das1801st; - break; - case 0x8: - if(dev->board == das1801hc || dev->board == das1802hc) - { - printk(" Board model: %s\n", (das1800_boards + dev->board)->name); - return dev->board; - } - printk(" Board model (probed, not recommended): DAS-1800HC series\n"); - return das1801hc; - break; - default : - printk(" Board model: probe returned 0x%x (unknown)\n", id); - return dev->board; - break; - } - return -1; -} - /* * A convenient macro that defines init_module() and cleanup_module(), * as necessary. */ COMEDI_INITCLEANUP(driver_das1800); -static void das1800_interrupt(int irq, void *d, struct pt_regs *regs) -{ - int status; - comedi_device *dev = d; - comedi_subdevice *s = dev->subdevices + 0; /* analog input subdevice */ - - status = inb(dev->iobase + DAS1800_STATUS); - /* if interrupt was not caused by das-1800 */ - if(!(status & INT)) - { - return; - } else if(devpriv->dma0) /* if dma is enabled and generated the interrupt */ - { - // dma buffer full or about-triggering (stop_src == TRIG_EXT) - if(status & (DMATC | CT0TC)) - das1800_handle_dma(dev, s); - } else if(status & FHF) - { - das1800_handle_fifo_half_full(dev, s); - } else if(status & FNE) - { - das1800_handle_fifo_not_empty(dev, s); - } - comedi_bufcheck(dev, s); - /* if the card's fifo has overflowed */ - if(status & OVF) - { - comedi_error(dev, "DAS1800 FIFO overflow"); - das1800_cancel(dev, s); - comedi_error_done(dev, s); - return; - } - /* stop taking data if appropriate */ - if(devpriv->count == 0 && devpriv->forever == 0) - { - disable_das1800(dev); /* disable hardware conversions */ - comedi_done(dev, s); - } - - return; -} - -static void das1800_handle_dma(comedi_device *dev, comedi_subdevice *s) +static int das1800_attach(comedi_device *dev, comedi_devconfig *it) { + comedi_subdevice *s; unsigned long flags; - int numPoints, maxPoints; - short dpnt; - short *buffer; - int unipolar; - int i; + int iobase = it->options[0]; + int irq = it->options[1]; + int dma0 = it->options[2]; + int dma1 = it->options[3]; + int iobase2; - flags = claim_dma_lock(); - disable_dma(devpriv->dma_current); - // figure out how many points to read - if(devpriv->forever) - numPoints = devpriv->dma_buf_size / sizeof(short); - else - numPoints = devpriv->count; - maxPoints = (devpriv->dma_buf_size - get_dma_residue(devpriv->dma_current)) / sizeof(short); - if(numPoints > maxPoints) - numPoints = maxPoints; - buffer = devpriv->dma_current_buf; - if(devpriv->dual_dma) + /* allocate and initialize dev->private */ + if(alloc_private(dev, sizeof(das1800_private)) < 0) + return -ENOMEM; + + printk("comedi%d: das1800: io 0x%x", dev->minor, iobase); + if(irq) { - if(devpriv->dma_current == devpriv->dma0) - { - devpriv->dma_current = devpriv->dma1; - devpriv->dma_current_buf = devpriv->dma1_buf; - } - else + printk(", irq %i", irq); + if(dma0) { - devpriv->dma_current = devpriv->dma0; - devpriv->dma_current_buf = devpriv->dma0_buf; + printk(", dma %i", dma0); + if(dma1) printk(" and %i", dma1); } } - set_dma_addr(devpriv->dma_current, (unsigned int) devpriv->dma_current_buf); - if((devpriv->count - numPoints) * sizeof(short) > devpriv->dma_buf_size || devpriv->forever) - set_dma_count(devpriv->dma_current, devpriv->dma_buf_size); - else - set_dma_count(devpriv->dma_current, (devpriv->count - numPoints) * sizeof(short)); + printk("\n"); - // if we are doing dual channel dma, enable the second channel immediately - if(devpriv->dual_dma) + if(iobase == 0) { - enable_dma(devpriv->dma_current); - release_dma_lock(flags); + printk("io base address required for das1800\n"); + return -EINVAL; } - /* see if card is using a unipolar or bipolar range */ - unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB; - - for( i = 0; i < numPoints; i++) + dev->board = das1800_probe(dev); + if(dev->board < 0) { - /* write data point to comedi buffer */ - dpnt = buffer[i]; - /* convert to unsigned type if we are in a bipolar mode */ - if(!unipolar); - dpnt += 1 << (thisboard->resolution - 1); - write_to_buffer(dev, s, dpnt); - if(devpriv->count > 0) devpriv->count--; - } - - // for single channel dma, re-enable after old data has been read - if(devpriv->dual_dma == 0) - { - enable_dma(devpriv->dma_current); - release_dma_lock(flags); - } - - /* clear interrupt */ - outb(FNE, dev->iobase + DAS1800_STATUS); - - return; -} - -static void das1800_handle_fifo_half_full(comedi_device *dev, comedi_subdevice *s) -{ - int i; /* loop index */ - int numPoints = 0; /* number of points to read */ - sampl_t dpnt; - int unipolar; - - unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB; - - numPoints = HALF_FIFO; - /* if we only need some of the points */ - if( devpriv->forever == 0 && devpriv->count < numPoints) - numPoints = devpriv->count; - for( i = 0; i < numPoints; i++) - { - /* write data point to buffer */ - dpnt = inw(dev->iobase + DAS1800_FIFO); - /* convert to unsigned type if we are in a bipolar mode */ - if(!unipolar); - dpnt += 1 << (thisboard->resolution - 1); - write_to_buffer(dev, s, dpnt); - if(devpriv->count > 0) devpriv->count--; - } - /* clear interrupt */ - outb(FNE, dev->iobase + DAS1800_STATUS); - // if there are just a few points left, switch to interrupt on end of conversion - if(devpriv->count < HALF_FIFO && devpriv->count > 0 && devpriv->forever == 0) - { - devpriv->irq_dma_bits &= ~FIMD; // interrupt fifo not empty - outb(devpriv->irq_dma_bits, dev->iobase + DAS1800_CONTROL_B); - } - return; -} - -static void das1800_handle_fifo_not_empty(comedi_device *dev, comedi_subdevice *s) -{ - sampl_t dpnt; - int unipolar; - - unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB; - - while(inb(dev->iobase + DAS1800_STATUS) & FNE) - { - dpnt = inw(dev->iobase + DAS1800_FIFO); - /* convert to unsigned type if we are in a bipolar mode */ - if(!unipolar); - dpnt += 1 << (thisboard->resolution - 1); - write_to_buffer(dev, s, dpnt); - if(devpriv->count > 0) devpriv->count--; - if(devpriv->count == 0 && devpriv->forever == 0) - break; - } - /* clear interrupt */ - outb(FNE, dev->iobase + DAS1800_STATUS); - - return; -} - -/* utility function used by das1800 interrupt service routines, really - * should be inline - */ -void write_to_buffer(comedi_device *dev, comedi_subdevice *s, sampl_t data_point) -{ - if(s->buf_int_ptr >= s->cur_trig.data_len ) - { - s->buf_int_ptr = 0; - comedi_eobuf(dev, s); - } - *((sampl_t *)((void *)s->cur_trig.data + s->buf_int_ptr)) = data_point; - s->cur_chan++; - if(s->cur_chan >= s->cur_chanlist_len) - { - s->cur_chan = 0; - comedi_eos(dev, s); - } - s->buf_int_count += sizeof(sampl_t); - s->buf_int_ptr += sizeof(sampl_t); -} - -static int das1800_attach(comedi_device *dev, comedi_devconfig *it) -{ - comedi_subdevice *s; - unsigned long flags; - int iobase = it->options[0]; - int irq = it->options[1]; - int dma0 = it->options[2]; - int dma1 = it->options[3]; - int iobase2; - - /* allocate and initialize dev->private */ - if(alloc_private(dev, sizeof(das1800_private)) < 0) - return -ENOMEM; - - printk("comedi%d: das1800: io 0x%x", dev->minor, iobase); - if(irq) - { - printk(", irq %i", irq); - if(dma0) - { - printk(", dma %i", dma0); - if(dma1) printk(" and %i", dma1); - } - } - printk("\n"); - - if(iobase == 0) - { - printk("io base address required for das1800\n"); - return -EINVAL; - } - - dev->board = das1800_probe(dev); - if(dev->board < 0) - { - printk("unable to determine board type\n"); - return -ENODEV; + printk("unable to determine board type\n"); + return -ENODEV; } dev->board_ptr = das1800_boards + dev->board; @@ -839,6 +529,7 @@ static int das1800_attach(comedi_device *dev, comedi_devconfig *it) iobase2, iobase2 + DAS1800_SIZE); return -EIO; } + request_region(iobase2, DAS1800_SIZE, thisboard->name); devpriv->iobase2 = iobase2; } @@ -930,7 +621,6 @@ static int das1800_attach(comedi_device *dev, comedi_devconfig *it) return -EINVAL; } devpriv->dma1 = dma1; - devpriv->dual_dma = 1; } devpriv->dma_buf_size = 0x1ff00; devpriv->dma0_buf = kmalloc(devpriv->dma_buf_size, GFP_BUFFER | GFP_DMA); @@ -957,104 +647,447 @@ static int das1800_attach(comedi_device *dev, comedi_devconfig *it) } } - dev->n_subdevices = 4; - if(alloc_subdevices(dev) < 0) - return -ENOMEM; + dev->n_subdevices = 4; + if(alloc_subdevices(dev) < 0) + return -ENOMEM; + + /* analog input subdevice */ + s = dev->subdevices + 0; + s->type = COMEDI_SUBD_AI; + s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_GROUND; + if(thisboard->common) + s->subdev_flags |= SDF_COMMON; + s->n_chan = thisboard->qram_len; + s->len_chanlist = thisboard->qram_len; + s->maxdata = (1 << thisboard->resolution) - 1; + s->range_table = das1800_ai_range_lkup[dev->board]; + s->do_cmd = das1800_ai_do_cmd; + s->do_cmdtest = das1800_ai_do_cmdtest; + s->insn_read = das1800_ai_rinsn; + s->cancel = das1800_cancel; + + /* analog out */ + s = dev->subdevices + 1; + if(thisboard->ao_ability == 1) + { + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITEABLE; + s->n_chan = thisboard->ao_n_chan; + s->maxdata = (1 << thisboard->resolution) - 1; + s->range_table = &range_ao_1; + s->insn_write = das1800_ao_winsn; + } + else + { + s->type = COMEDI_SUBD_UNUSED; + } + + /* di */ + s = dev->subdevices + 2; + s->type = COMEDI_SUBD_DI; + s->subdev_flags = SDF_READABLE; + s->n_chan = 4; + s->maxdata = 1; + s->range_table = &range_digital; + s->insn_read = das1800_di_rinsn; + + /* do */ + s = dev->subdevices + 3; + s->type = COMEDI_SUBD_DO; + s->subdev_flags = SDF_WRITEABLE; + s->n_chan = thisboard->do_n_chan; + s->maxdata = 1; + s->range_table = &range_digital; + s->insn_write = das1800_do_winsn; + + disable_das1800(dev); + + // initialize digital out channels + outb(devpriv->do_bits, dev->iobase + DAS1800_DIGITAL); + + // initialize analog out channels + if(thisboard->ao_ability == 1) + { + // select 'update' dac channel for baseAddress + 0x0 + outb(DAC(thisboard->ao_n_chan - 1), dev->iobase + DAS1800_SELECT); + outw(devpriv->ao_update_bits, dev->iobase + DAS1800_DAC); + } + + return 0; +}; + +static int das1800_detach(comedi_device *dev) +{ + /* only free stuff if it has been allocated by _attach */ + if(dev->iobase) + release_region(dev->iobase, DAS1800_SIZE); + if(devpriv->iobase2) + release_region(devpriv->iobase2, DAS1800_SIZE); + if(dev->irq) + comedi_free_irq(dev->irq, dev); + if(devpriv->dma0) + free_dma(devpriv->dma0); + if(devpriv->dma0_buf) + kfree(devpriv->dma0_buf); + if(devpriv->dma1) + free_dma(devpriv->dma1); + if(devpriv->dma1_buf) + kfree(devpriv->dma1_buf); + + printk("comedi%d: das1800: remove\n", dev->minor); + + return 0; +}; + +static int das1800_recognize(char *name) +{ + if(!strcmp(name, "das-1701st")) + return das1701st; + if(!strcmp(name, "das-1701st-da")) + return das1701st_da; + if(!strcmp(name, "das-1702st")) + return das1702st; + if(!strcmp(name, "das-1702st-da")) + return das1702st_da; + if(!strcmp(name, "das-1702hr")) + return das1702hr; + if(!strcmp(name, "das-1702hr-da")) + return das1702hr_da; + if(!strcmp(name, "das-1701ao")) + return das1701ao; + if(!strcmp(name, "das-1702ao")) + return das1702ao; + if(!strcmp(name, "das-1801st")) + return das1801st; + if(!strcmp(name, "das-1801st-da")) + return das1801st_da; + if(!strcmp(name, "das-1802st")) + return das1802st; + if(!strcmp(name, "das-1802st-da")) + return das1802st_da; + if(!strcmp(name, "das-1802hr")) + return das1802hr; + if(!strcmp(name, "das-1802hr-da")) + return das1802hr_da; + if(!strcmp(name, "das-1801hc")) + return das1801hc; + if(!strcmp(name, "das-1802hc")) + return das1802hc; + if(!strcmp(name, "das-1801ao")) + return das1801ao; + if(!strcmp(name, "das-1802ao")) + return das1802ao; + + return -1; +} + +/* probes and checks das-1800 series board type + */ +int das1800_probe(comedi_device *dev) +{ + int id; + id = (inb(dev->iobase + DAS1800_DIGITAL) >> 4) & 0x7; /* get id bits */ + switch(id) + { + // das-1800st-da + case 0x3: + if(dev->board == das1801st_da || dev->board == das1802st_da || + dev->board == das1701st_da || dev->board == das1702st_da) + { + printk(" Board model: %s\n", (das1800_boards + dev->board)->name); + return dev->board; + } + printk(" Board model (probed, not recommended): DAS-1800ST-DA series\n"); + return das1801st; + break; + // das-1800hr-da + case 0x4: + if(dev->board == das1802hr_da || dev->board == das1702hr_da) + { + printk(" Board model: %s\n", (das1800_boards + dev->board)->name); + return dev->board; + } + printk(" Board model (probed, not recommended): DAS-1802HR-DA\n"); + return das1802hr; + break; + case 0x5: + if(dev->board == das1801ao || dev->board == das1802ao || + dev->board == das1701ao || dev->board == das1702ao) + { + printk(" Board model: %s\n", (das1800_boards + dev->board)->name); + return dev->board; + } + printk(" Board model (probed, not recommended): DAS-1800AO series\n"); + return das1801ao; + break; + case 0x6: + if(dev->board == das1802hr || dev->board == das1702hr) + { + printk(" Board model: %s\n", (das1800_boards + dev->board)->name); + return dev->board; + } + printk(" Board model (probed, not recommended): DAS-1802HR\n"); + return das1802hr; + break; + case 0x7: + if(dev->board == das1801st || dev->board == das1802st || + dev->board == das1701st || dev->board == das1702st) + { + printk(" Board model: %s\n", (das1800_boards + dev->board)->name); + return dev->board; + } + printk(" Board model (probed, not recommended): DAS-1800ST series\n"); + return das1801st; + break; + case 0x8: + if(dev->board == das1801hc || dev->board == das1802hc) + { + printk(" Board model: %s\n", (das1800_boards + dev->board)->name); + return dev->board; + } + printk(" Board model (probed, not recommended): DAS-1800HC series\n"); + return das1801hc; + break; + default : + printk(" Board model: probe returned 0x%x (unknown)\n", id); + return dev->board; + break; + } + return -1; +} + +static int das1800_cancel(comedi_device *dev, comedi_subdevice *s) +{ + devpriv->forever = 0; + devpriv->count = 0; + disable_das1800(dev); + return 0; +} - /* analog input subdevice */ - s = dev->subdevices + 0; - s->type = COMEDI_SUBD_AI; - s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_GROUND; - if(thisboard->common) - s->subdev_flags |= SDF_COMMON; - s->n_chan = thisboard->qram_len; - s->len_chanlist = thisboard->qram_len; - s->maxdata = (1 << thisboard->resolution) - 1; - s->range_table = das1800_ai_range_lkup[dev->board]; - s->do_cmd = das1800_ai_do_cmd; - s->do_cmdtest = das1800_ai_do_cmdtest; - s->insn_read = das1800_ai_rinsn; - s->cancel = das1800_cancel; +static void das1800_interrupt(int irq, void *d, struct pt_regs *regs) +{ + int status, select; + comedi_device *dev = d; + comedi_subdevice *s = dev->subdevices + 0; /* analog input subdevice */ - /* analog out */ - s = dev->subdevices + 1; - if(thisboard->ao_ability == 1) + status = inb(dev->iobase + DAS1800_STATUS); + /* if interrupt was not caused by das-1800 */ + if(!(status & INT)) { - s->type = COMEDI_SUBD_AO; - s->subdev_flags = SDF_WRITEABLE; - s->n_chan = thisboard->ao_n_chan; - s->maxdata = (1 << thisboard->resolution) - 1; - s->range_table = &range_ao_1; - s->insn_write = das1800_ao_winsn; + return; + } + // save contents of data select register so they can be restored later + select = inb(dev->iobase + DAS1800_SELECT) & 0xf; + // select adc for base address + 0 + if(select != ADC) outb(ADC, dev->iobase + DAS1800_SELECT); + if(devpriv->dma0) /* if dma is enabled and generated the interrupt */ + { + // dma buffer full or about-triggering (stop_src == TRIG_EXT) + if(status & (DMATC | CT0TC)) + das1800_handle_dma(dev, s); + } else if(status & FHF) + { + das1800_handle_fifo_half_full(dev, s); + } else if(status & FNE) + { + das1800_handle_fifo_not_empty(dev, s); + } + comedi_bufcheck(dev, s); + // restore data select register + if(select != ADC) outb(select, dev->iobase + DAS1800_SELECT); + /* if the card's fifo has overflowed */ + if(status & OVF) + { + comedi_error(dev, "DAS1800 FIFO overflow"); + das1800_cancel(dev, s); + comedi_error_done(dev, s); + return; + } + // if stop_src TRIG_EXT has occurred + if(status & CT0TC) devpriv->forever = 0; + /* stop taking data if appropriate */ + if(devpriv->count == 0 && devpriv->forever == 0) + { + disable_das1800(dev); /* disable hardware conversions */ + comedi_done(dev, s); } + + return; +} + +static void das1800_handle_dma(comedi_device *dev, comedi_subdevice *s) +{ + unsigned long flags; + unsigned int numPoints, maxPoints, leftover, residue; + short dpnt; + short *buffer; + int unipolar; + int i; + + flags = claim_dma_lock(); + disable_dma(devpriv->dma_current); + + // figure out how many points to read + maxPoints = devpriv->dma_buf_size / sizeof(short); + /* residue is the number of points left to be done on the dma + * transfer. It should always be zero at this point unless + * the stop_src is set to external triggering. + */ + residue = get_dma_residue(devpriv->dma_current) / sizeof(short); + if(devpriv->forever) + numPoints = maxPoints - residue; + else + numPoints = devpriv->count; + if(numPoints > maxPoints - residue) + numPoints = maxPoints - residue; + // figure out how many points will be stored next time this buffer is used + leftover = 0; + if(devpriv->forever) + leftover = maxPoints; else { - s->type = COMEDI_SUBD_UNUSED; + if(devpriv->dma1) + { + if(devpriv->count - 2 * maxPoints > 0) + leftover = devpriv->count - 2 * maxPoints; + } + else + { + if(devpriv->count - maxPoints > 0) + leftover = devpriv->count - maxPoints; + } } + if(leftover > maxPoints) + leftover = maxPoints; + /* there should only be a residue if collection was stopped by having + * the stop_src set to an external trigger, in which case there + * will be no more data + */ + if(residue) + leftover = 0; - /* di */ - s = dev->subdevices + 2; - s->type = COMEDI_SUBD_DI; - s->subdev_flags = SDF_READABLE; - s->n_chan = 4; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_read = das1800_di_rinsn; + buffer = devpriv->dma_current_buf; - /* do */ - s = dev->subdevices + 3; - s->type = COMEDI_SUBD_DO; - s->subdev_flags = SDF_WRITEABLE; - s->n_chan = thisboard->do_n_chan; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_write = das1800_do_winsn; + /* see if card is using a unipolar or bipolar range */ + unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB; - disable_das1800(dev); + // read data from dma buffer + for( i = 0; i < numPoints; i++) + { + /* write data point to comedi buffer */ + dpnt = buffer[i]; + /* convert to unsigned type if we are in a bipolar mode */ + if(!unipolar); + dpnt += 1 << (thisboard->resolution - 1); + write_to_buffer(dev, s, dpnt); + if(devpriv->count > 0) devpriv->count--; + } - // initialize digital out channels - outb(devpriv->do_bits, dev->iobase + DAS1800_DIGITAL); + // re-enable channel + if(leftover) + { + set_dma_addr(devpriv->dma_current, (unsigned int) devpriv->dma_current_buf); + set_dma_count(devpriv->dma_current, leftover * sizeof(short)); + enable_dma(devpriv->dma_current); + } + release_dma_lock(flags); - // initialize analog out channels - if(thisboard->ao_ability == 1) + // if we are using dual dma, read data from the other channel next time + if(devpriv->dma1) { - // select 'update' dac channel for baseAddress + 0x0 - outb(DAC(thisboard->ao_n_chan - 1), dev->iobase + DAS1800_SELECT); - outw(devpriv->ao_update_bits, dev->iobase + DAS1800_DAC); + if(devpriv->dma_current == devpriv->dma0) + { + devpriv->dma_current = devpriv->dma1; + devpriv->dma_current_buf = devpriv->dma1_buf; + } + else + { + devpriv->dma_current = devpriv->dma0; + devpriv->dma_current_buf = devpriv->dma0_buf; + } } - return 0; -}; + /* clear interrupt */ + outb(FNE, dev->iobase + DAS1800_STATUS); -static int das1800_detach(comedi_device *dev) + return; +} + +static void das1800_handle_fifo_half_full(comedi_device *dev, comedi_subdevice *s) { - printk("comedi%d: das1800: remove\n", dev->minor); + int i; /* loop index */ + int numPoints = 0; /* number of points to read */ + sampl_t dpnt; + int unipolar; - /* only free stuff if it has been allocated by _attach */ - if(dev->iobase) - release_region(dev->iobase, DAS1800_SIZE); - if(devpriv->iobase2) - release_region(devpriv->iobase2, DAS1800_SIZE); - if(dev->irq) - comedi_free_irq(dev->irq, dev); - if(devpriv->dma0) - free_dma(devpriv->dma0); - if(devpriv->dma0_buf) - kfree(devpriv->dma0_buf); - if(devpriv->dma1) - free_dma(devpriv->dma1); - if(devpriv->dma1_buf) - kfree(devpriv->dma1_buf); + unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB; - return 0; -}; + numPoints = HALF_FIFO; + /* if we only need some of the points */ + if( devpriv->forever == 0 && devpriv->count < numPoints) + numPoints = devpriv->count; + for( i = 0; i < numPoints; i++) + { + /* write data point to buffer */ + dpnt = inw(dev->iobase + DAS1800_FIFO); + /* convert to unsigned type if we are in a bipolar mode */ + if(!unipolar); + dpnt += 1 << (thisboard->resolution - 1); + write_to_buffer(dev, s, dpnt); + if(devpriv->count > 0) devpriv->count--; + } + /* clear interrupt */ + outb(FNE, dev->iobase + DAS1800_STATUS); + // if there are just a few points left, switch to interrupt on end of conversion + if(devpriv->count < HALF_FIFO && devpriv->count > 0 && devpriv->forever == 0) + { + devpriv->irq_dma_bits &= ~FIMD; // interrupt fifo not empty + outb(devpriv->irq_dma_bits, dev->iobase + DAS1800_CONTROL_B); + } + return; +} -static int das1800_cancel(comedi_device *dev, comedi_subdevice *s) +static void das1800_handle_fifo_not_empty(comedi_device *dev, comedi_subdevice *s) { - devpriv->forever = 0; - devpriv->count = 0; - disable_das1800(dev); - return 0; + sampl_t dpnt; + int unipolar; + + unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB; + + while(inb(dev->iobase + DAS1800_STATUS) & FNE) + { + dpnt = inw(dev->iobase + DAS1800_FIFO); + /* convert to unsigned type if we are in a bipolar mode */ + if(!unipolar); + dpnt += 1 << (thisboard->resolution - 1); + write_to_buffer(dev, s, dpnt); + if(devpriv->count > 0) devpriv->count--; + if(devpriv->count == 0 && devpriv->forever == 0) + break; + } + /* clear interrupt */ + outb(FNE, dev->iobase + DAS1800_STATUS); + + return; +} + +/* utility function used by das1800 interrupt service routines */ +inline void write_to_buffer(comedi_device *dev, comedi_subdevice *s, sampl_t +data_point) { + if(s->buf_int_ptr >= s->cur_trig.data_len ) + { + s->buf_int_ptr = 0; + comedi_eobuf(dev, s); + } + *((sampl_t *)((void *)s->cur_trig.data + s->buf_int_ptr)) = data_point; + s->cur_chan++; + if(s->cur_chan >= s->cur_chanlist_len) + { + s->cur_chan = 0; + comedi_eos(dev, s); + } + s->buf_int_count += sizeof(sampl_t); + s->buf_int_ptr += sizeof(sampl_t); } void disable_das1800(comedi_device *dev) @@ -1111,7 +1144,8 @@ static int das1800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_c cmd->convert_src != TRIG_EXT) err++; if(cmd->scan_end_src != TRIG_COUNT) err++; if(cmd->stop_src != TRIG_COUNT && - cmd->stop_src != TRIG_NONE) err++; + cmd->stop_src != TRIG_NONE && + cmd->stop_src != TRIG_EXT) err++; //compatibility check if(cmd->scan_begin_src != TRIG_FOLLOW && cmd->convert_src != TRIG_TIMER) err++; @@ -1335,6 +1369,7 @@ static int das1800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s) devpriv->irq_dma_bits |= FIMD; //interrupt fifo half full break; case TRIG_NONE: + case TRIG_EXT: devpriv->forever = 1; devpriv->count = 0; devpriv->irq_dma_bits |= FIMD; @@ -1366,20 +1401,36 @@ static int das1800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s) } outb(devpriv->irq_dma_bits, dev->iobase + DAS1800_CONTROL_B); // enable irq/dma + + // set up dma if(devpriv->dma0) { lock_flags = claim_dma_lock(); disable_dma(devpriv->dma0); set_dma_addr(devpriv->dma0, (unsigned int) devpriv->dma0_buf); + // set appropriate size of transfer if(devpriv->count * sizeof(short) > devpriv->dma_buf_size || devpriv->forever) set_dma_count(devpriv->dma0, devpriv->dma_buf_size); else set_dma_count(devpriv->dma0, devpriv->count * sizeof(short)); + // set up dual dma if appropriate + if(devpriv->dma1 && (devpriv->count * sizeof(short) > devpriv->dma_buf_size || devpriv->forever)) + { + disable_dma(devpriv->dma1); + set_dma_addr(devpriv->dma1, (unsigned int) devpriv->dma1_buf); + // set appropriate size of transfer + if(devpriv->count * sizeof(short) > 2 * devpriv->dma_buf_size || devpriv->forever) + set_dma_count(devpriv->dma1, devpriv->dma_buf_size); + else + set_dma_count(devpriv->dma1, devpriv->count * sizeof(short) - devpriv->dma_buf_size); + enable_dma(devpriv->dma1); + } devpriv->dma_current = devpriv->dma0; devpriv->dma_current_buf = devpriv->dma0_buf; enable_dma(devpriv->dma0); release_dma_lock(lock_flags); } + outb(control_a, dev->iobase + DAS1800_CONTROL_A); /* enable fifo and triggering */ outb(CVEN, dev->iobase + DAS1800_STATUS); /* enable conversions */ -- 2.26.2