📄 rtd520.c
字号:
}#if 0 if (0 == (RtdFifoStatus (dev) & FS_ADC_EMPTY)) { /* DEBUG */ DPRINTK("comedi: READ OOPS on %d of %d\n", ii+1, count); break; }#endif d = RtdAdcFifoGet (dev); /* get 2s comp value */ d = d >> 3; /* low 3 bits are marker lines */ if (CHAN_ARRAY_TEST (devpriv->chanBipolar, s->async->cur_chan)) { sample = d + 2048; /* convert to comedi unsigned data */ } else { sample = d; } if (!comedi_buf_put (s->async, sample)) return -1; if (devpriv->aiCount > 0) /* < 0, means read forever */ devpriv->aiCount--; } return 0;}/* unknown amout of data is waiting in fifo.*/static int ai_read_dregs ( comedi_device *dev, comedi_subdevice *s){ while (RtdFifoStatus (dev) & FS_ADC_EMPTY) { /* 1 -> not empty */ sampl_t sample; s16 d = RtdAdcFifoGet (dev); /* get 2s comp value */ if (0 == devpriv->aiCount) { /* done */ continue; /* read rest */ } d = d >> 3; /* low 3 bits are marker lines */ if (CHAN_ARRAY_TEST (devpriv->chanBipolar, s->async->cur_chan)) { sample = d + 2048; /* convert to comedi unsigned data */ } else { sample = d; } if (!comedi_buf_put (s->async, sample)) return -1; if (devpriv->aiCount > 0) /* < 0, means read forever */ devpriv->aiCount--; } return 0;}#ifdef USE_DMA/* Terminate a DMA transfer and wait for everything to quiet down*/void abort_dma ( comedi_device *dev, unsigned int channel) /* DMA channel 0, 1 */{ unsigned long dma_cs_addr; /* the control/status register */ uint8_t status; unsigned int ii; //unsigned long flags; dma_cs_addr = (unsigned long)devpriv->lcfg + ((channel == 0) ? LCFG_DMACSR0 : LCFG_DMACSR1); // spinlock for plx dma control/status reg //comedi_spin_lock_irqsave( &dev->spinlock, flags ); // abort dma transfer if necessary status = readb(dma_cs_addr); if((status & PLX_DMA_EN_BIT) == 0) { /* not enabled (Error?) */ DPRINTK ("rtd520: AbortDma on non-active channel %d (0x%x)\n", channel, status); goto abortDmaExit; } /* wait to make sure done bit is zero (needed?) */ for (ii = 0; (status & PLX_DMA_DONE_BIT) && ii < RTD_DMA_TIMEOUT; ii++) { WAIT_QUIETLY; status = readb(dma_cs_addr); } if (status & PLX_DMA_DONE_BIT) { printk("rtd520: Timeout waiting for dma %i done clear\n", channel); goto abortDmaExit; } /* disable channel (required) */ writeb(0, dma_cs_addr); comedi_udelay(1); /* needed?? */ /* set abort bit for channel */ writeb(PLX_DMA_ABORT_BIT, dma_cs_addr); // wait for dma done bit to be set status = readb(dma_cs_addr); for(ii = 0; (status & PLX_DMA_DONE_BIT) == 0 && ii < RTD_DMA_TIMEOUT; ii++) { status = readb(dma_cs_addr); WAIT_QUIETLY; } if ((status & PLX_DMA_DONE_BIT) == 0) { printk("rtd520: Timeout waiting for dma %i done set\n", channel); }abortDmaExit: //comedi_spin_unlock_irqrestore( &dev->spinlock, flags );}/* Process what is in the DMA transfer buffer and pass to comedi Note: this is not re-entrant*/static int ai_process_dma ( comedi_device *dev, comedi_subdevice *s){ int ii, n; s16 *dp; if (devpriv->aiCount == 0) /* transfer already complete */ return 0; dp = devpriv->dma0Buff[devpriv->dma0Offset]; for (ii = 0; ii < thisboard->fifoLen/2;) { /* convert samples */ sampl_t sample; if (CHAN_ARRAY_TEST (devpriv->chanBipolar, s->async->cur_chan)) { sample = (*dp >> 3) + 2048; /* convert to comedi unsigned data */ } else { sample = *dp >> 3; /* low 3 bits are marker lines */ } *dp++ = sample; /* put processed value back */ if (++s->async->cur_chan >= s->async->cmd.chanlist_len) s->async->cur_chan = 0; ++ii; /* number ready to transfer */ if (devpriv->aiCount > 0) { /* < 0, means read forever */ if (--devpriv->aiCount == 0) { /* done */ /*DPRINTK ("rtd520: Final %d samples\n", ii);*/ break; } } } /* now pass the whole array to the comedi buffer */ dp = devpriv->dma0Buff[devpriv->dma0Offset]; n = comedi_buf_write_alloc (s->async, ii * sizeof (s16)); if (n < (ii * sizeof (s16))) { /* any residual is an error */ DPRINTK ("rtd520:ai_process_dma buffer overflow %d samples!\n", ii - (n / sizeof (s16))); s->async->events |= COMEDI_CB_ERROR; return -1; } comedi_buf_memcpy_to (s->async, 0, dp, n); comedi_buf_write_free (s->async, n); /* always at least 1 scan -- 1/2 FIFO is larger than our max scan list */ s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS; if (++devpriv->dma0Offset >= DMA_CHAIN_COUNT) { /* next buffer */ devpriv->dma0Offset = 0; } return 0;}#endif /* USE_DMA *//* Handle all rtd520 interrupts. Runs atomically and is never re-entered. This is a "slow handler"; other interrupts may be active. The data conversion may someday happen in a "bottom half".*/static void rtd_interrupt ( int irq, /* interrupt number (ignored) */ void *d, /* our data */ struct pt_regs *regs) /* cpu context (ignored) */{ comedi_device *dev = d; /* must be called "dev" for devpriv */ u16 status; u16 fifoStatus; comedi_subdevice *s = dev->subdevices + 0; /* analog in subdevice */ devpriv->intCount++; /* DEBUG statistics */ fifoStatus = RtdFifoStatus (dev); /* check for FIFO full, this automatically halts the ADC! */ if (!(fifoStatus & FS_ADC_FULL)) { /* 0 -> full */ DPRINTK("rtd520: FIFO full! fifo_status=0x%x\n", (fifoStatus ^ 0x6666) & 0x7777); /* should be all 0s */ goto abortTransfer; } #ifdef USE_DMA if (devpriv->flags & DMA0_ACTIVE) { /* Check DMA */ u32 istatus = RtdPlxInterruptRead (dev); if (istatus & ICS_DMA0_A) { if (ai_process_dma (dev, s) < 0) { DPRINTK("rtd520: comedi read buffer overflow (DMA) with %ld to go!\n", devpriv->aiCount); RtdDma0Control (dev, (devpriv->dma0Control & ~PLX_DMA_START_BIT) | PLX_CLEAR_DMA_INTR_BIT); goto abortTransfer; } /*DPRINTK ("rtd520: DMA transfer: %ld to go, istatus %x\n", devpriv->aiCount, istatus);*/ RtdDma0Control (dev, (devpriv->dma0Control & ~PLX_DMA_START_BIT) | PLX_CLEAR_DMA_INTR_BIT); if (0 == devpriv->aiCount) { /* counted down */ DPRINTK("rtd520: Samples Done (DMA).\n"); goto transferDone; } comedi_event (dev, s, s->async->events); } else { /*DPRINTK ("rtd520: No DMA ready: istatus %x\n", istatus);*/ } } /* Fall through and check for other interrupt sources */#endif /* USE_DMA */ status = RtdInterruptStatus (dev); /* if interrupt was not caused by our board, or handled above */ if (0 == status) { return; } if (status & IRQM_ADC_ABOUT_CNT) { /* sample count -> read FIFO */ /* since the priority interrupt controller may have queued a sample counter interrupt, even though we have already finished, we must handle the possibility that there is no data here */ if (!(fifoStatus & FS_ADC_HEMPTY)) { /* 0 -> 1/2 full */ /*DPRINTK("rtd520: Sample int, reading 1/2FIFO. fifo_status 0x%x\n", (fifoStatus ^ 0x6666) & 0x7777);*/ if (ai_read_n (dev, s, thisboard->fifoLen / 2) < 0) { DPRINTK("rtd520: comedi read buffer overflow (1/2FIFO) with %ld to go!\n", devpriv->aiCount); goto abortTransfer; } if (0 == devpriv->aiCount) { /* counted down */ DPRINTK("rtd520: Samples Done (1/2). fifo_status was 0x%x\n", (fifoStatus ^ 0x6666) & 0x7777); /* should be all 0s */ goto transferDone; } comedi_event (dev, s, s->async->events); } else if (devpriv->transCount > 0) { /* read often */ /*DPRINTK("rtd520: Sample int, reading %d fifo_status 0x%x\n", devpriv->transCount, (fifoStatus ^ 0x6666) & 0x7777);*/ if (fifoStatus & FS_ADC_EMPTY) { /* 1 -> not empty */ if (ai_read_n (dev, s, devpriv->transCount) < 0) { DPRINTK("rtd520: comedi read buffer overflow (N) with %ld to go!\n", devpriv->aiCount); goto abortTransfer; } if (0 == devpriv->aiCount) { /* counted down */ DPRINTK("rtd520: Samples Done (N). fifo_status was 0x%x\n", (fifoStatus ^ 0x6666) & 0x7777); goto transferDone; } comedi_event (dev, s, s->async->events); } } else { /* wait for 1/2 FIFO (old)*/ DPRINTK("rtd520: Sample int. Wait for 1/2. fifo_status 0x%x\n", (fifoStatus ^ 0x6666) & 0x7777); } } else { DPRINTK ("rtd520: unknown interrupt source!\n"); } if (0xffff & RtdInterruptOverrunStatus (dev)) { /* interrupt overrun */ DPRINTK("rtd520: Interrupt overrun with %ld to go! over_status=0x%x\n", devpriv->aiCount, 0xffff & RtdInterruptOverrunStatus (dev)); goto abortTransfer; } /* clear the interrupt */ RtdInterruptClearMask (dev, status); RtdInterruptClear (dev); return;abortTransfer: RtdAdcClearFifo (dev); /* clears full flag */ s->async->events |= COMEDI_CB_ERROR; devpriv->aiCount = 0; /* stop and don't transfer any more */ /* fall into transferDone */transferDone: RtdPacerStopSource (dev, 0); /* stop on SOFTWARE stop */ RtdPacerStop (dev); /* Stop PACER */ RtdAdcConversionSource (dev, 0); /* software trigger only */ RtdInterruptMask (dev, 0); /* mask out SAMPLE */#ifdef USE_DMA if (devpriv->flags & DMA0_ACTIVE) { RtdPlxInterruptWrite (dev, /* disable any more interrupts */ RtdPlxInterruptRead (dev) & ~ICS_DMA0_E); abort_dma (dev, 0); devpriv->flags &= ~DMA0_ACTIVE; /* if Using DMA, then we should have read everything by now */ if (devpriv->aiCount > 0) { DPRINTK ("rtd520: Lost DMA data! %ld remain\n", devpriv->aiCount); } }#endif /* USE_DMA */ if (devpriv->aiCount > 0) { /* there shouldn't be anything left */ fifoStatus = RtdFifoStatus (dev); DPRINTK("rtd520: Finishing up. %ld remain, fifoStat=%x\n", devpriv->aiCount, (fifoStatus ^ 0x6666) & 0x7777); /* should read all 0s */ ai_read_dregs (dev, s); /* read anything left in FIFO */ } s->async->events |= COMEDI_CB_EOA;/* signal end to comedi */ comedi_event (dev, s, s->async->events); /* clear the interrupt */ status = RtdInterruptStatus (dev); RtdInterruptClearMask (dev, status); RtdInterruptClear (dev); fifoStatus = RtdFifoStatus (dev); /* DEBUG */ DPRINTK("rtd520: Acquisition complete. %ld ints, intStat=%x, overStat=%x\n", devpriv->intCount, status, 0xffff & RtdInterruptOverrunStatus (dev));}#if 0/* return the number of samples available*/static int rtd_ai_poll (comedi_device *dev,comedi_subdevice *s){ /* TODO: This needs to mask interrupts, read_dregs, and then re-enable */ /* Not sure what to do if DMA is active */ return s->async->buf_write_count - s->async->buf_read_count;}#endif/* cmdtest tests a particular command to see if it is valid. Using the cmdtest ioctl, a user can create a valid cmd and then have it executed by the cmd ioctl (asyncronously). cmdtest returns 1,2,3,4 or 0, depending on which tests the command passes.*/static int rtd_ai_cmdtest ( comedi_device *dev, comedi_subdevice *s, comedi_cmd *cmd){ int err=0; int tmp; /* 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_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_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 */ /* note that mutual compatiblity is not an issue here */ if (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) { 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_TIMER){ /* Note: these are time periods, not actual rates */ if (1 == cmd->chanlist_len) { /* no scanning */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -