📄 das800.c
字号:
int irq = it->options[1]; unsigned long irq_flags; int board; printk("comedi%d: das800: io 0x%x", dev->minor, iobase); if(irq) { printk(", irq %i", irq); } printk("\n"); /* allocate and initialize dev->private */ if(alloc_private(dev, sizeof(das800_private)) < 0) return -ENOMEM; if(iobase == 0) { printk("io base address required for das800\n"); return -EINVAL; } /* check if io addresses are available */ if(check_region(iobase, DAS800_SIZE) < 0) { printk("I/O port conflict\n"); return -EIO; } request_region(iobase, DAS800_SIZE, "das800"); dev->iobase = iobase; board = das800_probe(dev); if(board < 0) { printk("unable to determine board type\n"); return -ENODEV; } dev->board_ptr = das800_boards + board; /* grab our IRQ */ if(irq == 1 || irq > 7 || irq < 0) { printk("irq out of range\n"); return -EINVAL; } if(irq) { if(comedi_request_irq( irq, das800_interrupt, 0, "das800", dev )) { printk( "unable to allocate irq %d\n", irq); return -EINVAL; } } dev->irq = irq; dev->board_name = thisboard->name; if(alloc_subdevices(dev, 3) < 0) return -ENOMEM; /* analog input subdevice */ s = dev->subdevices + 0; dev->read_subdev = s; s->type = COMEDI_SUBD_AI; s->subdev_flags = SDF_READABLE | SDF_GROUND; s->n_chan = 8; s->len_chanlist = 8; s->maxdata = (1 << thisboard->resolution) - 1; s->range_table = thisboard->ai_range; s->do_cmd = das800_ai_do_cmd; s->do_cmdtest = das800_ai_do_cmdtest; s->insn_read = das800_ai_rinsn; s->cancel = das800_cancel; /* di */ s = dev->subdevices + 1; s->type=COMEDI_SUBD_DI; s->subdev_flags = SDF_READABLE; s->n_chan = 3; s->maxdata = 1; s->range_table = &range_digital; s->insn_bits = das800_di_rbits; /* do */ s = dev->subdevices + 2; s->type=COMEDI_SUBD_DO; s->subdev_flags = SDF_WRITABLE | SDF_READABLE; s->n_chan = 4; s->maxdata = 1; s->range_table = &range_digital; s->insn_bits = das800_do_wbits; disable_das800(dev); /* initialize digital out channels */ comedi_spin_lock_irqsave(&dev->spinlock, irq_flags); outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */ outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1); comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags); return 0;};static int das800_detach(comedi_device *dev){ printk("comedi%d: das800: remove\n", dev->minor); /* 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; disable_das800(dev); return 0;}/* enable_das800 makes the card start taking hardware triggered conversions */static void enable_das800(comedi_device *dev){ unsigned long irq_flags; comedi_spin_lock_irqsave(&dev->spinlock, irq_flags); // enable fifo-half full interrupts for cio-das802/16 if(thisboard->resolution == 16) outb(CIO_ENHF, dev->iobase + DAS800_GAIN); outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */ outb(CONV_HCEN, dev->iobase + DAS800_CONV_CONTROL); /* enable hardware triggering */ outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */ outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1); /* enable card's interrupt */ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);}/* disable_das800 stops hardware triggered conversions */static void disable_das800(comedi_device *dev){ unsigned long irq_flags; comedi_spin_lock_irqsave(&dev->spinlock, irq_flags); 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 */ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);}static int das800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd){ int err = 0; int tmp; int gain, startChan; int i; /* step 1: make sure trigger sources are trivially valid */ tmp = cmd->start_src; cmd->start_src &= TRIG_NOW | TRIG_EXT; 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++; if(err) return 1; /* step 2: make sure trigger sources are unique and mutually compatible */ if(cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT) 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 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) { cmd->convert_arg = thisboard->ai_speed; err++; } } if(!cmd->chanlist_len) { cmd->chanlist_len = 1; 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) { cmd->stop_arg = 1; err++; } } else { /* TRIG_NONE */ if(cmd->stop_arg != 0) { cmd->stop_arg = 0; err++; } } if(err)return 3; /* step 4: fix up any arguments */ if(cmd->convert_src == TRIG_TIMER) { tmp = cmd->convert_arg; /* calculate counter values that give desired timing */ i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1), &(devpriv->divisor2), &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK); if(tmp != cmd->convert_arg) err++; } if(err)return 4; // check channel/gain list against card's limitations if(cmd->chanlist) { gain = CR_RANGE(cmd->chanlist[0]); startChan = CR_CHAN(cmd->chanlist[0]); for(i = 1; i < cmd->chanlist_len; i++) { if(CR_CHAN(cmd->chanlist[i]) != (startChan + i) % N_CHAN_AI) { comedi_error(dev, "entries in chanlist must be consecutive channels, counting upwards\n"); err++; } if(CR_RANGE(cmd->chanlist[i]) != gain) { comedi_error(dev, "entries in chanlist must all have the same gain\n"); err++; } } } if(err)return 5; return 0;}static int das800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s){ int startChan, endChan, scan, gain; int conv_bits; unsigned long irq_flags; comedi_async *async = s->async; if(!dev->irq) { comedi_error(dev, "no irq assigned for das-800, cannot do hardware conversions"); return -1; } disable_das800(dev); /* set channel scan limits */ startChan = CR_CHAN(async->cmd.chanlist[0]); endChan = (startChan + async->cmd.chanlist_len - 1) % 8; scan = (endChan << 3) | startChan; comedi_spin_lock_irqsave(&dev->spinlock, irq_flags); outb(SCAN_LIMITS, dev->iobase + DAS800_GAIN); /* select base address + 2 to be scan limits register */ outb(scan, dev->iobase + DAS800_SCAN_LIMITS); /* set scan limits */ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags); /* set gain */ gain = CR_RANGE(async->cmd.chanlist[0]); if( thisboard->resolution == 12 && gain > 0) gain += 0x7; gain &= 0xf; outb(gain, dev->iobase + DAS800_GAIN); switch(async->cmd.stop_src) { case TRIG_COUNT: devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len; devpriv->forever = 0; break; case TRIG_NONE: devpriv->forever = 1; devpriv->count = 0; break; default : break; } /* enable auto channel scan, send interrupts on end of conversion * and set clock source to internal or external */ conv_bits = 0; conv_bits |= EACS | IEOC; if(async->cmd.start_src == TRIG_EXT) conv_bits |= DTEN; switch(async->cmd.convert_src) { case TRIG_TIMER: conv_bits |= CASC | ITE; /* set conversion frequency */ i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1), &(devpriv->divisor2), &(async->cmd.convert_arg), async->cmd.flags & TRIG_ROUND_MASK); if(das800_set_frequency(dev) < 0) { comedi_error(dev, "Error setting up counters"); return -1; } break; case TRIG_EXT: break; default: break; } comedi_spin_lock_irqsave(&dev->spinlock, irq_flags); outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */ outb(conv_bits, dev->iobase + DAS800_CONV_CONTROL); comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags); async->events = 0; enable_das800(dev); 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; int timeout = 1000; unsigned long irq_flags; disable_das800(dev); /* disable hardware conversions (enables software conversions) */ /* set multiplexer */ chan = CR_CHAN(insn->chanspec); comedi_spin_lock_irqsave(&dev->spinlock, irq_flags); outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */ outb(chan | devpriv->do_bits, dev->iobase + DAS800_CONTROL1); comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags); /* set gain / range */ range = CR_RANGE(insn->chanspec); if(thisboard->resolution == 12 && range) range += 0x7; range &= 0xf; outb(range, dev->iobase + DAS800_GAIN); comedi_udelay(5); for(n = 0; n < insn->n; n++) { /* trigger conversion */ outb_p(0, dev->iobase + DAS800_MSB); for(i = 0; i < timeout; i++) { if(!(inb(dev->iobase + 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); if(thisboard->resolution == 12) { data[n] = (lsb >> 4) & 0xff; data[n] |= (msb << 4); }else { data[n] = (msb << 8) | lsb; } } return n;}static int das800_di_rbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){ lsampl_t bits; bits = inb(dev->iobase + DAS800_STATUS) >> 4; bits &= 0x7; data[1] = bits; data[0] = 0; return 2;}static int das800_do_wbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){ int wbits; unsigned long irq_flags; // only set bits that have been masked data[0] &= 0xf; wbits = devpriv->do_bits >> 4; wbits &= ~data[0]; wbits |= data[0] & data[1]; devpriv->do_bits = wbits << 4; comedi_spin_lock_irqsave(&dev->spinlock, irq_flags); outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */ outb(devpriv->do_bits | CONTROL1_INTE, dev->iobase + DAS800_CONTROL1); comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags); data[1] = wbits; return 2;}/* loads counters with divisor1, divisor2 from private structure */static int das800_set_frequency(comedi_device *dev){ int err = 0; if(i8254_load(dev->iobase + DAS800_8254, 1, devpriv->divisor1, 2)) err++; if(i8254_load(dev->iobase + DAS800_8254, 2, devpriv->divisor2, 2)) err++; if(err) return -1; return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -