From f214f88a1a04bfe75fb622e0491f99cc5b45971a Mon Sep 17 00:00:00 2001 From: Frank Mori Hess Date: Fri, 5 Jul 2002 23:01:50 +0000 Subject: [PATCH] First pass at revised buffer system, hopefully didn't create too many new bugs. Tested analog input command with comedi_test module, haven't tried analog output. --- comedi/comedi_fops.c | 105 ++++++------- comedi/drivers/adl_pci9118.c | 132 +++++++---------- comedi/drivers/comedi_parport.c | 9 +- comedi/drivers/das16m1.c | 2 +- comedi/drivers/das1800.c | 2 +- comedi/drivers/das6402.c | 2 +- comedi/drivers/dt282x.c | 76 ++-------- comedi/drivers/ni_mio_common.c | 56 +++---- comedi/drivers/ni_pcidio.c | 22 +-- comedi/drivers/pcl812.c | 2 +- comedi/drivers/pcl816.c | 6 +- comedi/drivers/rtd520.c | 2 +- comedi/kcomedilib/get.c | 4 +- comedi/kcomedilib/kcomedilib_main.c | 16 +- include/linux/comedidev.h | 222 ++++++++++++++++++++-------- 15 files changed, 340 insertions(+), 318 deletions(-) diff --git a/comedi/comedi_fops.c b/comedi/comedi_fops.c index f9fa648a..d53a7ff8 100644 --- a/comedi/comedi_fops.c +++ b/comedi/comedi_fops.c @@ -75,6 +75,7 @@ void do_become_nonbusy(comedi_device *dev,comedi_subdevice *s); static int do_cancel(comedi_device *dev,comedi_subdevice *s); static int comedi_fasync (int fd, struct file *file, int on); +static void init_async_buf( comedi_async *async ); static int comedi_ioctl(struct inode * inode,struct file * file, unsigned int cmd,unsigned long arg) @@ -457,32 +458,34 @@ static int do_bufinfo_ioctl(comedi_device *dev,void *arg) if(bi.bytes_read){ // check for buffer underflow - m = async->buf_int_count - async->buf_user_count; + m = async->buf_write_count - async->buf_read_count; if(bi.bytes_read > m) { DPRINTK("buffer underflow\n"); return -EIO; } - async->buf_user_ptr += bi.bytes_read; - async->buf_user_count += bi.bytes_read; + async->buf_read_ptr += bi.bytes_read; + if( async->buf_read_ptr >= async->data_len ) + async->buf_read_ptr %= async->data_len; + async->buf_read_count += bi.bytes_read; // check for buffer overflow - if(m > async->data_len){ - async->buf_user_count = async->buf_int_count; - async->buf_user_ptr = async->buf_int_ptr; + if( m > async->data_len ) + { do_cancel(dev, dev->read_subdev); DPRINTK("buffer overflow\n"); return -EIO; } - if(!(s->subdev_flags&SDF_RUNNING) && async->buf_int_count==async->buf_user_count){ + if(!(s->subdev_flags&SDF_RUNNING) && async->buf_write_count==async->buf_read_count){ do_become_nonbusy(dev,s); } } - bi.buf_int_count = async->buf_int_count; - bi.buf_int_ptr = async->buf_int_ptr; - bi.buf_user_count = async->buf_user_count; - bi.buf_user_ptr = async->buf_user_ptr; + // XXX fix bufinfo struct + bi.buf_int_count = async->buf_write_count; + bi.buf_int_ptr = async->buf_write_ptr; + bi.buf_user_count = async->buf_read_count; + bi.buf_user_ptr = async->buf_read_ptr; copyback: if(copy_to_user(arg, &bi, sizeof(comedi_bufinfo))) @@ -877,20 +880,18 @@ static int do_cmd_ioctl(comedi_device *dev,void *arg,void *file) async->cmd.data_len=async->prealloc_bufsz; #endif - async->data = async->prealloc_buf; - async->data_len=async->prealloc_bufsz; - - async->buf_int_ptr=0; - async->buf_int_count=0; - async->buf_user_ptr=0; - async->buf_user_count=0; + init_async_buf( async ); async->cur_chan = 0; + async->data = async->prealloc_buf; + async->data_len=async->prealloc_bufsz; + async->cb_mask = COMEDI_CB_EOA|COMEDI_CB_BLOCK|COMEDI_CB_ERROR; if(async->cmd.flags & TRIG_WAKE_EOS){ async->cb_mask |= COMEDI_CB_EOS; } + async->events = 0; s->runflags=SRF_USER; @@ -1144,7 +1145,7 @@ static int do_cancel_ioctl(comedi_device *dev,unsigned int arg,void *file) reads: nothing - + writes: nothing @@ -1284,7 +1285,7 @@ static unsigned int comedi_poll_v22(struct file *file, poll_table * wait) s = dev->read_subdev; async = s->async; if(!s->busy || - (async->buf_user_count < async->buf_int_count) || + (async->buf_read_count < async->buf_write_count) || !(s->subdev_flags&SDF_RUNNING)){ mask |= POLLIN | POLLRDNORM; } @@ -1294,7 +1295,7 @@ static unsigned int comedi_poll_v22(struct file *file, poll_table * wait) async = s->async; if(!s->busy || !(s->subdev_flags&SDF_RUNNING) || - (async->buf_user_count < async->buf_int_count + + (async->buf_write_count < async->buf_read_count + async->prealloc_bufsz)){ mask |= POLLOUT | POLLWRNORM; } @@ -1347,9 +1348,9 @@ static ssize_t comedi_write_v22(struct file *file,const char *buf,size_t nbytes, n=nbytes; - m = async->data_len + async->buf_int_count - async->buf_user_count; - if( async->buf_user_ptr + m > async->data_len ) - m = async->data_len - async->buf_user_ptr; + m = async->data_len + async->buf_read_count - async->buf_write_count; + if( async->buf_write_ptr + m > async->data_len ) + m = async->data_len - async->buf_write_ptr; if(m < n) n = m; @@ -1374,18 +1375,16 @@ static ssize_t comedi_write_v22(struct file *file,const char *buf,size_t nbytes, schedule(); continue; } - m=copy_from_user(async->data+async->buf_user_ptr,buf,n); - if(m) retval=-EFAULT; + m = 0; + if( write_to_async_buffer( async, buf, n, 1 ) ) + { + m = n; + retval = -EFAULT; + } n-=m; count+=n; nbytes-=n; - async->buf_user_ptr+=n; - async->buf_user_count+=n; - - if(async->buf_user_ptr>=async->data_len ){ - async->buf_user_ptr=0; - } buf+=n; break; /* makes device work like a pipe */ @@ -1440,9 +1439,9 @@ static ssize_t comedi_read_v22(struct file * file,char *buf,size_t nbytes,loff_t n=nbytes; - m = async->buf_int_ptr - async->buf_user_ptr; - if( m < 0 ) - m = async->data_len - async->buf_user_ptr; + m = async->buf_write_count - async->buf_read_count; + if( m > async->data_len ) + m = async->data_len; #if 0 printk("m is %d\n",m); @@ -1470,15 +1469,17 @@ printk("m is %d\n",m); schedule(); continue; } - m=copy_to_user(buf,async->data+async->buf_user_ptr,n); - if(m) retval=-EFAULT; + m=0; + retval = read_from_async_buffer( async, buf, n, 1 ); + if( retval ) + { + m = n; + } n-=m; /* check for buffer overflow */ - if(async->buf_int_count - async->buf_user_count > async->data_len){ - async->buf_user_count = async->buf_int_count; - async->buf_user_ptr = async->buf_int_ptr; - retval=-EIO; + if( retval == -EIO ) + { do_cancel(dev, dev->read_subdev); DPRINTK("buffer overflow\n"); break; @@ -1486,19 +1487,13 @@ printk("m is %d\n",m); count+=n; nbytes-=n; - async->buf_user_ptr+=n; - async->buf_user_count+=n; - - if(async->buf_user_ptr>=async->data_len ){ - async->buf_user_ptr=0; - } buf+=n; break; /* makes device work like a pipe */ } if(!(s->subdev_flags&SDF_RUNNING) && !(s->runflags & SRF_ERROR) && - async->buf_int_count - async->buf_user_count == 0) + async->buf_read_count - async->buf_write_count == 0) { do_become_nonbusy(dev,s); } @@ -1525,10 +1520,7 @@ void do_become_nonbusy(comedi_device *dev,comedi_subdevice *s) #endif if(async){ - async->buf_user_ptr=0; - async->buf_int_ptr=0; - async->buf_user_count=0; - async->buf_int_count=0; + init_async_buf( async ); }else{ printk("BUG: (?) do_become_nonbusy called with async=0\n"); } @@ -1859,3 +1851,12 @@ void comedi_event(comedi_device *dev,comedi_subdevice *s, unsigned int mask) } } +static void init_async_buf( comedi_async *async ) +{ + async->buf_read_count = 0; + async->buf_write_count = 0; + async->buf_dirty_count = 0; + async->buf_read_ptr = 0; + async->buf_write_ptr = 0; +} + diff --git a/comedi/drivers/adl_pci9118.c b/comedi/drivers/adl_pci9118.c index afe9438c..5fbfcfc7 100644 --- a/comedi/drivers/adl_pci9118.c +++ b/comedi/drivers/adl_pci9118.c @@ -301,7 +301,7 @@ typedef struct{ unsigned char cnt0_users; // bit field of 8254 CNT0 users (0-unused, 1-AO, 2-DI, 3-DO) unsigned char exttrg_users; // bit field of external trigger users (0-AI, 1-AO, 2-DI, 3-DO) unsigned int cnt0_divisor; // actual CNT0 divisor - int (*dma_ai_read_block)(comedi_device *, comedi_subdevice *, lsampl_t **, lsampl_t *, unsigned int *, unsigned int *); // ptr to actual transfer function from DMA buffer + int (*dma_ai_read_block)(comedi_device *, comedi_subdevice *, void *, int *, int *); // ptr to actual transfer function from DMA buffer void (*int_ai_func)(comedi_device *, comedi_subdevice *,unsigned short, unsigned int, unsigned short); // ptr to actual interrupt AI function unsigned char ai16bits; // =1 16 bit card unsigned char usedma; // =1 use DMA transfer and not INT @@ -333,7 +333,7 @@ static int pci9118_exttrg_del(comedi_device * dev, unsigned char source); static int pci9118_ai_cancel(comedi_device * dev, comedi_subdevice * s); static void pci9118_calc_divisors(char mode, comedi_device * dev, comedi_subdevice * s, unsigned int *tim1, unsigned int *tim2, unsigned int flags, - int chans, unsigned int *div1, unsigned int *div2, + int chans, unsigned int *div1, unsigned int *div2, char usessh, unsigned int chnsshfront); /* @@ -385,7 +385,7 @@ conv_finish: static int pci9118_insn_write_ao(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) { int n,chanreg,ch; - + ch=CR_CHAN(insn->chanspec); if (ch) { chanreg=PCI9118_DA2;} else { chanreg=PCI9118_DA1; } @@ -437,7 +437,7 @@ static int pci9118_insn_bits_do(comedi_device *dev,comedi_subdevice *s, comedi_i return 2; } -/* +/* ============================================================================== */ static void interrupt_pci9118_ai_mode4_switch(comedi_device *dev) @@ -481,14 +481,15 @@ static int skip_front_samples_16b(comedi_device *dev, comedi_subdevice *s, return -1; } -/* +/* ============================================================================== */ static int move_block_from_dma_12bit_16b(comedi_device *dev, comedi_subdevice *s, - sampl_t **dma, sampl_t *data, int *sampls, int *bufs) + void *dma_void, int *sampls, int *bufs) { unsigned int cc,sp,chans,chns; sampl_t sampl; + uint16_t **dma = (uint16_t **) dma_void; cc=s->async->cur_chan; sp=devpriv->ai_act_scanpos; @@ -513,7 +514,7 @@ static int move_block_from_dma_12bit_16b(comedi_device *dev, comedi_subdevice *s for (;ccchanlist[cc]) { + if ((sampl & 0x0f00)!=devpriv->chanlist[cc]) { rt_printk("comedi: A/D DMA - data dropout: received channel %04x, expected %04x!\n",(sampl & 0x0f00),devpriv->chanlist[cc]); DPRINTK("comedi: bufs=%d cc=%d sp=%d dmapos=%d chans=%d af=%d ab=%d sampls=%d (dma)=%04x dma=%04x!\n",*bufs,cc,sp,devpriv->ai_act_dmapos,chans,devpriv->ai_add_front,devpriv->ai_add_back,*sampls,*(*dma-1),(int)(*dma-1)); s->async->events|=COMEDI_CB_ERROR|COMEDI_CB_EOA; @@ -523,15 +524,14 @@ static int move_block_from_dma_12bit_16b(comedi_device *dev, comedi_subdevice *s } #endif sampl=((sampl & 0xff)<<4)|((sampl & 0xf000)>>12); // get one sample - *data=sampl; - data++; + comedi_buf_put( s->async, sampl); } sp+=cc; if (cc>=chans) { cc=0; devpriv->ai_act_dmapos=0; if (devpriv->ai_add_back) // drop added one - if (*bufs) { + if (*bufs) { (*bufs)--; (*dma)++; } @@ -539,8 +539,7 @@ static int move_block_from_dma_12bit_16b(comedi_device *dev, comedi_subdevice *s if(sp>=devpriv->ai_n_scanlen) { // is end of one scan? sp=0; devpriv->ai_act_scan++; - s->async->events|=COMEDI_CB_EOS; - if (*sampls) + if (*sampls) if (devpriv->ai_flags & TRIG_WAKE_EOS) { comedi_event(dev,s,s->async->events); s->async->events=0; @@ -549,19 +548,19 @@ static int move_block_from_dma_12bit_16b(comedi_device *dev, comedi_subdevice *s } // DPRINTK("9 cc=%d sp=%d *bufs=%d *sampls=%d chns=%d/%d dmapos=%d\n",cc,sp,*bufs,*sampls,chns,chans,devpriv->ai_act_dmapos); devpriv->ai_act_scanpos=sp; - s->async->cur_chan=cc; return 0; } -/* +/* ============================================================================== */ static int move_block_from_dma_16bit_16b(comedi_device *dev, comedi_subdevice *s, - sampl_t **dma, sampl_t *data, int *sampls, int *bufs) + void *dma_void, int *sampls, int *bufs) { unsigned int cc,sp,chans,chns; sampl_t sampl; + uint16_t **dma = (uint16_t **) dma_void; cc=s->async->cur_chan; sp=devpriv->ai_act_scanpos; @@ -586,15 +585,14 @@ static int move_block_from_dma_16bit_16b(comedi_device *dev, comedi_subdevice *s for (;cc>8))^0x8000; // get one sample - *data=sampl; - data++; + comedi_buf_put( s->async, sampl ); } sp+=cc; if (cc>=chans) { cc=0; devpriv->ai_act_dmapos=0; if (devpriv->ai_add_back) // drop added one - if (*bufs) { + if (*bufs) { (*bufs)--; (*dma)++; } @@ -602,8 +600,7 @@ static int move_block_from_dma_16bit_16b(comedi_device *dev, comedi_subdevice *s if(sp>=devpriv->ai_n_scanlen) { // is end of one scan? sp=0; devpriv->ai_act_scan++; - s->async->events|=COMEDI_CB_EOS; - if (*sampls) + if (*sampls) if (devpriv->ai_flags & TRIG_WAKE_EOS) { comedi_event(dev,s,s->async->events); s->async->events=0; @@ -612,12 +609,11 @@ static int move_block_from_dma_16bit_16b(comedi_device *dev, comedi_subdevice *s } // DPRINTK("9 cc=%d sp=%d *bufs=%d *sampls=%d chns=%d/%d dmapos=%d\n",cc,sp,*bufs,*sampls,chns,chans,devpriv->ai_act_dmapos); devpriv->ai_act_scanpos=sp; - s->async->cur_chan=cc; return 0; } -/* +/* ============================================================================== Skip a few samples at begin of every scan if is used software generated sample&hold signal @@ -627,7 +623,7 @@ static int skip_front_samples_32b(comedi_device *dev, comedi_subdevice *s, { unsigned int n; - n=(devpriv->ai_add_front-devpriv->ai_act_dmapos)>>1; + n=(devpriv->ai_add_front-devpriv->ai_act_dmapos)>>1; // DPRINTK("n=%d bufs=%d *dma=0x%08x dmapos=%d %d\n",n,*bufs,*dma,devpriv->ai_act_dmapos,x); if (*bufs>n) { (*bufs)-=n; @@ -645,17 +641,18 @@ static int skip_front_samples_32b(comedi_device *dev, comedi_subdevice *s, return -1; } -/* +/* ============================================================================== */ static int move_block_from_dma_12bit_32b(comedi_device *dev, comedi_subdevice *s, - lsampl_t **dma, lsampl_t *data, int *sampls, int *bufs) + void *dma_void, int *sampls, int *bufs) { int cc,sp,chans,chns,xx,yy; lsampl_t sampl; #ifdef PCI9118_PARANOIDCHECK lsampl_t *chanlist=(lsampl_t *)devpriv->chanlist; #endif + uint32_t ** dma = ( uint32_t ** ) dma_void; cc=s->async->cur_chan; sp=devpriv->ai_act_scanpos; @@ -674,7 +671,7 @@ static int move_block_from_dma_12bit_32b(comedi_device *dev, comedi_subdevice *s for (;ccai_act_dmapos,chns,chans,devpriv->ai_add_front,devpriv->ai_add_back,*sampls,*(*dma-1),(int)(*dma-1)); s->async->events|=COMEDI_CB_ERROR|COMEDI_CB_EOA; @@ -684,8 +681,7 @@ static int move_block_from_dma_12bit_32b(comedi_device *dev, comedi_subdevice *s } #endif sampl=((sampl & 0xfff0)<<12)|((sampl & 0xfff00000)>>20); - *data=sampl; - data++; + comedi_buf_put_long( s->async, sampl ); } sp+=cc<<1; if (cc>=chans) { @@ -695,8 +691,7 @@ static int move_block_from_dma_12bit_32b(comedi_device *dev, comedi_subdevice *s if(sp>=devpriv->ai_n_scanlen) { // is end of one scan? sp=0; devpriv->ai_act_scan++; - s->async->events|=COMEDI_CB_EOS; - if (*sampls) + if (*sampls) if (devpriv->ai_flags & TRIG_WAKE_EOS) { comedi_event(dev,s,s->async->events); s->async->events=0; @@ -706,18 +701,18 @@ static int move_block_from_dma_12bit_32b(comedi_device *dev, comedi_subdevice *s *bufs-=*bufs-(xx<<1); *sampls-=*sampls-(yy<<1); devpriv->ai_act_scanpos=sp; - s->async->cur_chan=cc; return 0; } -/* +/* ============================================================================== */ static int move_block_from_dma_16bit_32b(comedi_device *dev, comedi_subdevice *s, - lsampl_t **dma, lsampl_t *data, int *sampls, int *bufs) + void *dma_void, int *sampls, int *bufs) { int cc,sp,chans,chns,xx,yy; lsampl_t sampl; + uint32_t **dma = ( uint32_t ** ) dma_void; cc=s->async->cur_chan; sp=devpriv->ai_act_scanpos; @@ -736,8 +731,7 @@ static int move_block_from_dma_16bit_32b(comedi_device *dev, comedi_subdevice *s for (;cc>16))^0x80008000; - *data=sampl; - data++; + comedi_buf_put_long( s->async, sampl ); } sp+=cc<<1; if (cc>=chans) { @@ -747,8 +741,7 @@ static int move_block_from_dma_16bit_32b(comedi_device *dev, comedi_subdevice *s if(sp>=devpriv->ai_n_scanlen) { // is end of one scan? sp=0; devpriv->ai_act_scan++; - s->async->events|=COMEDI_CB_EOS; - if (*sampls) + if (*sampls) if (devpriv->ai_flags & TRIG_WAKE_EOS) { comedi_event(dev,s,s->async->events); s->async->events=0; @@ -758,7 +751,6 @@ static int move_block_from_dma_16bit_32b(comedi_device *dev, comedi_subdevice *s *bufs-=*bufs-(xx<<1); *sampls-=*sampls-(yy<<1); devpriv->ai_act_scanpos=sp; - s->async->cur_chan=cc; return 0; } @@ -810,7 +802,7 @@ static void interrupt_pci9118_ai_onesample(comedi_device *dev,comedi_subdevice * sampl=inw(dev->iobase+PCI9118_AD_DATA); if (devpriv->ai16bits) { - *(sampl_t *)(s->async->data+s->async->buf_int_ptr)=sampl^0x8000; + comedi_buf_put( s->async, sampl ^ 0x8000 ); } else { #ifdef PCI9118_PARANOIDCHECK if ((sampl & 0x000f)!=devpriv->chanlist[s->async->cur_chan]) { // data dropout! @@ -821,42 +813,31 @@ static void interrupt_pci9118_ai_onesample(comedi_device *dev,comedi_subdevice * return; } #endif - *(sampl_t *)(s->async->data+s->async->buf_int_ptr)=(sampl>>4) & 0x0fff; + comedi_buf_put( s->async, ( sampl >> 4 ) & 0x0fff ); } - s->async->buf_int_ptr+=sizeof(sampl_t); - s->async->buf_int_count+=sizeof(sampl_t); - - s->async->cur_chan++; - if (s->async->cur_chan >= devpriv->ai_n_scanlen) { /* one scan done */ - s->async->cur_chan=0; + if (s->async->cur_chan == 0) { /* one scan done */ devpriv->ai_act_scan++; - s->async->events |= COMEDI_CB_EOS; if (!(devpriv->ai_neverending)) if (devpriv->ai_act_scan>=devpriv->ai_scans) { /* all data sampled */ pci9118_ai_cancel(dev,s); s->async->events |= COMEDI_CB_EOA; } } - - if (s->async->buf_int_ptr >= s->async->data_len) { /* buffer rollover */ - s->async->buf_int_ptr = 0; - s->async->events |= COMEDI_CB_EOBUF; - } - if (s->async->events) + if (s->async->events) comedi_event(dev,s,s->async->events); } -/* +/* ============================================================================== */ -static void interrupt_pci9118_ai_dma(comedi_device *dev,comedi_subdevice *s, - unsigned short int_adstat, unsigned int int_amcc, unsigned short int_daq) +static void interrupt_pci9118_ai_dma(comedi_device *dev,comedi_subdevice *s, + unsigned short int_adstat, unsigned int int_amcc, unsigned short int_daq) { lsampl_t *ptr; unsigned int next_dma_buf, samplesinbuf, sampls, m; - + s->async->events=0; if (int_amcc&MASTER_ABORT_INT) { @@ -877,54 +858,43 @@ static void interrupt_pci9118_ai_dma(comedi_device *dev,comedi_subdevice *s, if (int_adstat & devpriv->ai_maskerr) // if (int_adstat & 0x106) - if (pci9118_decode_error_status(dev,s,int_adstat)) + if (pci9118_decode_error_status(dev,s,int_adstat)) return; samplesinbuf=devpriv->dmabuf_use_size[devpriv->dma_actbuf]>>1; // number of received real samples // DPRINTK("dma_actbuf=%d\n",devpriv->dma_actbuf); - + if (devpriv->dma_doublebuf) { // switch DMA buffers if is used double buffering next_dma_buf=1-devpriv->dma_actbuf; outl(devpriv->dmabuf_hw[next_dma_buf], devpriv->iobase_a+AMCC_OP_REG_MWAR); outl(devpriv->dmabuf_use_size[next_dma_buf], devpriv->iobase_a+AMCC_OP_REG_MWTC); devpriv->dmabuf_used_size[next_dma_buf]=devpriv->dmabuf_use_size[next_dma_buf]; - if (devpriv->ai_do==4) - interrupt_pci9118_ai_mode4_switch(dev); + if (devpriv->ai_do==4) + interrupt_pci9118_ai_mode4_switch(dev); } ptr=(lsampl_t *)devpriv->dmabuf_virt[devpriv->dma_actbuf]; while (samplesinbuf) { - m=(devpriv->ai_data_len-s->async->buf_int_ptr)>>1; // how many samples is to end of buffer + m = devpriv->ai_data_len >> 1; // how many samples is to end of buffer // DPRINTK("samps=%d m=%d %d %d\n",samplesinbuf,m,s->async->buf_int_count,s->async->buf_int_ptr); sampls=m; - if (devpriv->dma_ai_read_block(dev, s, &ptr, - ((void *)(devpriv->ai_data))+s->async->buf_int_ptr, + if (devpriv->dma_ai_read_block(dev, s, &ptr, &sampls, &samplesinbuf)) return; // Uiii, error m=m-sampls; // m= how many samples was transfered - s->async->buf_int_count+=m<<1; - s->async->buf_int_ptr+=m<<1; - s->async->events|=COMEDI_CB_BLOCK; - if (s->async->buf_int_ptr>=devpriv->ai_data_len) { // output buffer filled -// DPRINTK("EOBUF scans=%d\n",devpriv->ai_act_scan); - s->async->events|=COMEDI_CB_EOBUF; - s->async->buf_int_ptr=0; - comedi_event(dev,s,s->async->events); - s->async->events=0; - } } // DPRINTK("YYY\n"); - + if (!devpriv->ai_neverending) if ( devpriv->ai_act_scan>=devpriv->ai_scans ) { /* all data sampled */ pci9118_ai_cancel(dev,s); s->async->events|=COMEDI_CB_EOA; } - + if (devpriv->dma_doublebuf) { // switch dma buffers - devpriv->dma_actbuf=1-devpriv->dma_actbuf; + devpriv->dma_actbuf=1-devpriv->dma_actbuf; } else { // restart DMA if is not used double buffering outl(devpriv->dmabuf_hw[0], devpriv->iobase_a+AMCC_OP_REG_MWAR); outl(devpriv->dmabuf_use_size[0], devpriv->iobase_a+AMCC_OP_REG_MWTC); @@ -1450,17 +1420,17 @@ static int pci9118_ai_docmd_dma(comedi_device * dev, comedi_subdevice * s) switch (devpriv->usedma) { case 2: // 32 bit DMA mode if (devpriv->ai16bits) { // select interrupt function - devpriv->dma_ai_read_block=(void *)move_block_from_dma_16bit_32b; + devpriv->dma_ai_read_block=move_block_from_dma_16bit_32b; } else { - devpriv->dma_ai_read_block=(void *)move_block_from_dma_12bit_32b; + devpriv->dma_ai_read_block=move_block_from_dma_12bit_32b; } outl(0x00000000|AINT_WRITE_COMPL, devpriv->iobase_a+AMCC_OP_REG_INTCSR); break; case 1: // 16 bit DMA mode if (devpriv->ai16bits) { // select interrupt function - devpriv->dma_ai_read_block=(void *)move_block_from_dma_16bit_16b; + devpriv->dma_ai_read_block=move_block_from_dma_16bit_16b; } else { - devpriv->dma_ai_read_block=(void *)move_block_from_dma_12bit_16b; + devpriv->dma_ai_read_block=move_block_from_dma_12bit_16b; } outl(0x02000000|AINT_WRITE_COMPL, devpriv->iobase_a+AMCC_OP_REG_INTCSR); break; diff --git a/comedi/drivers/comedi_parport.c b/comedi/drivers/comedi_parport.c index 5c9fd93e..3d31117a 100644 --- a/comedi/drivers/comedi_parport.c +++ b/comedi/drivers/comedi_parport.c @@ -283,14 +283,7 @@ static void parport_interrupt(int irq,void *d,struct pt_regs *regs) return; } - *(sampl_t *)(s->async->data+s->async->buf_int_ptr)=0; - s->async->buf_int_ptr+=sizeof(sampl_t); - s->async->buf_int_count+=sizeof(sampl_t); - if(s->async->buf_int_ptr>=s->async->data_len){ - s->async->buf_int_ptr=0; - s->async->events |= COMEDI_CB_EOBUF; - } - s->async->events |= COMEDI_CB_EOS; + comedi_buf_put( s->async, 0 ); comedi_event(dev,s,s->async->events); } diff --git a/comedi/drivers/das16m1.c b/comedi/drivers/das16m1.c index 0e9a4aaf..fad54005 100644 --- a/comedi/drivers/das16m1.c +++ b/comedi/drivers/das16m1.c @@ -460,7 +460,7 @@ static int das16m1_poll(comedi_device *dev, comedi_subdevice *s) das16m1_handler(dev, status); comedi_spin_unlock_irqrestore(&dev->spinlock, flags); - return s->async->buf_int_count - s->async->buf_user_count; + return s->async->buf_write_count - s->async->buf_read_count; } static void das16m1_interrupt(int irq, void *d, struct pt_regs *regs) diff --git a/comedi/drivers/das1800.c b/comedi/drivers/das1800.c index 49126da6..dfdef7fb 100644 --- a/comedi/drivers/das1800.c +++ b/comedi/drivers/das1800.c @@ -879,7 +879,7 @@ static int das1800_ai_poll(comedi_device *dev,comedi_subdevice *s) das1800_ai_handler(dev); comedi_spin_unlock_irqrestore(&dev->spinlock, flags); - return s->async->buf_int_count - s->async->buf_user_count; + return s->async->buf_write_count - s->async->buf_read_count; } static void das1800_interrupt(int irq, void *d, struct pt_regs *regs) diff --git a/comedi/drivers/das6402.c b/comedi/drivers/das6402.c index 855c5a95..1ae87e24 100644 --- a/comedi/drivers/das6402.c +++ b/comedi/drivers/das6402.c @@ -173,7 +173,7 @@ static void intr_handler(int irq,void *d,struct pt_regs *regs) das6402_ai_fifo_dregs(dev,s); - if(s->async->buf_int_count >= devpriv->ai_bytes_to_read){ + if(s->async->buf_write_count >= devpriv->ai_bytes_to_read){ outw_p(SCANL,dev->iobase+2); /* clears the fifo */ outb(0x07,dev->iobase+8); /* clears all flip-flops */ #ifdef DEBUG diff --git a/comedi/drivers/dt282x.c b/comedi/drivers/dt282x.c index 6a2723d3..3b4ddaa0 100644 --- a/comedi/drivers/dt282x.c +++ b/comedi/drivers/dt282x.c @@ -409,53 +409,6 @@ static void dt282x_cleanup_buffer(comedi_device *dev,unsigned short *buf,unsigne } } -static void copy_to_buf(comedi_device *dev,comedi_subdevice *s,void *buf,unsigned int n_bytes) -{ - unsigned int n; - - n=n_bytes; - if(s->async->buf_int_ptr+n >= s->async->data_len){ - n=s->async->data_len-s->async->buf_int_ptr; - memcpy(s->async->data+s->async->buf_int_ptr,buf,n); - buf+=n; - s->async->buf_int_count+=n; - s->async->buf_int_ptr=0; - - n=n_bytes-n; - } - memcpy(s->async->data+s->async->buf_int_ptr,buf,n); - buf+=n; - s->async->buf_int_count+=n; - s->async->buf_int_ptr+=n; -} - -static int copy_from_buf(comedi_device *dev,comedi_subdevice *s,void *buf,unsigned int n_bytes) -{ - unsigned int n,m; - - n=s->async->buf_int_count-s->async->buf_user_count; - if(n==0)return 0; - if(n>n_bytes) - n=n_bytes; - - n_bytes=n; - if(s->async->buf_int_ptr+n >= s->async->data_len){ - m=s->async->data_len-s->async->buf_int_ptr; - memcpy(buf,s->async->data+s->async->buf_int_ptr,m); - buf+=m; - s->async->buf_int_count+=m; - s->async->buf_int_ptr=0; - - n-=m; - } - memcpy(buf,s->async->data+s->async->buf_int_ptr,n); - s->async->buf_int_count+=n; - s->async->buf_int_ptr+=n; - - return n_bytes; -} - - static void dt282x_ao_dma_interrupt(comedi_device * dev) { void *ptr; @@ -477,19 +430,18 @@ static void dt282x_ao_dma_interrupt(comedi_device * dev) devpriv->current_dma_chan=1-i; - size=copy_from_buf(dev,s,ptr,devpriv->dma_maxsize*2); - if(!size){ + size = comedi_buf_get_array( s->async, ptr, devpriv->dma_maxsize ); + if( size < 0){ printk("dt282x: AO underrun\n"); dt282x_ao_cancel(dev,s); s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR; comedi_event(dev,s,s->async->events); return; } - prep_ao_dma(dev,i,size/2); + prep_ao_dma(dev,i,size); enable_dma(devpriv->dma[i].chan); - s->async->events |= COMEDI_CB_BLOCK; comedi_event(dev,s,s->async->events); return; } @@ -516,7 +468,7 @@ static void dt282x_ai_dma_interrupt(comedi_device * dev) devpriv->current_dma_chan=1-i; dt282x_cleanup_buffer(dev,ptr,size); - copy_to_buf(dev,s,ptr,size*2); + comedi_buf_put_array( s->async, ptr, size ); devpriv->nread-=size; if(devpriv->nread<0){ @@ -535,8 +487,6 @@ static void dt282x_ai_dma_interrupt(comedi_device * dev) comedi_event(dev,s,s->async->events); return; - }else{ - s->async->events |= COMEDI_CB_BLOCK; } #if 1 @@ -646,13 +596,7 @@ static void dt282x_interrupt(int irq, void *d, struct pt_regs *regs) if(devpriv->ad_2scomp){ data^=1<<(boardtype.adbits-1); } - *(sampl_t *)(s->async->data+s->async->buf_int_ptr)=data; - s->async->buf_int_ptr+=sizeof(sampl_t); - s->async->buf_int_count+=sizeof(sampl_t); - if(s->async->buf_int_ptr>=s->async->data_len){ - s->async->buf_int_ptr = 0; - s->async->events |= COMEDI_CB_EOBUF; - } + comedi_buf_put( s->async, data ); devpriv->nread--; if(!devpriv->nread){ @@ -1049,12 +993,14 @@ static int dt282x_ao_inttrig(comedi_device *dev,comedi_subdevice *s, if(x!=0)return -EINVAL; - size=copy_from_buf(dev,s,devpriv->dma[0].buf,devpriv->dma_maxsize*2); - prep_ao_dma(dev,0,size/2); + size=comedi_buf_get_array( s->async, devpriv->dma[0].buf, devpriv->dma_maxsize); + if( size < 0 ) return size; + + prep_ao_dma(dev,0,size); enable_dma(devpriv->dma[0].chan); - size=copy_from_buf(dev,s,devpriv->dma[1].buf,devpriv->dma_maxsize*2); - prep_ao_dma(dev,1,size/2); + size=comedi_buf_get_array( s->async, devpriv->dma[1].buf, devpriv->dma_maxsize); + prep_ao_dma(dev,1,size); enable_dma(devpriv->dma[1].chan); update_supcsr(DT2821_STRIG); diff --git a/comedi/drivers/ni_mio_common.c b/comedi/drivers/ni_mio_common.c index 7057b689..4e101b6f 100644 --- a/comedi/drivers/ni_mio_common.c +++ b/comedi/drivers/ni_mio_common.c @@ -339,7 +339,7 @@ static void ni_E_interrupt(int irq,void *d,struct pt_regs * regs) handle_a_interrupt(dev,a_status,m0_status); } if(b_status&Interrupt_B_St)handle_b_interrupt(dev,b_status); - + win_restore(wsave); } @@ -352,41 +352,41 @@ static void mite_handle_a_linkc(struct mite_struct *mite, comedi_device *dev) writel(CHOR_CLRLC, mite->mite_io_addr+MITE_CHOR+CHAN_OFFSET(mite->chan)); - count = mite_bytes_transferred(mite, 0) - async->buf_int_count; + count = mite_bytes_transferred(mite, 0) - async->buf_write_count; if(count < 0 || count > 100000){ printk("BUG: too many samples in interrupt (%d)\n",count); return; } - async->buf_int_count += count; + async->buf_write_count += count; if(async->cmd.flags & CMDF_RAWDATA){ /* * Don't munge the data, just update the user's status * variables */ - async->buf_int_ptr += count; - if(async->buf_int_ptr >= async->prealloc_bufsz) - async->buf_int_ptr -= async->prealloc_bufsz; + async->buf_write_ptr += count; + if(async->buf_write_ptr >= async->prealloc_bufsz) + async->buf_write_ptr -= async->prealloc_bufsz; }else{ /* * Munge the ADC data to change its format from two's * complement to unsigned int. This is slow but makes * it more compatible with other cards. */ - if(async->buf_int_ptr + count >= async->prealloc_bufsz){ + if(async->buf_write_ptr + count >= async->prealloc_bufsz){ ni_munge(dev,s, - async->prealloc_buf + async->buf_int_ptr, + async->prealloc_buf + async->buf_write_ptr, async->prealloc_buf + async->prealloc_bufsz); - async->buf_int_ptr += count; - async->buf_int_ptr -= async->prealloc_bufsz; + async->buf_write_ptr += count; + async->buf_write_ptr -= async->prealloc_bufsz; ni_munge(dev,s, async->prealloc_buf, - async->prealloc_buf + async->buf_int_ptr); + async->prealloc_buf + async->buf_write_ptr); }else{ ni_munge(dev,s, - async->prealloc_buf + async->buf_int_ptr, - async->prealloc_buf + async->buf_int_ptr + count); - async->buf_int_ptr += count; + async->prealloc_buf + async->buf_write_ptr, + async->prealloc_buf + async->buf_write_ptr + count); + async->buf_write_ptr += count; } } s->async->events |= COMEDI_CB_BLOCK; @@ -399,7 +399,7 @@ static void mite_handle_interrupt(comedi_device *dev,unsigned int m_status) comedi_subdevice *s = dev->subdevices+0; comedi_async *async = s->async; struct mite_struct *mite = devpriv->mite; - + async->events |= COMEDI_CB_BLOCK; MDPRINTK("mite_handle_interrupt: m_status=%08x\n",m_status); @@ -852,7 +852,7 @@ static int ni_ai_poll(comedi_device *dev,comedi_subdevice *s) //comedi_event(dev,s,s->async->events); - return s->async->buf_int_count-s->async->buf_user_count; + return s->async->buf_write_count - s->async->buf_read_count; #else /* XXX we don't support this yet. */ return -EINVAL; @@ -1647,14 +1647,14 @@ static void ni_ao_fifo_load(comedi_device *dev,comedi_subdevice *s, // increment channel index async->cur_chan = (async->cur_chan + n) % async->cmd.chanlist_len; // increment async buf_int_count and buf_int_ptr - async->buf_int_count += n * sizeof(sampl_t); - async->buf_int_ptr += n * sizeof(sampl_t); + async->buf_read_count += n * sizeof(sampl_t); + async->buf_read_ptr += n * sizeof(sampl_t); // check if we have reached end of buffer - if(async->buf_int_ptr >= async->data_len) + if(async->buf_read_ptr >= async->data_len) { - async->buf_int_ptr -= async->data_len; + async->buf_read_ptr -= async->data_len; // this shouldn't ever happen - if(async->buf_int_ptr >= async->data_len) + if(async->buf_read_ptr >= async->data_len) comedi_error(dev, "ao bug!"); } } @@ -1680,17 +1680,17 @@ static int ni_ao_fifo_half_empty(comedi_device *dev,comedi_subdevice *s) { int n,m; - n=(s->async->buf_int_count-s->async->buf_user_count)/sizeof(sampl_t); + n=(s->async->buf_read_count - s->async->buf_write_count) / sizeof(sampl_t); if(n==0)return 0; if(n>boardtype.ao_fifo_depth/2) n=boardtype.ao_fifo_depth/2; - if(s->async->buf_int_ptr+n*sizeof(sampl_t)>s->async->data_len){ - m=(s->async->data_len-s->async->buf_int_ptr)/sizeof(sampl_t); - ni_ao_fifo_load(dev,s,s->async->data+s->async->buf_int_ptr,m); + if(s->async->buf_read_ptr+n*sizeof(sampl_t)>s->async->data_len){ + m=(s->async->data_len-s->async->buf_read_ptr)/sizeof(sampl_t); + ni_ao_fifo_load(dev,s,s->async->data+s->async->buf_read_ptr,m); n-=m; } - ni_ao_fifo_load(dev,s,s->async->data+s->async->buf_int_ptr,n); + ni_ao_fifo_load(dev,s,s->async->data+s->async->buf_read_ptr,n); s->async->events |= COMEDI_CB_BLOCK; @@ -1705,12 +1705,12 @@ static int ni_ao_prep_fifo(comedi_device *dev,comedi_subdevice *s) win_out(0,DAC_FIFO_Clear); /* load some data */ - n=(s->async->buf_user_count-s->async->buf_int_count)/sizeof(sampl_t); + n=(s->async->buf_write_count-s->async->buf_read_count)/sizeof(sampl_t); if(n<0)return 0; if(n>boardtype.ao_fifo_depth) n=boardtype.ao_fifo_depth; - ni_ao_fifo_load(dev,s,s->async->data+s->async->buf_int_ptr,n); + ni_ao_fifo_load(dev,s,s->async->data+s->async->buf_read_ptr,n); return n; } diff --git a/comedi/drivers/ni_pcidio.c b/comedi/drivers/ni_pcidio.c index 36679af2..ca0e3ee8 100644 --- a/comedi/drivers/ni_pcidio.c +++ b/comedi/drivers/ni_pcidio.c @@ -403,13 +403,13 @@ static void nidio_interrupt(int irq, void *d, struct pt_regs *regs) status = readb(dev->iobase+Interrupt_And_Window_Status); flags = readb(dev->iobase+Group_1_Flags); m_status = readl(mite->mite_io_addr + MITE_CHSR + CHAN_OFFSET(1)); - + //interrupcions parasites if(dev->attached == 0){ comedi_error(dev,"premature interrupt"); - async->events |= COMEDI_CB_ERROR|COMEDI_CB_EOA; + async->events |= COMEDI_CB_ERROR|COMEDI_CB_EOA; } - + DPRINTK("ni_pcidio_interrupt: status=0x%02x,flags=0x%02x,m_status=0x%08x\n", status,flags,m_status); ni_pcidio_print_flags(flags); @@ -432,10 +432,10 @@ static void nidio_interrupt(int irq, void *d, struct pt_regs *regs) /* XXX need to byteswap */ - async->buf_int_count += count; - async->buf_int_ptr += count; - if(async->buf_int_ptr >= async->data_len){ - async->buf_int_ptr -= async->data_len; + async->buf_write_count += count; + async->buf_write_ptr += count; + if(async->buf_write_ptr >= async->data_len){ + async->buf_write_ptr -= async->data_len; } mite->current_link++; if(mite->current_link >= mite->n_links){ @@ -454,7 +454,7 @@ static void nidio_interrupt(int irq, void *d, struct pt_regs *regs) } async->events |= COMEDI_CB_BLOCK; } - + while(status&DataLeft){ work++; if(work>20){ @@ -464,7 +464,7 @@ static void nidio_interrupt(int irq, void *d, struct pt_regs *regs) } flags &= IntEn; - + if(flags & TransferReady){ //DPRINTK("TransferReady\n"); while(flags & TransferReady){ @@ -493,7 +493,7 @@ static void nidio_interrupt(int irq, void *d, struct pt_regs *regs) DPRINTK("CountExpired\n"); writeb(ClearExpired,dev->iobase+Group_1_Second_Clear); async->events |= COMEDI_CB_EOA; - + writeb(0x00,dev->iobase+OpMode); writeb(0x00,dev->iobase+Master_DMA_And_Interrupt_Control); #ifdef USE_DMA @@ -838,7 +838,7 @@ static int ni_pcidio_cmd(comedi_device *dev,comedi_subdevice *s) }else{ /* XXX */ } - + #ifdef USE_DMA writeb(0x05,dev->iobase+DMA_Line_Control); writeb(0x30,dev->iobase+Group_1_First_Clear); diff --git a/comedi/drivers/pcl812.c b/comedi/drivers/pcl812.c index d4f337d7..136d17c6 100644 --- a/comedi/drivers/pcl812.c +++ b/comedi/drivers/pcl812.c @@ -1029,7 +1029,7 @@ static int pcl812_ai_poll(comedi_device *dev,comedi_subdevice *s) comedi_spin_unlock_irqrestore(&dev->spinlock,flags); - return s->async->buf_int_count-s->async->buf_user_count; + return s->async->buf_write_count-s->async->buf_read_count; } /* diff --git a/comedi/drivers/pcl816.c b/comedi/drivers/pcl816.c index ca957239..f629db36 100644 --- a/comedi/drivers/pcl816.c +++ b/comedi/drivers/pcl816.c @@ -700,7 +700,7 @@ static int pcl816_ai_poll(comedi_device *dev,comedi_subdevice *s) unsigned int top1,top2,i; if (!devpriv->dma) return 0; // poll is valid only for DMA transfer - + comedi_spin_lock_irqsave(&dev->spinlock, flags); for (i=0; i<20; i++) { @@ -723,11 +723,11 @@ static int pcl816_ai_poll(comedi_device *dev,comedi_subdevice *s) transfer_from_dma_buf(dev, s, (sampl_t*)devpriv->dmabuf[devpriv->next_dma_buf], devpriv->ai_poll_ptr, top2); - + devpriv->ai_poll_ptr = top1; // new buffer position comedi_spin_unlock_irqrestore(&dev->spinlock,flags); - return s->async->buf_int_count - s->async->buf_user_count; + return s->async->buf_write_count - s->async->buf_read_count; } diff --git a/comedi/drivers/rtd520.c b/comedi/drivers/rtd520.c index a369bfc6..e792bf0e 100644 --- a/comedi/drivers/rtd520.c +++ b/comedi/drivers/rtd520.c @@ -1187,7 +1187,7 @@ transferDone: static int rtd_ai_poll (comedi_device *dev,comedi_subdevice *s) { /* TODO: This needs to mask interrupts, read_dregs, and then re-enable */ - return s->async->buf_int_count - s->async->buf_user_count; + return s->async->buf_write_count - s->async->buf_read_count; } /* diff --git a/comedi/kcomedilib/get.c b/comedi/kcomedilib/get.c index 04aa33b4..62f2b000 100644 --- a/comedi/kcomedilib/get.c +++ b/comedi/kcomedilib/get.c @@ -178,7 +178,7 @@ unsigned int comedi_get_buf_head_pos(comedi_t *d,unsigned int subdevice) async = s->async; if(async == NULL) return 0; - return async->buf_int_count; + return async->buf_write_count; } /* @@ -191,7 +191,7 @@ int comedi_set_user_int_count(comedi_t *d,unsigned int subdevice,unsigned int bu comedi_async *async; async = s->async; - async->buf_user_count = buf_user_count; + async->buf_read_count = buf_user_count; return 0; } diff --git a/comedi/kcomedilib/kcomedilib_main.c b/comedi/kcomedilib/kcomedilib_main.c index b4615886..676d63b4 100644 --- a/comedi/kcomedilib/kcomedilib_main.c +++ b/comedi/kcomedilib/kcomedilib_main.c @@ -105,7 +105,7 @@ void comedi_perror(const char *message) char *comedi_strerror(int err) { return "unknown error"; -} +} int comedi_fileno(comedi_t *d) { @@ -115,6 +115,15 @@ int comedi_fileno(comedi_t *d) return dev->minor; } +static void init_async_buf( comedi_async *async ) +{ + async->buf_read_count = 0; + async->buf_write_count = 0; + async->buf_dirty_count = 0; + async->buf_read_ptr = 0; + async->buf_write_ptr = 0; +} + int comedi_command(comedi_t *d,comedi_cmd *cmd) { comedi_device *dev = (comedi_device *)d; @@ -145,10 +154,7 @@ int comedi_command(comedi_t *d,comedi_cmd *cmd) s->subdev_flags |= SDF_RUNNING; - async->buf_user_ptr=0; - async->buf_user_count=0; - async->buf_int_ptr=0; - async->buf_int_count=0; + init_async_buf( async ); async->data = cmd->data; async->data_len = cmd->data_len; diff --git a/include/linux/comedidev.h b/include/linux/comedidev.h index 9d994599..2e1311a1 100644 --- a/include/linux/comedidev.h +++ b/include/linux/comedidev.h @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -131,23 +132,26 @@ struct comedi_async_struct{ unsigned int mmap_count; /* current number of mmaps of prealloc_buf */ /* To avoid races and provide reliable overrun detection, read / writes - * to buffer should proceed as follows: + * to buffer should make use of the read_from_async_buffer() and + * write_to_async_buffer() functions and proceed + * as follows: + * * Writing: - * 1) if appropriate, check space in buffer, using either 'count' or 'ptr' members - * ('count' members are more convenient). - * 2) increment write count - * 3) write data to buffer - * 4) increment write ptr + * 1) if appropriate, check space in buffer, using read/write count members + * 2) call write_to_async_buffer() * Reading: - * 1) read data from buffer, using 'ptr' members to determine how much - * 2) update the read ptr - * 3) if appropriate, check for overrun using 'count' members - * 4) increment read count + * 1) if appropriate, check how much data is in buffer, using read/write + * count members + * 2) call read_from_async_buffer() + * 3) if appropriate, check return value for buffer overrun + * + * Note: there should be exactly one reader and one writer */ - volatile unsigned int buf_int_ptr; /* buffer marker for interrupt */ - unsigned int buf_user_ptr; /* buffer marker for read() and write() */ - volatile unsigned int buf_int_count; /* byte count for interrupt */ - unsigned int buf_user_count; /* byte count for read() and write() */ + unsigned int buf_write_ptr; /* buffer marker for writer */ + unsigned int buf_read_ptr; /* buffer marker for reader */ + volatile unsigned int buf_write_count; /* byte count for writer (write completed) */ + volatile unsigned int buf_dirty_count; /* byte count for writer (write in progress) */ + volatile unsigned int buf_read_count; /* byte count for reader */ unsigned int cur_chan; /* useless channel marker for interrupt */ void *data; @@ -321,77 +325,179 @@ static inline int alloc_private(comedi_device *dev,int size) return 0; } -// writes a data point to comedi's buffer, used for input -static inline void comedi_buf_put(comedi_async *async, sampl_t x) +/* To avoid races and provide reliable overrun detection, read / writes +* to buffer should proceed generally as follows: +* Writing: +* 1) if appropriate, check space in buffer, using read/write count members +* 2) increment dirty count +* 3) write data to buffer +* 4) increment write count +* 5) keep track of where to write with write ptr +* Reading: +* 1) read data from buffer, using read/write count members to determine how much +* 3) if appropriate, check for overrun using read/dirty count members +* 4) increment read count +* 2) keep track of where to read with read ptr +* +* Note: there should be exactly one reader and one writer +*/ +static inline int write_to_async_buffer( comedi_async *async, const void *array, + unsigned int num_bytes, unsigned int from_user_space ) { - async->buf_int_count += sizeof(sampl_t); - *(sampl_t *)(async->data + async->buf_int_ptr) = x; - async->buf_int_ptr += sizeof(sampl_t); - if(async->buf_int_ptr >= async->data_len){ - async->buf_int_ptr = 0; - async->events |= COMEDI_CB_EOBUF; - } - if(++async->cur_chan >= async->cmd.chanlist_len){ - async->cur_chan = 0; - async->events |= COMEDI_CB_EOS; + async->buf_dirty_count += num_bytes; + while( num_bytes ) + { + unsigned int block_size; + + block_size = num_bytes; + if( async->buf_write_ptr + block_size > async->data_len) + block_size = async->data_len - async->buf_write_ptr; + + if( from_user_space ) + { + if( copy_from_user( async->data + async->buf_write_ptr, array, block_size ) ) + return -EFAULT; + }else + memcpy( async->data + async->buf_write_ptr, array, block_size ); + + array += block_size; + num_bytes -= block_size; + async->buf_write_count += block_size; + async->buf_write_ptr += block_size; + if( async->buf_write_ptr == async->data_len ) + { + async->buf_write_ptr = 0; + } } - async->events |= COMEDI_CB_BLOCK; + + return 0; } -/* Writes an array of data points to comedi's buffer, used for input. - * Can be more efficient than putting comedi_buf_put() in a loop. */ -static inline void comedi_buf_put_array(comedi_async *async, sampl_t* array, unsigned int length) +static inline int read_from_async_buffer( comedi_async *async, void *destination, + unsigned int num_bytes, unsigned int from_user_space ) { - unsigned int num_bytes; - unsigned int xfer_count = 0; + unsigned int bytes_available = async->buf_write_count - async->buf_read_count; + unsigned int remaining = bytes_available; + + if( bytes_available == 0 ) return -EAGAIN; - while((num_bytes = length * sizeof(sampl_t) - xfer_count)) + while( remaining ) { - async->buf_int_count += num_bytes; - if( async->buf_int_ptr + num_bytes > async->data_len) - num_bytes = async->data_len - async->buf_int_ptr; + unsigned int block_size; - memcpy(async->data + async->buf_int_ptr + xfer_count, array, num_bytes); + block_size = remaining; + if( async->buf_read_ptr + block_size > async->data_len ) + block_size = async->data_len - async->buf_read_ptr; - async->buf_int_ptr += num_bytes; - if(async->buf_int_ptr >= async->data_len) + if( from_user_space ) { - async->buf_int_ptr %= async->data_len; - async->events |= COMEDI_CB_EOBUF; - } - async->cur_chan += num_bytes / sizeof(sampl_t); - if(async->cur_chan >= async->cmd.chanlist_len) + if( copy_to_user( destination, async->data + async->buf_read_ptr, block_size ) ) + return -EFAULT; + }else + memcpy( destination, async->data + async->buf_read_ptr, block_size ); + + destination += block_size; + remaining -= block_size; + async->buf_read_ptr += block_size; + if( async->buf_read_ptr == async->data_len ) { - async->cur_chan %= async->cmd.chanlist_len; - async->events |= COMEDI_CB_EOS; + async->buf_read_ptr = 0; } - xfer_count += num_bytes; + } + // check if buffer has overrun + if( async->buf_dirty_count - async->buf_read_count > async->data_len ) + return -EIO; + async->buf_read_count += bytes_available; + + return bytes_available - remaining; +} + +/* Writes an array of data points to comedi's buffer, used for input. + * Can be more efficient than putting comedi_buf_put() in a loop. */ +static inline void __comedi_buf_put_array(comedi_async *async, void* array, + unsigned int num_bytes, unsigned int bytes_per_sample ) +{ + if( async->buf_write_ptr + num_bytes >= async->data_len ) + { + async->events |= COMEDI_CB_EOBUF; + } + + write_to_async_buffer( async, array, num_bytes, 0 ); + + async->cur_chan += num_bytes / bytes_per_sample; + if( async->cur_chan >= async->cmd.chanlist_len ) + { + async->cur_chan %= async->cmd.chanlist_len; + async->events |= COMEDI_CB_EOS; } async->events |= COMEDI_CB_BLOCK; } +static inline void comedi_buf_put_array(comedi_async *async, sampl_t *array, + unsigned int num_samples ) +{ + __comedi_buf_put_array( async, array, num_samples * sizeof( sampl_t ), sizeof( sampl_t ) ); +} + +static inline void comedi_buf_put_long_array(comedi_async *async, lsampl_t *array, + unsigned int num_samples ) +{ + __comedi_buf_put_array( async, array, num_samples * sizeof( lsampl_t ), sizeof( lsampl_t ) ); +} + +// writes a data point to comedi's buffer, used for input +static inline void comedi_buf_put(comedi_async *async, sampl_t x) +{ + comedi_buf_put_array( async, &x, 1 ); +} + +// writes a long data point to comedi's buffer, used for input +static inline void comedi_buf_put_long(comedi_async *async, lsampl_t x) +{ + comedi_buf_put_long_array( async, &x, 1 ); +} + /* Reads a data point from comedi's buffer, used for output. * returns negative value on error. */ -static inline int comedi_buf_get(comedi_async *async, sampl_t *x) +static inline int comedi_buf_get_array(comedi_async *async, sampl_t *array, unsigned int num_samples) { - if(async->buf_int_count == async->buf_user_count) - return -EAGAIN; + unsigned int num_bytes = num_samples * sizeof( sampl_t ); + int retval; + unsigned int read_ptr = async->buf_read_ptr; - *x = *(sampl_t *)(async->data + async->buf_int_ptr); - async->buf_int_ptr += sizeof(sampl_t); - if(async->buf_int_ptr >= async->data_len){ - async->buf_int_ptr = 0; + retval = read_from_async_buffer( async, array, num_bytes, 0 ); + + if( retval < 0 ) + { + async->events |= COMEDI_CB_ERROR; + return retval; + } + + num_bytes = retval; + num_samples = retval / sizeof( sampl_t ); + + if( read_ptr + num_bytes >= async->data_len) + { async->events |= COMEDI_CB_EOBUF; } - if(++async->cur_chan >= async->cmd.chanlist_len){ - async->cur_chan = 0; + + async->cur_chan += num_samples; + if( async->cur_chan >= async->cmd.chanlist_len) + { + async->cur_chan %= async->cmd.chanlist_len; async->events |= COMEDI_CB_EOS; } - async->buf_int_count += sizeof(sampl_t); async->events |= COMEDI_CB_BLOCK; - return 0; + return retval / sizeof( sampl_t ); +} + +/* Reads a data point from comedi's buffer, used for output. + * returns negative value on error. */ +static inline int comedi_buf_get(comedi_async *async, sampl_t *x) +{ + return comedi_buf_get_array( async, x, 1); } -- 2.26.2