📄 das16.c
字号:
id: 0xc0 }, { name: "cio-das1401/12", // cio-das1400_series.pdf ai: das16_ai_rinsn, ai_nbits: 12, ai_speed: 6250, ai_pg: das16_pg_1601, ao: NULL, di: das16_di_rbits, do_: das16_do_wbits, i8255_offset: 0, i8254_offset: 0x0c, size: 0x408, id: 0xc0 }, { name: "cio-das1402/12", // cio-das1400_series.pdf ai: das16_ai_rinsn, ai_nbits: 12, ai_speed: 6250, ai_pg: das16_pg_1602, ao: NULL, di: das16_di_rbits, do_: das16_do_wbits, i8255_offset: 0, i8254_offset: 0x0c, size: 0x408, id: 0xc0 }, { name: "cio-das1402/16", // cio-das1400_series.pdf ai: das16_ai_rinsn, ai_nbits: 16, ai_speed: 10000, ai_pg: das16_pg_1602, ao: NULL, di: das16_di_rbits, do_: das16_do_wbits, i8255_offset: 0, i8254_offset: 0x0c, size: 0x408, id: 0xc0 }, { name: "cio-das1601/12", // cio-das160x-1x.pdf ai: das16_ai_rinsn, ai_nbits: 12, ai_speed: 6250, ai_pg: das16_pg_1601, ao: das16_ao_winsn, ao_nbits: 12, di: das16_di_rbits, do_: das16_do_wbits, i8255_offset: 0x400, i8254_offset: 0x0c, size: 0x408, id: 0xc0 }, { name: "cio-das1602/12", // cio-das160x-1x.pdf ai: das16_ai_rinsn, ai_nbits: 12, ai_speed: 10000, ai_pg: das16_pg_1602, ao: das16_ao_winsn, ao_nbits: 12, di: das16_di_rbits, do_: das16_do_wbits, i8255_offset: 0x400, i8254_offset: 0x0c, size: 0x408, id: 0xc0 }, { name: "cio-das1602/16", // cio-das160x-1x.pdf ai: das16_ai_rinsn, ai_nbits: 16, ai_speed: 10000, ai_pg: das16_pg_1602, ao: das16_ao_winsn, ao_nbits: 12, di: das16_di_rbits, do_: das16_do_wbits, i8255_offset: 0x400, i8254_offset: 0x0c, size: 0x408, id: 0xc0 }, { name: "cio-das16/330", // ? ai: das16_ai_rinsn, ai_nbits: 12, ai_speed: 3030, ai_pg: das16_pg_16jr, ao: NULL, di: das16_di_rbits, do_: das16_do_wbits, i8255_offset: 0, i8254_offset: 0x0c, size: 0x14, id: 0xf0 },#if 0 { name: "das16/330i", // ? }, { name: "das16/jr/ctr5", // ? }, { name: "cio-das16/m1/16", // cio-das16_m1_16.pdf, this board is a bit quirky, no dma },#endif};#define n_das16_boards ((sizeof(das16_boards))/(sizeof(das16_board)))static int das16_attach(comedi_device *dev,comedi_devconfig *it);static int das16_detach(comedi_device *dev);static comedi_driver driver_das16={ driver_name: "das16", module: THIS_MODULE, attach: das16_attach, detach: das16_detach, board_name: das16_boards, num_names: n_das16_boards, offset: sizeof(das16_boards[0]),};#define DAS16_TIMEOUT 1000static const int timer_period = HZ / 20 + 1; // period for timer interrupt in jiffies (about 1/20 of a second)struct das16_private_struct{ unsigned int ai_unipolar; // unipolar flag unsigned int ai_singleended; // single ended flag unsigned int clockbase; // master clock speed in ns volatile unsigned int control_state; // dma, interrupt and trigger control bits volatile unsigned long adc_byte_count; // number of samples remaining unsigned int divisor1; // divisor dividing master clock to get conversion frequency unsigned int divisor2; // divisor dividing master clock to get conversion frequency unsigned int dma_chan; // dma channel uint16_t *dma_buffer[2]; dma_addr_t dma_buffer_addr[2]; unsigned int current_buffer; volatile unsigned int dma_transfer_size; // target number of bytes to transfer per dma shot // user-defined analog input and output ranges defined from config options comedi_lrange *user_ai_range_table; comedi_lrange *user_ao_range_table; struct munge_info ai_munge_info; struct timer_list timer; // for timed interrupt volatile unsigned int timer_running : 1; volatile unsigned int timer_mode : 1; // true if using timer mode};#define devpriv ((struct das16_private_struct *)(dev->private))#define thisboard ((struct das16_board_struct *)(dev->board_ptr))static int das16_cmd_test(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd){ int err=0, tmp; int gain, start_chan, i; int mask; /* make sure triggers are valid */ tmp=cmd->start_src; cmd->start_src &= TRIG_NOW; if(!cmd->start_src || tmp!=cmd->start_src)err++; tmp=cmd->scan_begin_src; mask = TRIG_FOLLOW; // if board supports burst mode if(thisboard->size > 0x400) mask |= TRIG_TIMER | TRIG_EXT; cmd->scan_begin_src &= mask; if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++; tmp=cmd->convert_src; mask = TRIG_TIMER | TRIG_EXT; // if board supports burst mode if(thisboard->size > 0x400) mask |= TRIG_NOW; cmd->convert_src &= mask; 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->scan_begin_src != TRIG_TIMER && cmd->scan_begin_src != TRIG_EXT && cmd->scan_begin_src != TRIG_FOLLOW) err++; if(cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW) err++; if(cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT) err++; // make sure scan_begin_src and convert_src dont conflict if(cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW) err++; if(cmd->scan_begin_src != TRIG_FOLLOW && cmd->convert_src != TRIG_NOW) 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->scan_begin_src==TRIG_FOLLOW) { /* internal trigger */ if(cmd->scan_begin_arg!=0) { cmd->scan_begin_arg=0; err++; } } if(cmd->scan_end_arg != cmd->chanlist_len) { cmd->scan_end_arg = cmd->chanlist_len; err++; } // check against maximum frequency if(cmd->scan_begin_src == TRIG_TIMER) { if(cmd->scan_begin_arg < thisboard->ai_speed * cmd->chanlist_len) { cmd->scan_begin_arg = thisboard->ai_speed * cmd->chanlist_len; err++; } } if(cmd->convert_src == TRIG_TIMER) { if(cmd->convert_arg < thisboard->ai_speed) { cmd->convert_arg = thisboard->ai_speed; err++; } } if(cmd->stop_src == TRIG_NONE) { if(cmd->stop_arg != 0) { cmd->stop_arg = 0; err++; } } if(err)return 3; // step 4: fix up arguments if(cmd->scan_begin_src == TRIG_TIMER) { unsigned int tmp = cmd->scan_begin_arg; // set divisors, correct timing arguments i8253_cascade_ns_to_timer_2div(devpriv->clockbase, &(devpriv->divisor1), &(devpriv->divisor2), &(cmd->scan_begin_arg), cmd->flags & TRIG_ROUND_MASK); err += (tmp!=cmd->scan_begin_arg); } if(cmd->convert_src == TRIG_TIMER) { unsigned int tmp = cmd->convert_arg; // set divisors, correct timing arguments i8253_cascade_ns_to_timer_2div(devpriv->clockbase, &(devpriv->divisor1), &(devpriv->divisor2), &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK); err += (tmp!=cmd->convert_arg); } if(err)return 4; // check channel/gain list against card's limitations if(cmd->chanlist){ gain = CR_RANGE(cmd->chanlist[0]); start_chan = CR_CHAN(cmd->chanlist[0]); for(i = 1; i < cmd->chanlist_len; i++) { if(CR_CHAN(cmd->chanlist[i]) != (start_chan + i) % s->n_chan) { 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 das16_cmd_exec(comedi_device *dev,comedi_subdevice *s){ comedi_async *async = s->async; comedi_cmd *cmd = &async->cmd; unsigned int byte; unsigned long flags; int range; if(devpriv->dma_chan == 0 || (dev->irq == 0 && devpriv->timer_mode == 0)) { comedi_error(dev, "irq (or use of 'timer mode') dma required to execute comedi_cmd"); return -1; } if(cmd->flags & TRIG_RT) { comedi_error(dev, "isa dma transfers cannot be performed with TRIG_RT, aborting"); return -1; } devpriv->adc_byte_count = cmd->stop_arg * cmd->chanlist_len * sizeof( uint16_t ); // disable conversions for das1600 mode if(thisboard->size > 0x400) { outb(DAS1600_CONV_DISABLE, dev->iobase + DAS1600_CONV); } // set scan limits byte = CR_CHAN(cmd->chanlist[0]); byte |= CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]) << 4; outb(byte, dev->iobase + DAS16_MUX); /* set gain (this is also burst rate register but according to * computer boards manual, burst rate does nothing, even on keithley cards) */ if(thisboard->ai_pg != das16_pg_none){ range = CR_RANGE(cmd->chanlist[0]); outb((das16_gainlists[thisboard->ai_pg])[range], dev->iobase+DAS16_GAIN); } /* set counter mode and counts */ cmd->convert_arg = das16_set_pacer(dev, cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK); DEBUG_PRINT("pacer period: %d ns\n", cmd->convert_arg); /* enable counters */ byte = 0; /* Enable burst mode if appropriate. */ if(thisboard->size > 0x400) { if(cmd->convert_src == TRIG_NOW) { outb(DAS1600_BURST_VAL, dev->iobase + DAS1600_BURST); // set burst length byte |= BURST_LEN_BITS(cmd->chanlist_len - 1); }else { outb(0, dev->iobase + DAS1600_BURST); } } outb(byte, dev->iobase + DAS16_PACER); // set up dma transfer flags = claim_dma_lock(); disable_dma(devpriv->dma_chan); /* clear flip-flop to make sure 2-byte registers for * count and address get set correctly */ clear_dma_ff(devpriv->dma_chan); init_munge_info( &devpriv->ai_munge_info ); devpriv->current_buffer = 0; set_dma_addr( devpriv->dma_chan, devpriv->dma_buffer_addr[ devpriv->current_buffer ] ); // set appropriate size of transfer devpriv->dma_transfer_size = das16_suggest_transfer_size(dev, *cmd); set_dma_count(devpriv->dma_chan, devpriv->dma_transfer_size); enable_dma(devpriv->dma_chan); release_dma_lock(flags); // set up interrupt if( devpriv->timer_mode ) { devpriv->timer_running = 1; devpriv->timer.expires = jiffies + timer_period; add_timer(&devpriv->timer); devpriv->control_state &= ~DAS16_INTE; }else { /* clear interrupt bit */ outb(0x00, dev->iobase + DAS16_STATUS); /* enable interrupts */ devpriv->control_state |= DAS16_INTE; } devpriv->control_state |= DMA_ENABLE; devpriv->control_state &= ~PACING_MASK; if(cmd->convert_src == TRIG_EXT) devpriv->control_state |= EXT_PACER; else devpriv->control_state |= INT_PACER; outb(devpriv->control_state, dev->iobase + DAS16_CONTROL); /* Enable conversions if using das1600 mode */ if(thisboard->size > 0x400) { outb(0, dev->iobase + DAS1600_CONV); } return 0;}static int das16_cancel(comedi_device *dev, comedi_subdevice *s){ unsigned long flags; comedi_spin_lock_irqsave( &dev->spinlock, flags ); /* disable interrupts, dma and pacer clocked conversions */ devpriv->control_state &= ~DAS16_INTE & ~PACING_MASK & ~DMA_ENABLE; outb(devpriv->control_state, dev->iobase + DAS16_CONTROL); if(devpriv->dma_chan) disable_dma(devpriv->dma_chan); // disable SW timer if( devpriv->timer_mode && devpriv->timer_running ) { devpriv->timer_running = 0; del_timer(&devpriv->timer); } /* disable burst mode */ if(thisboard->size > 0x400) { outb(0, dev->iobase + DAS1600_BURST); } comedi_spin_unlock_irqrestore( &dev->spinlock, flags ); return 0;}static void das16_reset(comedi_device *dev){ outb(0, dev->iobase + DAS16_STATUS); outb(0, dev->iobase + DAS16_CONTROL); outb(0, dev->iobase + DAS16_PACER); outb(0, dev->iobase + DAS16_CNTR_CONTROL);}static int das16_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data){ int i,n; int range; int chan; int msb,lsb; // disable interrupts and pacing devpriv->control_state &= ~DAS16_INTE & ~DMA_ENABLE & ~PACING_MASK; outb(devpriv->control_state, dev->iobase + DAS16_CONTROL); /* set multiplexer */ chan = CR_CHAN(insn->chanspec); chan |= CR_CHAN(insn->chanspec) << 4; outb(chan, dev->iobase + DAS16_MUX); /* set gain */ if(thisboard->ai_pg != das16_pg_none) { range = CR_RANGE(insn->chanspec); outb((das16_gainlists[thisboard->ai_pg])[range], dev->iobase+DAS16_GAIN); } for(n = 0; n < insn->n; n++) { /* trigger conversion */ outb_p(0, dev->iobase + DAS16_TRIG); for(i = 0; i < DAS16_TIMEOUT; i++) { if(!(inb(dev->iobase + DAS16_STATUS) & BUSY)) break; } if(i == DAS16_TIMEOUT) { rt_printk("das16: timeout\n"); return -ETIME; } msb = inb(dev->iobase + DAS16_AI_MSB); lsb = inb(dev->iobase + DAS16_AI_LSB); if(thisboard->ai_nbits == 12){ data[n] = ((lsb >> 4) & 0xf) | (msb << 4); }else{ data[n] = lsb | (msb << 8); } } return n;}static int das16_di_rbits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data){ lsampl_t bits; bits = inb(dev->iobase + DAS16_DIO) & 0xf; data[1] = bits; data[0] = 0; return 2;}static int das16_do_wbits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data){ lsampl_t wbits; // only set bits that have been masked data[0] &= 0xf; wbits = s->state; // zero bits that have been masked wbits &= ~data[0]; // set masked bits wbits |= data[0] & data[1]; s->state = wbits; data[1] = wbits; outb(s->state, dev->iobase + DAS16_DIO); return 2;}static int das16_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data){ int i; int lsb,msb; int chan; chan = CR_CHAN(insn->chanspec); for(i = 0; i < insn->n; i++) { if(thisboard->ao_nbits == 12) { lsb = (data[i] << 4) & 0xff; msb = (data[i] >> 4) & 0xff; }else { lsb = data[i] & 0xff; msb = (data[i] >> 8) & 0xff; } outb(lsb,dev->iobase+DAS16_AO_LSB(chan)); outb(msb,dev->iobase+DAS16_AO_MSB(chan)); } return i;}static void das16_dma_interrupt(int irq, void *d, struct pt_regs *regs){ int status; comedi_device *dev = d; status = inb(dev->iobase + DAS16_STATUS); if((status & DAS16_INT ) == 0) { DEBUG_PRINT( "spurious interrupt\n" ); return; } /* clear interrupt */ outb(0x00, dev->iobase + DAS16_STATUS); das16_interrupt(dev);}static void das16_timer_interrupt(unsigned long arg){ comedi_device *dev = (comedi_device*) arg; das16_interrupt(dev);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -