📄 das1800.c
字号:
return s->async->buf_write_count - s->async->buf_read_count;}static void das1800_interrupt(int irq, void *d, struct pt_regs *regs){ comedi_device *dev = d; unsigned int status; if(dev->attached == 0) { comedi_error(dev, "premature interrupt"); return; } /* Prevent race with das1800_ai_poll() on multi processor systems. * Also protects indirect addressing in das1800_ai_handler */ spin_lock(&dev->spinlock); status = inb(dev->iobase + DAS1800_STATUS); /* if interrupt was not caused by das-1800 */ if(!(status & INT)) { spin_unlock(&dev->spinlock); return; } /* clear the interrupt status bit INT*/ outb(CLEAR_INTR_MASK & ~INT, dev->iobase + DAS1800_STATUS); // handle interrupt das1800_ai_handler(dev); spin_unlock(&dev->spinlock);}// the guts of the interrupt handler, that is shared with das1800_ai_pollstatic void das1800_ai_handler(comedi_device *dev){ comedi_subdevice *s = dev->subdevices + 0; /* analog input subdevice */ comedi_async *async = s->async; comedi_cmd *cmd = &async->cmd; unsigned int status = inb(dev->iobase + DAS1800_STATUS); async->events = 0; // select adc for base address + 0 outb(ADC, dev->iobase + DAS1800_SELECT); // dma buffer full if(devpriv->irq_dma_bits & DMA_ENABLED) { // look for data from dma transfer even if dma terminal count hasn't happened yet das1800_handle_dma(dev, s, status); }else if(status & FHF) { // if fifo half full das1800_handle_fifo_half_full(dev, s); }else if(status & FNE) { // if fifo not empty das1800_handle_fifo_not_empty(dev, s); } async->events |= COMEDI_CB_BLOCK; /* if the card's fifo has overflowed */ if(status & OVF) { // clear OVF interrupt bit outb(CLEAR_INTR_MASK & ~OVF, dev->iobase + DAS1800_STATUS); comedi_error(dev, "DAS1800 FIFO overflow"); das1800_cancel(dev, s); async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; comedi_event(dev, s, async->events); return; } // stop taking data if appropriate /* stop_src TRIG_EXT */ if(status & CT0TC) { // clear CT0TC interrupt bit outb(CLEAR_INTR_MASK & ~CT0TC, dev->iobase + DAS1800_STATUS); // make sure we get all remaining data from board before quitting if(devpriv->irq_dma_bits & DMA_ENABLED) das1800_flush_dma(dev, s); else das1800_handle_fifo_not_empty(dev, s); das1800_cancel(dev, s); /* disable hardware conversions */ async->events |= COMEDI_CB_EOA; }else if(cmd->stop_src == TRIG_COUNT && devpriv->count == 0) { // stop_src TRIG_COUNT das1800_cancel(dev, s); /* disable hardware conversions */ async->events |= COMEDI_CB_EOA; } comedi_event(dev, s, async->events); return;}static void das1800_handle_dma(comedi_device *dev, comedi_subdevice *s, unsigned int status){ unsigned long flags; const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL; flags = claim_dma_lock(); das1800_flush_dma_channel(dev, s, devpriv->dma_current, devpriv->dma_current_buf); // re-enable dma channel set_dma_addr(devpriv->dma_current, virt_to_bus(devpriv->dma_current_buf)); set_dma_count(devpriv->dma_current, devpriv->dma_transfer_size); enable_dma(devpriv->dma_current); release_dma_lock(flags); if(status & DMATC) { // clear DMATC interrupt bit outb(CLEAR_INTR_MASK & ~DMATC, dev->iobase + DAS1800_STATUS); // switch dma channels for next time, if appropriate if(dual_dma) { // read data from the other channel next time if(devpriv->dma_current == devpriv->dma0) { devpriv->dma_current = devpriv->dma1; devpriv->dma_current_buf = devpriv->ai_buf1; } else { devpriv->dma_current = devpriv->dma0; devpriv->dma_current_buf = devpriv->ai_buf0; } } } return;}static inline uint16_t munge_bipolar_sample( const comedi_device *dev, uint16_t sample ){ sample += 1 << ( thisboard->resolution - 1 ); return sample;}static void munge_data( comedi_device *dev, uint16_t *array, unsigned int num_elements ){ unsigned int i; int unipolar; /* see if card is using a unipolar or bipolar range so we can munge data correctly */ unipolar = inb( dev->iobase + DAS1800_CONTROL_C ) & UB; /* convert to unsigned type if we are in a bipolar mode */ if( !unipolar ) { for( i = 0; i < num_elements; i++ ) { array[i] = munge_bipolar_sample( dev, array[i] ); } }}/* Utility function used by das1800_flush_dma() and das1800_handle_dma(). * Assumes dma lock is held */static void das1800_flush_dma_channel(comedi_device *dev, comedi_subdevice *s, unsigned int channel, uint16_t *buffer){ unsigned int num_bytes, num_samples; comedi_cmd *cmd = &s->async->cmd; disable_dma(channel); /* clear flip-flop to make sure 2-byte registers * get set correctly */ clear_dma_ff(channel); // figure out how many points to read num_bytes = devpriv->dma_transfer_size - get_dma_residue( channel ); num_samples = num_bytes / sizeof( sampl_t ); /* if we only need some of the points */ if( cmd->stop_src == TRIG_COUNT && devpriv->count < num_samples ) num_samples = devpriv->count; munge_data( dev, buffer, num_samples ); cfc_write_array_to_buffer( s, buffer, num_bytes ); if(s->async->cmd.stop_src == TRIG_COUNT) devpriv->count -= num_samples; return;}/* flushes remaining data from board when external trigger has stopped aquisition * and we are using dma transfers */static void das1800_flush_dma(comedi_device *dev, comedi_subdevice *s){ unsigned long flags; const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL; flags = claim_dma_lock(); das1800_flush_dma_channel(dev, s, devpriv->dma_current, devpriv->dma_current_buf); if(dual_dma) { // switch to other channel and flush it if(devpriv->dma_current == devpriv->dma0) { devpriv->dma_current = devpriv->dma1; devpriv->dma_current_buf = devpriv->ai_buf1; } else { devpriv->dma_current = devpriv->dma0; devpriv->dma_current_buf = devpriv->ai_buf0; } das1800_flush_dma_channel(dev, s, devpriv->dma_current, devpriv->dma_current_buf); } release_dma_lock(flags); // get any remaining samples in fifo das1800_handle_fifo_not_empty(dev, s); return;}static void das1800_handle_fifo_half_full(comedi_device *dev, comedi_subdevice *s){ int numPoints = 0; /* number of points to read */ comedi_cmd *cmd = &s->async->cmd; numPoints = FIFO_SIZE / 2; /* if we only need some of the points */ if(cmd->stop_src == TRIG_COUNT && devpriv->count < numPoints) numPoints = devpriv->count; insw(dev->iobase + DAS1800_FIFO, devpriv->ai_buf0, numPoints); munge_data( dev, devpriv->ai_buf0, numPoints ); cfc_write_array_to_buffer( s, devpriv->ai_buf0, numPoints * sizeof( devpriv->ai_buf0[0] ) ); if( cmd->stop_src == TRIG_COUNT ) devpriv->count -= numPoints; return;}static void das1800_handle_fifo_not_empty(comedi_device *dev, comedi_subdevice *s){ sampl_t dpnt; int unipolar; comedi_cmd *cmd = &s->async->cmd; unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB; while( inb( dev->iobase + DAS1800_STATUS ) & FNE ) { if(cmd->stop_src == TRIG_COUNT && devpriv->count == 0) break; dpnt = inw( dev->iobase + DAS1800_FIFO ); /* convert to unsigned type if we are in a bipolar mode */ if(!unipolar); dpnt = munge_bipolar_sample( dev, dpnt ); cfc_write_to_buffer( s, dpnt ); if(cmd->stop_src == TRIG_COUNT) devpriv->count--; } return;}static int das1800_cancel(comedi_device *dev, comedi_subdevice *s){ outb(0x0, dev->iobase + DAS1800_STATUS); /* disable conversions */ outb(0x0, dev->iobase + DAS1800_CONTROL_B); /* disable interrupts and dma */ outb(0x0, dev->iobase + DAS1800_CONTROL_A); /* disable and clear fifo and stop triggering */ if(devpriv->dma0) disable_dma(devpriv->dma0); if(devpriv->dma1) disable_dma(devpriv->dma1); return 0;}/* test analog input cmd */static int das1800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd){ int err = 0; int tmp; unsigned int tmp_arg; int i; int unipolar; /* 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 | TRIG_TIMER | TRIG_EXT; 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_EXT | 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 */ // uniqueness check if(cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT) err++; if(cmd->scan_begin_src != TRIG_FOLLOW && cmd->scan_begin_src != TRIG_TIMER && cmd->scan_begin_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 && cmd->stop_src != TRIG_EXT) err++; //compatibility check if(cmd->scan_begin_src != TRIG_FOLLOW && cmd->convert_src != TRIG_TIMER) 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++; } switch(cmd->stop_src) { case TRIG_COUNT: if(!cmd->stop_arg) { cmd->stop_arg = 1; err++; } break; case TRIG_NONE: if(cmd->stop_arg != 0) { cmd->stop_arg = 0; err++; } break; default: break; } if(err) return 3; /* step 4: fix up any arguments */ if(cmd->convert_src == TRIG_TIMER) { // if we are not in burst mode if(cmd->scan_begin_src == TRIG_FOLLOW) { tmp_arg = 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_arg != cmd->convert_arg) err++; } // if we are in burst mode else { // check that convert_arg is compatible tmp_arg = cmd->convert_arg; cmd->convert_arg = burst_convert_arg(cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK); if(tmp_arg != cmd->convert_arg) err++; if(cmd->scan_begin_src == TRIG_TIMER) { // if scans are timed faster than conversion rate allows if(cmd->convert_arg * cmd->chanlist_len > cmd->scan_begin_arg) { cmd->scan_begin_arg = cmd->convert_arg * cmd->chanlist_len; err++; } tmp_arg = cmd->scan_begin_arg; /* calculate counter values that give desired timing */ i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1), &(devpriv->divisor2), &(cmd->scan_begin_arg), cmd->flags & TRIG_ROUND_MASK); if(tmp_arg != cmd->scan_begin_arg) err++; } } } if(err) return 4; // make sure user is not trying to mix unipolar and bipolar ranges if(cmd->chanlist) { unipolar = CR_RANGE(cmd->chanlist[0]) & UNIPOLAR; for(i = 1; i < cmd->chanlist_len; i++) { if(unipolar != (CR_RANGE(cmd->chanlist[i]) & UNIPOLAR)) { comedi_error(dev, "unipolar and bipolar ranges cannot be mixed in the chanlist"); err++; break; } } } if(err) return 5; return 0;}/* analog input cmd interface */// first, some utility functions used in the main ai_do_cmd()// returns appropriate bits for control register a, depending on commandstatic int control_a_bits(comedi_cmd cmd){ int control_a; control_a = FFEN; //enable fifo if(cmd.stop_src == TRIG_EXT) { control_a |= ATEN; } switch(cmd.start_src) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -