📄 dt282x.c
字号:
/* modules/dt282x.c hardware driver for Data Translation DT2821 series COMEDI - Linux Control and Measurement Device Interface Copyright (C) 1997-8 David A. Schleef <ds@schleef.org> 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.oDescription: Data Translation DT2821 series (including DT-EZ)Author: dsDevices: [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: completeUpdated: Wed, 22 Aug 2001 17:11:34 -0700Configuration 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 <linux/comedidev.h>#include <linux/ioport.h>#include <linux/interrupt.h>#include <asm/dma.h>#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;_i<DT2821_TIMEOUT;_i++){ \ if(a){_i=0;break;} \ comedi_udelay(5); \ } \ if(_i){b} \ }while(0)static int dt282x_attach(comedi_device * dev, comedi_devconfig * it);static int dt282x_detach(comedi_device * dev);static comedi_driver driver_dt282x={ driver_name: "dt282x", module: THIS_MODULE, attach: dt282x_attach, detach: dt282x_detach, board_name: boardtypes, num_names: n_boardtypes, offset: sizeof(boardtype_t),};COMEDI_INITCLEANUP(driver_dt282x);static void free_resources(comedi_device *dev);static int prep_ai_dma(comedi_device * dev,int chan,int size);static int prep_ao_dma(comedi_device * dev,int chan,int size);static int dt282x_ai_cancel(comedi_device * dev, comedi_subdevice * s);static int dt282x_ao_cancel(comedi_device * dev, comedi_subdevice * s);static int dt282x_ns_to_timer(int *nanosec,int round_mode);static int dt282x_grab_dma(comedi_device *dev,int dma1,int dma2);static void dt282x_copy_to_buffer(comedi_device *dev,sampl_t *buf, unsigned int nbytes){ comedi_async *async = dev->subdevices[0].async; unsigned int i; unsigned short mask=(1<<boardtype.adbits)-1; unsigned short sign=1<<(boardtype.adbits-1); unsigned short *abuf; int n; if(devpriv->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;i<n;i++){ abuf[i] = (buf[i]&mask)^sign; } nbytes -= n; abuf = async->prealloc_buf; buf = buf + i; } n = nbytes/2; for(i=0;i<n;i++){ abuf[i] = (buf[i]&mask)^sign; }}static void dt282x_ao_dma_interrupt(comedi_device * dev){ void *ptr; int size; int i; comedi_subdevice *s=dev->subdevices+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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -