/* modules/dt282x.c hardware driver for Data Translation DT2821 series COMEDI - Linux Control and Measurement Device Interface Copyright (C) 1997-8 David A. Schleef This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Driver: dt282x.o Description: Data Translation DT2821 series (including DT-EZ) Author: ds Devices: [Data Translation] DT2821 (dt2821), DT2823 (dt2823), DT2824-PGH (dt2824-pgh), DT2824-PGL (dt2824-pgl), DT2825 (dt2825), DT2827 (dt2827), DT2828 (dt2828), DT21-EZ (dt21-ez), DT23-EZ (dt23-ez), DT24-EZ (dt24-ez), DT24-EZ-PGL (dt24-ez-pgl) Status: complete Updated: Wed, 22 Aug 2001 17:11:34 -0700 Configuration options: [0] - I/O port base address [1] - IRQ [2] - DMA 1 [3] - DMA 2 [4] - AI jumpered for 0=single ended, 1=differential [5] - AI jumpered for 0=straight binary, 1=2's complement [6] - AO 0 jumpered for 0=straight binary, 1=2's complement [7] - AO 1 jumpered for 0=straight binary, 1=2's complement [8] - AI jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5] [9] - AO 0 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5], 4=[-2.5,2.5] [10]- A0 1 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5], 4=[-2.5,2.5] Notes: - AO commands might be broken. - If you try to run a command on both the AI and AO subdevices simultaneously, bad things will happen. The driver needs to be fixed to check for this situation and return an error. */ #include #include #include #include #define DEBUG #define DT2821_TIMEOUT 100 /* 500 us */ #define DT2821_SIZE 0x10 /* * Registers in the DT282x */ #define DT2821_ADCSR 0x00 /* A/D Control/Status */ #define DT2821_CHANCSR 0x02 /* Channel Control/Status */ #define DT2821_ADDAT 0x04 /* A/D data */ #define DT2821_DACSR 0x06 /* D/A Control/Status */ #define DT2821_DADAT 0x08 /* D/A data */ #define DT2821_DIODAT 0x0a /* digital data */ #define DT2821_SUPCSR 0x0c /* Supervisor Control/Status */ #define DT2821_TMRCTR 0x0e /* Timer/Counter */ /* * At power up, some registers are in a well-known state. The * masks and values are as follows: */ #define DT2821_ADCSR_MASK 0xfff0 #define DT2821_ADCSR_VAL 0x7c00 #define DT2821_CHANCSR_MASK 0xf0f0 #define DT2821_CHANCSR_VAL 0x70f0 #define DT2821_DACSR_MASK 0x7c93 #define DT2821_DACSR_VAL 0x7c90 #define DT2821_SUPCSR_MASK 0xf8ff #define DT2821_SUPCSR_VAL 0x0000 #define DT2821_TMRCTR_MASK 0xff00 #define DT2821_TMRCTR_VAL 0xf000 /* * Bit fields of each register */ /* ADCSR */ #define DT2821_ADERR 0x8000 /* (R) 1 for A/D error */ #define DT2821_ADCLK 0x0200 /* (R/W) A/D clock enable */ /* 0x7c00 read as 1's */ #define DT2821_MUXBUSY 0x0100 /* (R) multiplexer busy */ #define DT2821_ADDONE 0x0080 /* (R) A/D done */ #define DT2821_IADDONE 0x0040 /* (R/W) interrupt on A/D done */ /* 0x0030 gain select */ /* 0x000f channel select */ /* CHANCSR */ #define DT2821_LLE 0x8000 /* (R/W) Load List Enable */ /* 0x7000 read as 1's */ /* 0x0f00 (R) present address */ /* 0x00f0 read as 1's */ /* 0x000f (R) number of entries - 1 */ /* DACSR */ #define DT2821_DAERR 0x8000 /* (R) D/A error */ #define DT2821_YSEL 0x0200 /* (R/W) DAC 1 select */ #define DT2821_SSEL 0x0100 /* (R/W) single channel select */ #define DT2821_DACRDY 0x0080 /* (R) DAC ready */ #define DT2821_IDARDY 0x0040 /* (R/W) interrupt on DAC ready */ #define DT2821_DACLK 0x0020 /* (R/W) D/A clock enable */ #define DT2821_HBOE 0x0002 /* (R/W) DIO high byte output enable */ #define DT2821_LBOE 0x0001 /* (R/W) DIO low byte output enable */ /* SUPCSR */ #define DT2821_DMAD 0x8000 /* (R) DMA done */ #define DT2821_ERRINTEN 0x4000 /* (R/W) interrupt on error */ #define DT2821_CLRDMADNE 0x2000 /* (W) clear DMA done */ #define DT2821_DDMA 0x1000 /* (R/W) dual DMA */ #define DT2821_DS1 0x0800 /* (R/W) DMA select 1 */ #define DT2821_DS0 0x0400 /* (R/W) DMA select 0 */ #define DT2821_BUFFB 0x0200 /* (R/W) buffer B selected */ #define DT2821_SCDN 0x0100 /* (R) scan done */ #define DT2821_DACON 0x0080 /* (W) DAC single conversion */ #define DT2821_ADCINIT 0x0040 /* (W) A/D initialize */ #define DT2821_DACINIT 0x0020 /* (W) D/A initialize */ #define DT2821_PRLD 0x0010 /* (W) preload multiplexer */ #define DT2821_STRIG 0x0008 /* (W) software trigger */ #define DT2821_XTRIG 0x0004 /* (R/W) external trigger enable */ #define DT2821_XCLK 0x0002 /* (R/W) external clock enable */ #define DT2821_BDINIT 0x0001 /* (W) initialize board */ static comedi_lrange range_dt282x_ai_lo_bipolar = { 4, { RANGE( -10, 10 ), RANGE( -5, 5 ), RANGE( -2.5, 2.5 ), RANGE( -1.25, 1.25 ) }}; static comedi_lrange range_dt282x_ai_lo_unipolar = { 4, { RANGE( 0, 10 ), RANGE( 0, 5 ), RANGE( 0, 2.5 ), RANGE( 0, 1.25 ) }}; static comedi_lrange range_dt282x_ai_5_bipolar = { 4, { RANGE( -5, 5 ), RANGE( -2.5, 2.5 ), RANGE( -1.25, 1.25 ), RANGE( -0.625, 0.625 ), }}; static comedi_lrange range_dt282x_ai_5_unipolar = { 4, { RANGE( 0, 5 ), RANGE( 0, 2.5 ), RANGE( 0, 1.25 ), RANGE( 0, 0.625 ), }}; static comedi_lrange range_dt282x_ai_hi_bipolar = { 4, { RANGE( -10, 10 ), RANGE( -1, 1 ), RANGE( -0.1, 0.1 ), RANGE( -0.02, 0.02 ) }}; static comedi_lrange range_dt282x_ai_hi_unipolar = { 4, { RANGE( 0, 10 ), RANGE( 0, 1 ), RANGE( 0, 0.1 ), RANGE( 0, 0.02 ) }}; typedef struct { char *name; int adbits; int adchan_se; int adchan_di; int ai_speed; int ispgl; int dachan; int dabits; } boardtype_t; static boardtype_t boardtypes[] = { { name: "dt2821", adbits: 12, adchan_se: 16, adchan_di: 8, ai_speed: 4000, ispgl: 0, dachan: 2, dabits: 12, }, { name: "dt2823", adbits: 16, adchan_se: 0, adchan_di: 4, ai_speed: 4000, ispgl: 0, dachan: 2, dabits: 16, }, { name: "dt2824-pgh", adbits: 12, adchan_se: 16, adchan_di: 8, ai_speed: 4000, ispgl: 0, dachan: 0, dabits: 0, }, { name: "dt2824-pgl", adbits: 12, adchan_se: 16, adchan_di: 8, ai_speed: 4000, ispgl: 1, dachan: 0, dabits: 0, }, { name: "dt2825", adbits: 12, adchan_se: 16, adchan_di: 8, ai_speed: 4000, ispgl: 0, dachan: 2, dabits: 12, }, { name: "dt2827", adbits: 16, adchan_se: 0, adchan_di: 4, ai_speed: 4000, ispgl: 0, dachan: 2, dabits: 12, }, { name: "dt2828", adbits: 12, adchan_se: 4, adchan_di: 0, ai_speed: 4000, ispgl: 0, dachan: 2, dabits: 12, }, { name: "dt21-ez", adbits: 12, adchan_se: 16, adchan_di: 8, ai_speed: 4000, ispgl: 0, dachan: 2, dabits: 12, }, { name: "dt23-ez", adbits: 16, adchan_se: 16, adchan_di: 8, ai_speed: 4000, ispgl: 0, dachan: 0, dabits: 0, }, { name: "dt24-ez", adbits: 12, adchan_se: 16, adchan_di: 8, ai_speed: 4000, ispgl: 0, dachan: 0, dabits: 0, }, { name: "dt24-ez-pgl", adbits: 12, adchan_se: 16, adchan_di: 8, ai_speed: 4000, ispgl: 1, dachan: 0, dabits: 0, }, }; #define n_boardtypes sizeof(boardtypes)/sizeof(boardtype_t) #define this_board ((boardtype_t *)dev->board_ptr) typedef struct { int ad_2scomp; /* we have 2's comp jumper set */ int da0_2scomp; /* same, for DAC0 */ int da1_2scomp; /* same, for DAC1 */ comedi_lrange *darangelist[2]; sampl_t ao[2]; int dacsr; /* software copies of registers */ int adcsr; int supcsr; int ntrig; int nread; struct{ int chan; short *buf; /* DMA buffer */ int size; /* size of current transfer */ }dma[2]; int dma_maxsize; /* max size of DMA transfer (in bytes) */ int usedma; /* driver uses DMA */ int current_dma_chan; int dma_dir; } dt282x_private; #define devpriv ((dt282x_private *)dev->private) #define boardtype (*(boardtype_t *)dev->board_ptr) /* * Some useless abstractions */ #define chan_to_DAC(a) ((a)&1) #define update_dacsr(a) outw(devpriv->dacsr|(a),dev->iobase+DT2821_DACSR) #define update_adcsr(a) outw(devpriv->adcsr|(a),dev->iobase+DT2821_ADCSR) #define mux_busy() (inw(dev->iobase+DT2821_ADCSR)&DT2821_MUXBUSY) #define ad_done() (inw(dev->iobase+DT2821_ADCSR)&DT2821_ADDONE) #define update_supcsr(a) outw(devpriv->supcsr|(a),dev->iobase+DT2821_SUPCSR) /* * danger! macro abuse... a is the expression to wait on, and b is * the statement(s) to execute if it doesn't happen. */ #define wait_for(a,b) \ do{ \ int _i; \ for(_i=0;_isubdevices[0].async; unsigned int i; unsigned short mask=(1<ad_2scomp){ sign = 1<<(boardtype.adbits-1); }else{ sign = 0; } abuf = async->prealloc_buf + async->buf_write_ptr; if(async->buf_write_ptr + nbytes >= async->prealloc_bufsz){ n = (async->prealloc_bufsz - async->buf_write_ptr - nbytes)/2; for(i=0;iprealloc_buf; buf = buf + i; } n = nbytes/2; for(i=0;isubdevices+1; update_supcsr(DT2821_CLRDMADNE); if(!s->async->prealloc_buf){ printk("async->data disappeared. dang!\n"); return; } i=devpriv->current_dma_chan; ptr=devpriv->dma[i].buf; disable_dma(devpriv->dma[i].chan); devpriv->current_dma_chan=1-i; size = comedi_buf_read_n_available(s->async); if(size>devpriv->dma_maxsize)size=devpriv->dma_maxsize; if( size == 0){ rt_printk("dt282x: AO underrun\n"); dt282x_ao_cancel(dev,s); s->async->events |= COMEDI_CB_OVERFLOW; comedi_event(dev,s,s->async->events); return; } comedi_buf_memcpy_from(s->async, 0, ptr, size); comedi_buf_read_free(s->async, size); prep_ao_dma(dev,i,size); enable_dma(devpriv->dma[i].chan); comedi_event(dev,s,s->async->events); return; } static void dt282x_ai_dma_interrupt(comedi_device * dev) { void *ptr; int size; int i; int ret; comedi_subdevice *s=dev->subdevices; update_supcsr(DT2821_CLRDMADNE); if(!s->async->prealloc_buf){ printk("async->data disappeared. dang!\n"); return; } i = devpriv->current_dma_chan; ptr = devpriv->dma[i].buf; size = devpriv->dma[i].size; disable_dma(devpriv->dma[i].chan); devpriv->current_dma_chan = 1-i; ret = comedi_buf_write_alloc(s->async, size); if(!ret){ rt_printk("dt282x: AI buffer overflow\n"); s->async->events |= COMEDI_CB_OVERFLOW; comedi_event(dev,s,s->async->events); return; } dt282x_copy_to_buffer(dev, ptr, size); comedi_buf_write_free(s->async, size); devpriv->nread-=size/2; if(devpriv->nread<0){ printk("dt282x: off by one\n"); devpriv->nread=0; } if (!devpriv->nread) { devpriv->adcsr=0; update_adcsr(0); /* this eliminates "extra sample" issues */ devpriv->supcsr = 0; update_supcsr(DT2821_ADCINIT); s->async->events |= COMEDI_CB_EOA; comedi_event(dev,s,s->async->events); return; } #if 1 /* clear the dual dma flag, making this the last dma segment */ /* XXX probably wrong */ if(!devpriv->ntrig){ devpriv->supcsr &= ~(DT2821_DDMA); update_supcsr(0); } #endif /* restart the channel */ prep_ai_dma(dev,i,0); enable_dma(devpriv->dma[i].chan); comedi_event(dev,s,s->async->events); } static int prep_ai_dma(comedi_device * dev,int chan,int n) { int dma_chan; unsigned long dma_ptr; unsigned long flags; if(!devpriv->ntrig) return 0; if(n==0) n = devpriv->dma_maxsize; if (n >= devpriv->ntrig*2) n = devpriv->ntrig*2; devpriv->ntrig -= n/2; devpriv->dma[chan].size = n; dma_chan = devpriv->dma[chan].chan; dma_ptr = virt_to_bus(devpriv->dma[chan].buf); set_dma_mode(dma_chan, DMA_MODE_READ); flags=claim_dma_lock(); set_dma_addr(dma_chan, dma_ptr); set_dma_count(dma_chan, n); release_dma_lock(flags); return n; } static int prep_ao_dma(comedi_device * dev,int chan,int n) { int dma_chan; unsigned long dma_ptr; unsigned long flags; devpriv->dma[chan].size = n; dma_chan = devpriv->dma[chan].chan; dma_ptr = virt_to_bus(devpriv->dma[chan].buf); set_dma_mode(dma_chan, DMA_MODE_WRITE); flags=claim_dma_lock(); set_dma_addr(dma_chan, dma_ptr); set_dma_count(dma_chan, n ); release_dma_lock(flags); return n; } static void dt282x_interrupt(int irq, void *d, struct pt_regs *regs) { comedi_device *dev = d; comedi_subdevice *s = dev->subdevices+0; comedi_subdevice *s_ao = dev->subdevices+1; unsigned int supcsr, adcsr, dacsr; sampl_t data; int ret; adcsr=inw(dev->iobase + DT2821_ADCSR); if (adcsr & DT2821_ADERR) { comedi_error(dev, "A/D error"); dt282x_ai_cancel(dev,s); s->async->events |= COMEDI_CB_ERROR; comedi_event(dev,s,s->async->events); return; } supcsr = inw(dev->iobase + DT2821_SUPCSR); /*printk("supcsr=%02x\n",supcsr);*/ if (supcsr & DT2821_DMAD) { if(devpriv->dma_dir==DMA_MODE_READ) dt282x_ai_dma_interrupt(dev); else dt282x_ao_dma_interrupt(dev); return; } if ((dacsr = inw(dev->iobase + DT2821_DACSR)) & DT2821_DAERR) { #if 0 static int warn = 5; if(--warn<=0){ disable_irq(dev->irq); printk("disabling irq\n"); } #endif comedi_error(dev, "D/A error"); dt282x_ao_cancel(dev,s_ao); s->async->events |= COMEDI_CB_ERROR; comedi_event(dev,s_ao,s->async->events); return; } if (adcsr & DT2821_ADDONE) { data = (sampl_t) inw(dev->iobase + DT2821_ADDAT); data&=(1<ad_2scomp){ data^=1<<(boardtype.adbits-1); } ret = comedi_buf_put( s->async, data ); if(ret==0){ s->async->events |= COMEDI_CB_OVERFLOW; } devpriv->nread--; if(!devpriv->nread){ s->async->events |= COMEDI_CB_EOA; }else{ if(supcsr&DT2821_SCDN) update_supcsr(DT2821_STRIG); } comedi_event(dev,s,s->async->events); return; } } static void dt282x_load_changain(comedi_device * dev, int n, unsigned int *chanlist) { unsigned int i; unsigned int chan, range; outw(DT2821_LLE | (n - 1), dev->iobase + DT2821_CHANCSR); for (i = 0; i < n; i++) { chan = CR_CHAN(chanlist[i]); range = CR_RANGE(chanlist[i]); update_adcsr((range << 4) | (chan)); } outw(n - 1, dev->iobase + DT2821_CHANCSR); } /* * Performs a single A/D conversion. * - Put channel/gain into channel-gain list * - preload multiplexer * - trigger conversion and wait for it to finish */ static int dt282x_ai_insn_read(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn, lsampl_t *data) { int i; devpriv->adcsr = DT2821_ADCLK; update_adcsr(0); dt282x_load_changain(dev, 1, &insn->chanspec); update_supcsr(DT2821_PRLD); wait_for(!mux_busy(), comedi_error(dev, "timeout\n"); return -ETIME; ); for(i=0;in;i++){ update_supcsr(DT2821_STRIG); wait_for(ad_done(), comedi_error(dev, "timeout\n"); return -ETIME; ); data[i] = inw(dev->iobase + DT2821_ADDAT) & ((1<ad_2scomp) data[i] ^= (1 << (boardtype.adbits - 1)); } return i; } static int dt282x_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_FOLLOW|TRIG_EXT; if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++; tmp=cmd->convert_src; cmd->convert_src &= TRIG_TIMER; 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_FOLLOW && cmd->scan_begin_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_FOLLOW){ /* internal trigger */ if(cmd->scan_begin_arg!=0){ cmd->scan_begin_arg=0; err++; } }else{ /* external trigger */ /* should be level/edge, hi/lo specification here */ if(cmd->scan_begin_arg!=0){ cmd->scan_begin_arg=0; err++; } } if(cmd->convert_arg<4000){ /* XXX board dependent */ cmd->convert_arg=4000; err++; } #define SLOWEST_TIMER (250*(1<<15)*255) if(cmd->convert_arg>SLOWEST_TIMER){ cmd->convert_arg=SLOWEST_TIMER; err++; } if(cmd->scan_end_arg!=cmd->chanlist_len){ cmd->scan_end_arg=cmd->chanlist_len; err++; } if(cmd->stop_src==TRIG_COUNT){ /* any count is allowed */ }else{ /* TRIG_NONE */ if(cmd->stop_arg!=0){ cmd->stop_arg=0; err++; } } if(err)return 3; /* step 4: fix up any arguments */ tmp=cmd->convert_arg; dt282x_ns_to_timer(&cmd->convert_arg,cmd->flags&TRIG_ROUND_MASK); if(tmp!=cmd->convert_arg)err++; if(err)return 4; return 0; } static int dt282x_ai_cmd(comedi_device * dev, comedi_subdevice * s) { comedi_cmd *cmd=&s->async->cmd; int timer; timer=dt282x_ns_to_timer(&cmd->convert_arg,TRIG_ROUND_NEAREST); outw(timer, dev->iobase + DT2821_TMRCTR); if(cmd->scan_begin_src==TRIG_FOLLOW){ /* internal trigger */ devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS0; }else{ /* external trigger */ devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS0 | DT2821_DS1; } update_supcsr(DT2821_CLRDMADNE | DT2821_BUFFB | DT2821_ADCINIT); devpriv->adcsr = 0; devpriv->ntrig=cmd->stop_arg*cmd->scan_end_arg; devpriv->nread=devpriv->ntrig; devpriv->dma_dir=DMA_MODE_READ; devpriv->current_dma_chan=0; prep_ai_dma(dev,0,0); enable_dma(devpriv->dma[0].chan); if(devpriv->ntrig){ prep_ai_dma(dev,1,0); enable_dma(devpriv->dma[1].chan); devpriv->supcsr |= DT2821_DDMA; update_supcsr(0); } devpriv->adcsr = DT2821_ADCLK | DT2821_IADDONE; update_adcsr(0); dt282x_load_changain(dev,cmd->chanlist_len,cmd->chanlist); devpriv->adcsr = DT2821_ADCLK | DT2821_IADDONE; update_adcsr(0); update_supcsr(DT2821_PRLD); wait_for(!mux_busy(), comedi_error(dev, "timeout\n"); return -ETIME; ); if(cmd->scan_begin_src==TRIG_FOLLOW){ update_supcsr(DT2821_STRIG); }else{ devpriv->supcsr |= DT2821_XTRIG; update_supcsr(0); } return 0; } static int dt282x_ai_cancel(comedi_device * dev, comedi_subdevice * s) { devpriv->adcsr=0; update_adcsr(0); devpriv->supcsr = 0; update_supcsr(DT2821_ADCINIT); return 0; } static int dt282x_ns_to_timer(int *nanosec,int round_mode) { int prescale,base,divider; for(prescale=0;prescale<16;prescale++){ if(prescale==1)continue; base=250*(1<ao[CR_CHAN(insn->chanspec)]; return 1; } static int dt282x_ao_insn_write(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data) { sampl_t d; unsigned int chan; chan = CR_CHAN(insn->chanspec); d = data[0]; d &= (1<ao[chan] = d; devpriv->dacsr |= DT2821_SSEL; if (chan) { /* select channel */ devpriv->dacsr |= DT2821_YSEL; if (devpriv->da0_2scomp) d ^= (1<<(boardtype.dabits-1)); } else { devpriv->dacsr &= ~DT2821_YSEL; if (devpriv->da1_2scomp) d ^= (1<<(boardtype.dabits-1)); } update_dacsr(0); outw(d, dev->iobase + DT2821_DADAT); update_supcsr(DT2821_DACON); return 1; } static int dt282x_ao_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_INT; if(!cmd->start_src || tmp!=cmd->start_src)err++; tmp=cmd->scan_begin_src; cmd->scan_begin_src &= TRIG_TIMER; if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++; tmp=cmd->convert_src; cmd->convert_src &= TRIG_NOW; 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_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->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_arg < 5000 /* XXX unknown */){ cmd->scan_begin_arg = 5000; err++; } if(cmd->convert_arg != 0){ cmd->convert_arg = 0; err++; } if(cmd->scan_end_arg > 2){ /* XXX chanlist stuff? */ cmd->scan_end_arg = 2; err++; } if(cmd->stop_src==TRIG_COUNT){ /* any count is allowed */ }else{ /* TRIG_NONE */ if(cmd->stop_arg!=0){ cmd->stop_arg=0; err++; } } if(err)return 3; /* step 4: fix up any arguments */ tmp=cmd->scan_begin_arg; dt282x_ns_to_timer(&cmd->scan_begin_arg,cmd->flags&TRIG_ROUND_MASK); if(tmp!=cmd->scan_begin_arg)err++; if(err)return 4; return 0; } static int dt282x_ao_inttrig(comedi_device *dev,comedi_subdevice *s, unsigned int x) { int size; if(x!=0)return -EINVAL; size = comedi_buf_read_n_available(s->async); if(size>devpriv->dma_maxsize)size=devpriv->dma_maxsize; if( size == 0){ rt_printk("dt282x: AO underrun\n"); return -EPIPE; } comedi_buf_memcpy_from(s->async, 0, devpriv->dma[0].buf, size); comedi_buf_read_free(s->async, size); prep_ao_dma(dev,0,size); enable_dma(devpriv->dma[0].chan); size = comedi_buf_read_n_available(s->async); if(size>devpriv->dma_maxsize)size=devpriv->dma_maxsize; if( size == 0){ rt_printk("dt282x: AO underrun\n"); return -EPIPE; } comedi_buf_memcpy_from(s->async, 0, devpriv->dma[1].buf, size); comedi_buf_read_free(s->async, size); prep_ao_dma(dev,1,size); enable_dma(devpriv->dma[1].chan); update_supcsr(DT2821_STRIG); s->async->inttrig=NULL; return 1; } static int dt282x_ao_cmd(comedi_device *dev,comedi_subdevice *s) { int timer; comedi_cmd *cmd=&s->async->cmd; devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS1 | DT2821_DDMA; update_supcsr(DT2821_CLRDMADNE | DT2821_BUFFB | DT2821_DACINIT); devpriv->ntrig=cmd->stop_arg*cmd->chanlist_len; devpriv->nread=devpriv->ntrig; devpriv->dma_dir=DMA_MODE_WRITE; devpriv->current_dma_chan=0; timer=dt282x_ns_to_timer(&cmd->scan_begin_arg,TRIG_ROUND_NEAREST); outw(timer, dev->iobase + DT2821_TMRCTR); devpriv->dacsr = DT2821_SSEL| DT2821_DACLK | DT2821_IDARDY; update_dacsr(0); s->async->inttrig=dt282x_ao_inttrig; return 0; } static int dt282x_ao_cancel(comedi_device * dev, comedi_subdevice * s) { devpriv->dacsr=0; update_dacsr(0); devpriv->supcsr = 0; update_supcsr(DT2821_DACINIT); return 0; } static int dt282x_dio_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) { if(data[0]){ s->state &= ~data[0]; s->state |= (data[0]&data[1]); outw(s->state, dev->iobase + DT2821_DIODAT); } data[1] = inw(dev->iobase + DT2821_DIODAT); return 2; } static int dt282x_dio_insn_config(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) { int mask; mask=(CR_CHAN(insn->chanspec)<8)?0x00ff:0xff00; if(data[0])s->io_bits|=mask; else s->io_bits&=~mask; if(s->io_bits&0x00ff)devpriv->dacsr|=DT2821_LBOE; else devpriv->dacsr&=~DT2821_LBOE; if(s->io_bits&0xff00)devpriv->dacsr|=DT2821_HBOE; else devpriv->dacsr&=~DT2821_HBOE; outw(devpriv->dacsr, dev->iobase + DT2821_DACSR); return 1; } static comedi_lrange *ai_range_table[]={ &range_dt282x_ai_lo_bipolar, &range_dt282x_ai_lo_unipolar, &range_dt282x_ai_5_bipolar, &range_dt282x_ai_5_unipolar }; static comedi_lrange *ai_range_pgl_table[]={ &range_dt282x_ai_hi_bipolar, &range_dt282x_ai_hi_unipolar }; static comedi_lrange *opt_ai_range_lkup(int ispgl,int x) { if(ispgl){ if(x<0 || x>=2)x=0; return ai_range_pgl_table[x]; }else{ if(x<0 || x>=4)x=0; return ai_range_table[x]; } } static comedi_lrange *ao_range_table[]={ &range_bipolar10, &range_unipolar10, &range_bipolar5, &range_unipolar5, &range_bipolar2_5 }; static comedi_lrange *opt_ao_range_lkup(int x) { if(x<0 || x>=5)x=0; return ao_range_table[x]; } enum{ opt_iobase=0, opt_irq, opt_dma1, opt_dma2, /* i/o base, irq, dma channels */ opt_diff, /* differential */ opt_ai_twos, opt_ao0_twos, opt_ao1_twos, /* twos comp */ opt_ai_range, opt_ao0_range, opt_ao1_range, /* range */ }; /* options: 0 i/o base 1 irq 2 dma1 3 dma2 4 0=single ended, 1=differential 5 ai 0=straight binary, 1=2's comp 6 ao0 0=straight binary, 1=2's comp 7 ao1 0=straight binary, 1=2's comp 8 ai 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V 9 ao0 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V, 4=±2.5 V 10 ao1 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V, 4=±2.5 V */ static int dt282x_attach(comedi_device * dev, comedi_devconfig * it) { int i, irqs, irq; long flags; int ret; comedi_subdevice *s; int iobase; dev->board_name = this_board->name; iobase = it->options[opt_iobase]; if(!iobase)iobase = 0x240; printk("comedi%d: dt282x: 0x%04x", dev->minor, iobase); if (check_region(iobase, DT2821_SIZE) < 0) { printk(" I/O port conflict\n"); return -EBUSY; } request_region(iobase, DT2821_SIZE, "dt282x"); dev->iobase = iobase; outw(DT2821_BDINIT, dev->iobase + DT2821_SUPCSR); i = inw(dev->iobase + DT2821_ADCSR); #ifdef DEBUG printk(" fingerprint=%x,%x,%x,%x,%x", inw(dev->iobase + DT2821_ADCSR), inw(dev->iobase + DT2821_CHANCSR), inw(dev->iobase + DT2821_DACSR), inw(dev->iobase + DT2821_SUPCSR), inw(dev->iobase + DT2821_TMRCTR)); #endif if ( ((inw(dev->iobase + DT2821_ADCSR) & DT2821_ADCSR_MASK) != DT2821_ADCSR_VAL) || ((inw(dev->iobase + DT2821_CHANCSR) & DT2821_CHANCSR_MASK) != DT2821_CHANCSR_VAL) || ((inw(dev->iobase + DT2821_DACSR) & DT2821_DACSR_MASK) != DT2821_DACSR_VAL) || ((inw(dev->iobase + DT2821_SUPCSR) & DT2821_SUPCSR_MASK) != DT2821_SUPCSR_VAL) || ((inw(dev->iobase + DT2821_TMRCTR) & DT2821_TMRCTR_MASK) != DT2821_TMRCTR_VAL)) { printk(" board not found"); return -EIO; } /* should do board test */ irq = it->options[opt_irq]; if (irq < 0) { save_flags(flags); sti(); irqs = probe_irq_on(); /* trigger interrupt */ udelay(100); irq = probe_irq_off(irqs); restore_flags(flags); if (0 /* error */ ) { printk(" error probing irq (bad)"); } } dev->irq = 0; if (irq > 0) { printk(" ( irq = %d )", irq); ret = comedi_request_irq(irq, dt282x_interrupt, 0, "dt282x", dev); if(ret<0) { printk(" failed to get irq\n"); return -EIO; } dev->irq = irq; } else if (irq == 0) { printk(" (no irq)"); } else { printk(" (probe returned multiple irqs--bad)"); } if((ret=alloc_private(dev,sizeof(dt282x_private)))<0) return ret; ret=dt282x_grab_dma(dev,it->options[opt_dma1],it->options[opt_dma2]); if(ret<0) return ret; dev->n_subdevices = 3; if((ret=alloc_subdevices(dev))<0) return ret; s=dev->subdevices+0; dev->read_subdev=s; /* ai subdevice */ s->type=COMEDI_SUBD_AI; s->subdev_flags=SDF_READABLE|((it->options[opt_diff])?SDF_DIFF:SDF_COMMON); s->n_chan=(it->options[opt_diff])?boardtype.adchan_di:boardtype.adchan_se; s->insn_read=dt282x_ai_insn_read; s->do_cmdtest=dt282x_ai_cmdtest; s->do_cmd=dt282x_ai_cmd; s->cancel=dt282x_ai_cancel; s->maxdata=(1<len_chanlist=16; s->range_table = opt_ai_range_lkup(boardtype.ispgl,it->options[opt_ai_range]); devpriv->ad_2scomp=it->options[opt_ai_twos]; s++; if((s->n_chan=boardtype.dachan)){ /* ao subsystem */ s->type=COMEDI_SUBD_AO; dev->write_subdev=s; s->subdev_flags=SDF_WRITABLE; s->insn_read=dt282x_ao_insn_read; s->insn_write=dt282x_ao_insn_write; s->do_cmdtest=dt282x_ao_cmdtest; s->do_cmd=dt282x_ao_cmd; s->cancel=dt282x_ao_cancel; s->maxdata=(1<len_chanlist=2; s->range_table_list=devpriv->darangelist; devpriv->darangelist[0]= opt_ao_range_lkup(it->options[opt_ao0_range]); devpriv->darangelist[1]= opt_ao_range_lkup(it->options[opt_ao1_range]); devpriv->da0_2scomp=it->options[opt_ao0_twos]; devpriv->da1_2scomp=it->options[opt_ao1_twos]; }else{ s->type=COMEDI_SUBD_UNUSED; } s++; /* dio subsystem */ s->type=COMEDI_SUBD_DIO; s->subdev_flags=SDF_READABLE|SDF_WRITABLE; s->n_chan=16; s->insn_bits = dt282x_dio_insn_bits; s->insn_config = dt282x_dio_insn_config; s->maxdata=1; s->range_table = &range_digital; printk("\n"); return 0; } static void free_resources(comedi_device *dev) { if (dev->irq) { comedi_free_irq(dev->irq, dev); } if(dev->iobase) release_region(dev->iobase, DT2821_SIZE); if(dev->private){ if (devpriv->dma[0].chan) free_dma(devpriv->dma[0].chan); if (devpriv->dma[1].chan) free_dma(devpriv->dma[1].chan); if (devpriv->dma[0].buf) free_page((unsigned long) devpriv->dma[0].buf); if (devpriv->dma[1].buf) free_page((unsigned long) devpriv->dma[1].buf); } } static int dt282x_detach(comedi_device * dev) { printk("comedi%d: dt282x: remove\n", dev->minor); free_resources(dev); return 0; } static int dt282x_grab_dma(comedi_device *dev,int dma1,int dma2) { int ret; devpriv->usedma=0; if(!dma1 && !dma2){ printk(" (no dma)"); return 0; } if(dma1==dma2 || dma1<5 || dma2<5 || dma1>7 || dma2>7) return -EINVAL; if(dma2dma[0].chan=dma1; ret = request_dma(dma2, "dt282x B"); if (ret) return -EBUSY; devpriv->dma[1].chan=dma2; devpriv->dma_maxsize = PAGE_SIZE; devpriv->dma[0].buf = (void *) get_free_page(GFP_KERNEL | GFP_DMA); devpriv->dma[1].buf = (void *) get_free_page(GFP_KERNEL | GFP_DMA); if (!devpriv->dma[0].buf || !devpriv->dma[1].buf) { printk(" can't get DMA memory"); return -ENOMEM; } printk(" (dma=%d,%d)",dma1,dma2); devpriv->usedma=1; return 0; }