From: Frank Mori Hess Date: Thu, 26 Jul 2007 20:36:55 +0000 (+0000) Subject: Protected use of subdevice runflags with spinlock, since it is modified X-Git-Tag: r0_7_74~34 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=8cca7ca597b1f7427ffd84069bf795f099bf0259;p=comedi.git Protected use of subdevice runflags with spinlock, since it is modified in comedi_event(). Replaced most usage of SDF_RUNNING in subdev_flags with SRF_RUNNING in runflags so I don't have to protect subdev_flags with a lock too. Made mite_release_channel() always disarm and reset mite channel. Move ack/confirm of counter interrupts/errors into ni_tio. Added ni_tio_set_mite_channel(), which acquires counter's spinlock before modifiying its mite channel. Added some missing locking of mite_channel_lock in ni_mio_common.c. --- diff --git a/comedi/comedi_fops.c b/comedi/comedi_fops.c index 56c8b770..646bd459 100644 --- a/comedi/comedi_fops.c +++ b/comedi/comedi_fops.c @@ -364,6 +364,8 @@ static int do_subdinfo_ioctl(comedi_device *dev,comedi_subdinfo *arg,void *file) us->type = s->type; us->n_chan = s->n_chan; us->subd_flags = s->subdev_flags; + if(comedi_get_subdevice_runflags(s) & SRF_RUNNING) + us->subd_flags |= SDF_RUNNING; #define TIMER_nanosec 5 /* backwards compatibility */ us->timer_type = TIMER_nanosec; us->len_chanlist = s->len_chanlist; @@ -503,9 +505,8 @@ static int do_bufinfo_ioctl(comedi_device *dev,void *arg) bi.bytes_read = comedi_buf_read_alloc(async, bi.bytes_read); comedi_buf_read_free(async, bi.bytes_read); - if(!(s->subdev_flags&SDF_RUNNING) && - !(s->runflags & SRF_ERROR) && - async->buf_write_count==async->buf_read_count){ + if(!(comedi_get_subdevice_runflags(s) & (SRF_ERROR | SRF_RUNNING)) && + async->buf_write_count == async->buf_read_count){ do_become_nonbusy(dev,s); } } @@ -966,14 +967,12 @@ static int do_cmd_ioctl(comedi_device *dev,void *arg,void *file) async->cb_mask |= COMEDI_CB_EOS; } - s->runflags=SRF_USER; - - s->subdev_flags|=SDF_RUNNING; + comedi_set_subdevice_runflags(s, ~0, SRF_USER | SRF_RUNNING); #ifdef CONFIG_COMEDI_RT if(async->cmd.flags&TRIG_RT){ comedi_switch_to_rt(dev); - s->runflags |= SRF_RT; + comedi_set_subdevice_runflags(s, SRF_RT, SRF_RT); } #endif @@ -1247,7 +1246,7 @@ static int do_cancel(comedi_device *dev,comedi_subdevice *s) { int ret=0; - if((s->subdev_flags&SDF_RUNNING) && s->cancel) + if((comedi_get_subdevice_runflags(s) & SRF_RUNNING) && s->cancel) ret=s->cancel(dev,s); do_become_nonbusy(dev,s); @@ -1349,7 +1348,7 @@ static unsigned int comedi_poll(struct file *file, poll_table * wait) async = s->async; if(!s->busy || comedi_buf_read_n_available(async)>0 - || !(s->subdev_flags&SDF_RUNNING)){ + || !(comedi_get_subdevice_runflags(s) & SRF_RUNNING)){ mask |= POLLIN | POLLRDNORM; } } @@ -1357,7 +1356,7 @@ static unsigned int comedi_poll(struct file *file, poll_table * wait) s = dev->write_subdev; async = s->async; if(!s->busy - || !(s->subdev_flags&SDF_RUNNING) + || !(comedi_get_subdevice_runflags(s) & SRF_RUNNING) || comedi_buf_write_n_available(async) > 0){ mask |= POLLOUT | POLLWRNORM; } @@ -1420,8 +1419,8 @@ static ssize_t comedi_write(struct file *file,const char *buf,size_t nbytes,loff retval=-ERESTARTSYS; break; } - if(!(s->subdev_flags&SDF_RUNNING)){ - if(s->runflags & SRF_ERROR){ + if(!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)){ + if(comedi_get_subdevice_runflags(s) & SRF_ERROR){ retval = -EPIPE; }else{ retval = 0; @@ -1495,9 +1494,9 @@ static ssize_t comedi_read(struct file * file,char *buf,size_t nbytes,loff_t *of if(msubdev_flags&SDF_RUNNING)){ + if(!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)){ do_become_nonbusy(dev,s); - if(s->runflags & SRF_ERROR){ + if(comedi_get_subdevice_runflags(s) & SRF_ERROR){ retval = -EPIPE; }else{ retval = 0; @@ -1531,8 +1530,7 @@ static ssize_t comedi_read(struct file * file,char *buf,size_t nbytes,loff_t *of buf+=n; break; /* makes device work like a pipe */ } - if(!(s->subdev_flags&SDF_RUNNING) && - !(s->runflags & SRF_ERROR) && + if(!(comedi_get_subdevice_runflags(s) & (SRF_ERROR | SRF_RUNNING)) && async->buf_read_count - async->buf_write_count == 0) { do_become_nonbusy(dev,s); @@ -1550,11 +1548,11 @@ void do_become_nonbusy(comedi_device *dev,comedi_subdevice *s) { comedi_async *async = s->async; - s->subdev_flags &= ~SDF_RUNNING; + comedi_set_subdevice_runflags(s, SRF_RUNNING, 0); #ifdef CONFIG_COMEDI_RT - if(s->runflags&SRF_RT){ + if(comedi_get_subdevice_runflags(s) & SRF_RT){ comedi_switch_to_non_rt(dev); - s->runflags &= ~SRF_RT; + comedi_set_subdevice_runflags(s, SRF_RT, 0); } #endif if(async){ @@ -1813,29 +1811,26 @@ void comedi_error(const comedi_device *dev,const char *s) rt_printk("comedi%d: %s: %s\n", dev->minor, dev->driver->driver_name, s); } -void comedi_event(comedi_device *dev,comedi_subdevice *s, unsigned int mask) +void comedi_event(comedi_device *dev, comedi_subdevice *s, unsigned useless) { comedi_async *async = s->async; - mask = s->async->events; - s->async->events = 0; - //DPRINTK("comedi_event 0x%x\n",mask); - if( (s->subdev_flags & SDF_RUNNING) == 0) + if((comedi_get_subdevice_runflags(s) & SRF_RUNNING) == 0) return; - if(mask&(COMEDI_CB_EOA|COMEDI_CB_ERROR|COMEDI_CB_OVERFLOW)){ - s->subdev_flags &= ~SDF_RUNNING; + if(s->async->events & (COMEDI_CB_EOA |COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)){ + comedi_set_subdevice_runflags(s, SRF_RUNNING, 0); } /* remember if an error event has occured, so an error * can be returned the next time the user does a read() */ - if(mask & (COMEDI_CB_ERROR|COMEDI_CB_OVERFLOW)){ - s->runflags |= SRF_ERROR; + if(s->async->events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)){ + comedi_set_subdevice_runflags(s, SRF_ERROR, SRF_ERROR); } - if(async->cb_mask&mask){ - if(s->runflags&SRF_USER){ + if(async->cb_mask & s->async->events){ + if(comedi_get_subdevice_runflags(s) & SRF_USER){ if(dev->rt){ #ifdef CONFIG_COMEDI_RT @@ -1858,7 +1853,7 @@ void comedi_event(comedi_device *dev,comedi_subdevice *s, unsigned int mask) } } }else{ - if(async->cb_func)async->cb_func(mask,async->cb_arg); + if(async->cb_func)async->cb_func(s->async->events, async->cb_arg); /* XXX bug here. If subdevice A is rt, and * subdevice B tries to callback to a normal * linux kernel function, it will be at the @@ -1866,4 +1861,26 @@ void comedi_event(comedi_device *dev,comedi_subdevice *s, unsigned int mask) * common, I'm not going to worry about it. */ } } + s->async->events = 0; +} + +void comedi_set_subdevice_runflags(comedi_subdevice *s, unsigned mask, unsigned bits) +{ + unsigned long flags; + + comedi_spin_lock_irqsave(&s->runflags_lock, flags); + s->runflags &= ~mask; + s->runflags |= (bits & mask); + comedi_spin_unlock_irqrestore(&s->runflags_lock, flags); +} + +unsigned comedi_get_subdevice_runflags(comedi_subdevice *s) +{ + unsigned long flags; + unsigned runflags; + + comedi_spin_lock_irqsave(&s->runflags_lock, flags); + runflags = s->runflags; + comedi_spin_unlock_irqrestore(&s->runflags_lock, flags); + return runflags; } diff --git a/comedi/comedi_ksyms.c b/comedi/comedi_ksyms.c index 7eab0db7..f86bc701 100644 --- a/comedi/comedi_ksyms.c +++ b/comedi/comedi_ksyms.c @@ -44,6 +44,8 @@ EXPORT_SYMBOL(comedi_error); //EXPORT_SYMBOL(comedi_eobuf); //EXPORT_SYMBOL(comedi_eos); EXPORT_SYMBOL(comedi_event); +EXPORT_SYMBOL(comedi_get_subdevice_runflags); +EXPORT_SYMBOL(comedi_set_subdevice_runflags); EXPORT_SYMBOL(range_bipolar10); EXPORT_SYMBOL(range_bipolar5); EXPORT_SYMBOL(range_bipolar2_5); diff --git a/comedi/drivers/mite.c b/comedi/drivers/mite.c index 46df1877..d9181251 100644 --- a/comedi/drivers/mite.c +++ b/comedi/drivers/mite.c @@ -276,8 +276,14 @@ void mite_release_channel(struct mite_channel *mite_chan) // spin lock to prevent races with mite_request_channel comedi_spin_lock_irqsave(&mite->lock, flags); - mite->channel_allocated[mite_chan->channel] = 0; - mite_chan->ring = NULL; + if(mite->channel_allocated[mite_chan->channel]) + { + mite_dma_disarm(mite_chan); + mite_dma_reset(mite_chan); + mite->channel_allocated[mite_chan->channel] = 0; + mite_chan->ring = NULL; + mmiowb(); + } comedi_spin_unlock_irqrestore(&mite->lock, flags); } diff --git a/comedi/drivers/ni_660x.c b/comedi/drivers/ni_660x.c index 6c120277..25577ddd 100644 --- a/comedi/drivers/ni_660x.c +++ b/comedi/drivers/ni_660x.c @@ -40,9 +40,7 @@ DAQ 660x Register-Level Programmer Manual (NI 370505A-01) DAQ 6601/6602 User Manual (NI 322137B-01) Things to do: -- Add DMA support (see mite.c and ni_pcidio.c for examples) -- Add commands (copy from ni_pcidio.c ?) -- Add interrupts +- Add commands (see ni_tio.c and ni_mio_common.c) */ #include @@ -589,6 +587,30 @@ static NI_660x_Register ni_gpct_to_660x_register(enum ni_gpct_register reg) case NITIO_G3_DMA_Status_Reg: ni_660x_register = G3DMAStatusRegister; break; + case NITIO_G0_Interrupt_Acknowledge_Reg: + ni_660x_register = G0InterruptAcknowledge; + break; + case NITIO_G1_Interrupt_Acknowledge_Reg: + ni_660x_register = G1InterruptAcknowledge; + break; + case NITIO_G2_Interrupt_Acknowledge_Reg: + ni_660x_register = G2InterruptAcknowledge; + break; + case NITIO_G3_Interrupt_Acknowledge_Reg: + ni_660x_register = G3InterruptAcknowledge; + break; + case NITIO_G0_Status_Reg: + ni_660x_register = G0StatusRegister; + break; + case NITIO_G1_Status_Reg: + ni_660x_register = G0StatusRegister; + break; + case NITIO_G2_Status_Reg: + ni_660x_register = G0StatusRegister; + break; + case NITIO_G3_Status_Reg: + ni_660x_register = G0StatusRegister; + break; default: rt_printk("%s: unhandled register 0x%x in switch.\n", __FUNCTION__, reg); BUG(); @@ -706,8 +728,6 @@ static int ni_660x_attach(comedi_device *dev,comedi_devconfig *it) devpriv->counter_dev->counters[i].chip_index = i / CTRS_PER_CHIP; devpriv->counter_dev->counters[i].counter_index = i % CTRS_PER_CHIP; - devpriv->counter_dev->counters[i].clock_period_ps = 0; - devpriv->counter_dev->counters[i].mite_chan = NULL; }else { s->type = COMEDI_SUBD_UNUSED; diff --git a/comedi/drivers/ni_mio_common.c b/comedi/drivers/ni_mio_common.c index f415c18a..d7d4ba64 100644 --- a/comedi/drivers/ni_mio_common.c +++ b/comedi/drivers/ni_mio_common.c @@ -269,8 +269,7 @@ static int ni_gpct_insn_config(comedi_device *dev,comedi_subdevice *s, static int ni_gpct_cmd(comedi_device *dev,comedi_subdevice *s); static int ni_gpct_cmdtest(comedi_device *dev, comedi_subdevice *s, comedi_cmd *cmd); static int ni_gpct_cancel(comedi_device *dev,comedi_subdevice *s); -static void handle_gpct_interrupt(comedi_device *dev, unsigned short counter_index, - unsigned short is_terminal_count); +static void handle_gpct_interrupt(comedi_device *dev, unsigned short counter_index); static int init_cs5529(comedi_device *dev); static int cs5529_do_conversion(comedi_device *dev, unsigned short *data); @@ -284,8 +283,8 @@ static int ni_6143_pwm_config(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); static int ni_set_master_clock(comedi_device *dev, unsigned source, unsigned period_ns); -static void ack_a_interrupt(comedi_device *dev, unsigned short a_status, unsigned short g_status); -static void ack_b_interrupt(comedi_device *dev, unsigned short b_status, unsigned short g_status); +static void ack_a_interrupt(comedi_device *dev, unsigned short a_status); +static void ack_b_interrupt(comedi_device *dev, unsigned short b_status); enum aimodes { @@ -432,18 +431,20 @@ static int ni_request_ao_mite_channel(comedi_device *dev) static int ni_request_gpct_mite_channel(comedi_device *dev, unsigned gpct_index) { unsigned long flags; + struct mite_channel *mite_chan; BUG_ON(gpct_index >= NUM_GPCT); comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags); BUG_ON(devpriv->counter_dev->counters[gpct_index].mite_chan); - devpriv->counter_dev->counters[gpct_index].mite_chan = mite_request_channel(devpriv->mite, devpriv->gpct_mite_ring[gpct_index]); - if(devpriv->counter_dev->counters[gpct_index].mite_chan == NULL) + mite_chan = mite_request_channel(devpriv->mite, devpriv->gpct_mite_ring[gpct_index]); + if(mite_chan == NULL) { comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); comedi_error(dev, "failed to reserve mite dma channel for counter."); return -EBUSY; } - ni_set_gpct_dma_channel(dev, gpct_index, devpriv->counter_dev->counters[gpct_index].mite_chan->channel); + ni_tio_set_mite_channel(&devpriv->counter_dev->counters[gpct_index], mite_chan); + ni_set_gpct_dma_channel(dev, gpct_index, mite_chan->channel); comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); return 0; } @@ -459,8 +460,6 @@ static void ni_release_ai_mite_channel(comedi_device *dev) if(devpriv->ai_mite_chan) { ni_set_ai_dma_channel(dev, -1); - mite_dma_disarm(devpriv->ai_mite_chan); - mite_dma_reset(devpriv->ai_mite_chan); mite_release_channel(devpriv->ai_mite_chan); devpriv->ai_mite_chan = NULL; } @@ -477,8 +476,6 @@ static void ni_release_ao_mite_channel(comedi_device *dev) if(devpriv->ao_mite_chan) { ni_set_ao_dma_channel(dev, -1); - mite_dma_disarm(devpriv->ao_mite_chan); - mite_dma_reset(devpriv->ao_mite_chan); mite_release_channel(devpriv->ao_mite_chan); devpriv->ao_mite_chan = NULL; } @@ -496,10 +493,8 @@ void ni_release_gpct_mite_channel(comedi_device *dev, unsigned gpct_index) if(devpriv->counter_dev->counters[gpct_index].mite_chan) { ni_set_gpct_dma_channel(dev, gpct_index, -1); - mite_dma_disarm(devpriv->counter_dev->counters[gpct_index].mite_chan); - mite_dma_reset(devpriv->counter_dev->counters[gpct_index].mite_chan); mite_release_channel(devpriv->counter_dev->counters[gpct_index].mite_chan); - devpriv->counter_dev->counters[gpct_index].mite_chan = NULL; + ni_tio_set_mite_channel(&devpriv->counter_dev->counters[gpct_index], NULL); } comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); #endif // PCIDMA @@ -658,7 +653,6 @@ static irqreturn_t ni_E_interrupt(int irq, void *d PT_REGS_ARG) unsigned short b_status; unsigned int ai_mite_status = 0; unsigned int ao_mite_status = 0; - unsigned g_status; unsigned long flags; struct mite_struct *mite = devpriv->mite; @@ -675,20 +669,27 @@ static irqreturn_t ni_E_interrupt(int irq, void *d PT_REGS_ARG) comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags_too); if(devpriv->ai_mite_chan) + { ai_mite_status = mite_get_status(devpriv->ai_mite_chan); + if(ai_mite_status & CHSR_LINKC) + writel(CHOR_CLRLC, devpriv->mite->mite_io_addr + MITE_CHOR(devpriv->ai_mite_chan->channel)); + } if(devpriv->ao_mite_chan) + { ao_mite_status = mite_get_status(devpriv->ao_mite_chan); + if(ao_mite_status & CHSR_LINKC) + writel(CHOR_CLRLC, mite->mite_io_addr + MITE_CHOR(devpriv->ao_mite_chan->channel)); + } comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags_too); } - g_status = devpriv->stc_readw(dev, G_Status_Register); - ack_a_interrupt(dev, a_status, g_status); - ack_b_interrupt(dev, b_status, g_status); + ack_a_interrupt(dev, a_status); + ack_b_interrupt(dev, b_status); if((a_status & Interrupt_A_St) || (ai_mite_status & CHSR_INT)) handle_a_interrupt(dev, a_status, ai_mite_status); if((b_status & Interrupt_B_St) || (ao_mite_status & CHSR_INT)) handle_b_interrupt(dev, b_status, ao_mite_status); - handle_gpct_interrupt(dev, 0, (a_status & G0_TC_St)); - handle_gpct_interrupt(dev, 1, (b_status & G1_TC_St)); + handle_gpct_interrupt(dev, 0); + handle_gpct_interrupt(dev, 1); comedi_spin_unlock_irqrestore(&dev->spinlock, flags); return IRQ_HANDLED; @@ -709,26 +710,24 @@ static void ni_sync_ai_dma(comedi_device *dev) static void mite_handle_b_linkc(struct mite_struct *mite, comedi_device *dev) { comedi_subdevice *s = dev->subdevices + NI_AO_SUBDEV; + unsigned long flags; - if(devpriv->ao_mite_chan == NULL) return; - writel(CHOR_CLRLC, mite->mite_io_addr + MITE_CHOR(devpriv->ao_mite_chan->channel)); - - if(mite_sync_output_dma(devpriv->ao_mite_chan, s->async) < 0) + comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags); + if(devpriv->ao_mite_chan) { - s->async->events |= COMEDI_CB_ERROR; - return; + if(mite_sync_output_dma(devpriv->ao_mite_chan, s->async) < 0) + { + s->async->events |= COMEDI_CB_ERROR; + return; + } } + comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); } -// #define DEBUG_DMA_TIMING static int ni_ao_wait_for_dma_load( comedi_device *dev ) { static const int timeout = 10000; int i; -#ifdef DEBUG_DMA_TIMING - struct timeval start; - do_gettimeofday(&start); -#endif for(i = 0; i < timeout; i++) { unsigned short b_status; @@ -740,23 +739,6 @@ static int ni_ao_wait_for_dma_load( comedi_device *dev ) to slow the dma transfer down */ comedi_udelay(10); } -#ifdef DEBUG_DMA_TIMING - rt_printk("looped %i times waiting for ao fifo load.\n", i); - struct timeval now; - do_gettimeofday(&now); - unsigned elapsed_usec = 1000000 * (now.tv_sec - start.tv_sec) + now.tv_usec - start.tv_usec; - rt_printk("total elapsed usec=%i\n", elapsed_usec); - do_gettimeofday(&start); - unsigned b_status; - for(i = 0; i < 100; ++i) - { -// devpriv->stc_writew(dev, devpriv->ao_mode3, AO_Mode_3_Register); - b_status = devpriv->stc_readw(dev, AO_Status_1_Register); - } - do_gettimeofday(&now); - elapsed_usec = 1000000 * (now.tv_sec - start.tv_sec) + now.tv_usec - start.tv_usec; - rt_printk("usec to do 100 word xfers=%i\n", elapsed_usec); -#endif if( i == timeout ) { comedi_error(dev, "timed out waiting for dma load"); @@ -828,59 +810,16 @@ static void ni_event(comedi_device *dev, comedi_subdevice *s, unsigned events) comedi_event(dev, s, events); } -static void handle_gpct_interrupt(comedi_device *dev, unsigned short counter_index, unsigned short is_terminal_count) +static void handle_gpct_interrupt(comedi_device *dev, unsigned short counter_index) { - unsigned gpct_mite_status; - unsigned long flags; - struct mite_channel *mite_chan; comedi_subdevice *s = dev->subdevices + NI_GPCT_SUBDEV(counter_index); - comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags); - mite_chan = devpriv->counter_dev->counters[counter_index].mite_chan; - if(mite_chan == NULL) - { - comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); - return; - } - gpct_mite_status = mite_get_status(mite_chan); - if(gpct_mite_status & CHSR_LINKC) - { - writel(CHOR_CLRLC, devpriv->mite->mite_io_addr + MITE_CHOR(mite_chan->channel)); - } - mite_sync_input_dma(mite_chan, s->async); - - comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); + ni_tio_handle_interrupt(&devpriv->counter_dev->counters[counter_index], s); if(s->async->events) ni_event(dev, s, s->async->events); } - /* During buffered input counter operation for e-series, the gate interrupt is acked - automatically by the dma controller, due to the Gi_Read/Write_Acknowledges_IRQ bits - in the input select register. */ -int should_ack_gate(comedi_device *dev, unsigned counter_index) -{ - unsigned long flags; - int retval = 0; - - if(boardtype.reg_type & ni_reg_m_series_mask) return 1; - - comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags); - { - struct mite_channel *mite_chan = devpriv->counter_dev->counters[counter_index].mite_chan; - - if(mite_chan == NULL || - mite_chan->dir != COMEDI_INPUT || - (mite_done(devpriv->counter_dev->counters[counter_index].mite_chan))) - { - retval = 1; - } - } - comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); - - return retval; -} - -static void ack_a_interrupt(comedi_device *dev, unsigned short a_status, unsigned short g_status) +static void ack_a_interrupt(comedi_device *dev, unsigned short a_status) { unsigned short ack = 0; @@ -901,19 +840,6 @@ static void ack_a_interrupt(comedi_device *dev, unsigned short a_status, unsigne /* not sure why we used to ack the START here also, instead of doing it independently. Frank Hess 2007-07-06 */ ack |= AI_STOP_Interrupt_Ack /*| AI_START_Interrupt_Ack*/; } - if(a_status & G0_TC_St) - { - ack |= G0_TC_Interrupt_Ack; - } - if(a_status & G0_Gate_Interrupt_St) - { - if(should_ack_gate(dev, 0)) - ack |= G0_Gate_Interrupt_Ack; - } - if(g_status & G0_Gate_Error_St) - { - ack |= G0_Gate_Error_Confirm; - } if(ack) devpriv->stc_writew(dev, ack, Interrupt_A_Ack_Register); } @@ -931,9 +857,7 @@ static void handle_a_interrupt(comedi_device *dev, unsigned short status, ni_mio_print_status_a(status); #endif #ifdef PCIDMA - /* Currently, mite.c requires us to handle LINKC */ if(ai_mite_status & CHSR_LINKC){ - writel(CHOR_CLRLC, devpriv->mite->mite_io_addr + MITE_CHOR(devpriv->ai_mite_chan->channel)); ni_sync_ai_dma(dev); } @@ -951,7 +875,7 @@ static void handle_a_interrupt(comedi_device *dev, unsigned short status, rt_printk("ni_mio_common: a_status=0xffff. Card removed?\n"); /* we probably aren't even running a command now, * so it's a good idea to be careful. */ - if(s->subdev_flags&SDF_RUNNING){ + if(comedi_get_subdevice_runflags(s) & SRF_RUNNING){ s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; ni_event(dev, s, s->async->events); } @@ -1010,7 +934,7 @@ static void handle_a_interrupt(comedi_device *dev, unsigned short status, #endif } -static void ack_b_interrupt(comedi_device *dev, unsigned short b_status, unsigned short g_status) +static void ack_b_interrupt(comedi_device *dev, unsigned short b_status) { unsigned short ack = 0; if(b_status & AO_BC_TC_St) @@ -1041,19 +965,6 @@ static void ack_b_interrupt(comedi_device *dev, unsigned short b_status, unsigne { ack |= AO_UPDATE_Interrupt_Ack; } - if(b_status & G1_Gate_Interrupt_St) - { - if(should_ack_gate(dev, 1)) - ack |= G1_Gate_Interrupt_Ack; - } - if(g_status & G1_Gate_Error_St) - { - ack |= G1_Gate_Error_Confirm; - } - if(b_status & G1_TC_St) - { - ack |= G1_TC_Interrupt_Ack; - } if(ack) devpriv->stc_writew(dev, ack, Interrupt_B_Ack_Register); } @@ -1330,26 +1241,32 @@ static int ni_ai_drain_dma(comedi_device *dev ) { int i; static const int timeout = 10000; + unsigned long flags; + int retval = 0; - if(devpriv->ai_mite_chan == NULL) return 0; - for( i = 0; i < timeout; i++ ) - { - if((devpriv->stc_readw(dev, AI_Status_1_Register) & AI_FIFO_Empty_St) && - mite_bytes_in_transit(devpriv->ai_mite_chan) == 0) - break; - comedi_udelay(2); - } - if(i == timeout) + comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags); + if(devpriv->ai_mite_chan) { - rt_printk("ni_mio_common: wait for dma drain timed out\n"); - rt_printk("mite_bytes_in_transit=%i, AI_Status1_Register=0x%x\n", - mite_bytes_in_transit(devpriv->ai_mite_chan), devpriv->stc_readw(dev, AI_Status_1_Register)); - return -1; + for(i = 0; i < timeout; i++) + { + if((devpriv->stc_readw(dev, AI_Status_1_Register) & AI_FIFO_Empty_St) && + mite_bytes_in_transit(devpriv->ai_mite_chan) == 0) + break; + comedi_udelay(5); + } + if(i == timeout) + { + rt_printk("ni_mio_common: wait for dma drain timed out\n"); + rt_printk("mite_bytes_in_transit=%i, AI_Status1_Register=0x%x\n", + mite_bytes_in_transit(devpriv->ai_mite_chan), devpriv->stc_readw(dev, AI_Status_1_Register)); + retval = -1; + } } + comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); ni_sync_ai_dma(dev); - return 0; + return retval; } #endif /* @@ -1504,14 +1421,15 @@ static int ni_ao_setup_MITE_dma(comedi_device *dev) { comedi_subdevice *s = dev->subdevices + NI_AO_SUBDEV; int retval; + unsigned long flags; retval = ni_request_ao_mite_channel(dev); if(retval) return retval; - //rt_printk("comedi_debug: using mite channel %i for ao.\n", devpriv->ao_mite_chan->channel); /* read alloc the entire buffer */ comedi_buf_read_alloc(s->async, s->async->prealloc_bufsz); + comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags); devpriv->ao_mite_chan->dir = COMEDI_OUTPUT; if(boardtype.reg_type & (ni_reg_611x | ni_reg_6713)) { @@ -1522,8 +1440,9 @@ static int ni_ao_setup_MITE_dma(comedi_device *dev) makes the mite do 32 bit pci transfers, doubling pci bandwidth. */ mite_prep_dma(devpriv->ao_mite_chan, 16, 32); } - /*start the MITE*/ mite_dma_arm(devpriv->ao_mite_chan); + comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); + return 0; } @@ -3516,6 +3435,18 @@ static unsigned ni_gpct_to_stc_register(enum ni_gpct_register reg) case NITIO_G01_Joint_Status2_Reg: stc_register = Joint_Status_2_Register; break; + case NITIO_G0_Interrupt_Acknowledge_Reg: + stc_register = Interrupt_A_Ack_Register; + break; + case NITIO_G1_Interrupt_Acknowledge_Reg: + stc_register = Interrupt_B_Ack_Register; + break; + case NITIO_G0_Status_Reg: + stc_register = AI_Status_1_Register; + break; + case NITIO_G1_Status_Reg: + stc_register = AO_Status_1_Register; + break; default: rt_printk("%s: unhandled register 0x%x in switch.\n", __FUNCTION__, reg); BUG(); @@ -3878,8 +3809,6 @@ static int ni_E_init(comedi_device *dev,comedi_devconfig *it) devpriv->counter_dev->counters[j].chip_index = 0; devpriv->counter_dev->counters[j].counter_index = j; - devpriv->counter_dev->counters[j].clock_period_ps = 0; - devpriv->counter_dev->counters[j].mite_chan = NULL; ni_tio_init_counter(&devpriv->counter_dev->counters[j]); } @@ -4524,7 +4453,6 @@ static int ni_gpct_cmd(comedi_device *dev, comedi_subdevice *s) { int retval; #ifdef PCIDMA - unsigned long flags; struct ni_gpct *counter = s->private; const comedi_cmd *cmd = &s->async->cmd; @@ -4544,10 +4472,7 @@ static int ni_gpct_cmd(comedi_device *dev, comedi_subdevice *s) Gi_Gate_Interrupt_Enable_Bit(counter->counter_index), 1); } ni_e_series_enable_second_irq(dev, counter->counter_index, 1); - - comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags); retval = ni_tio_cmd(counter, s->async); - comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); #else retval = -ENOTSUPP; #endif @@ -4564,13 +4489,9 @@ static int ni_gpct_cmdtest(comedi_device *dev, comedi_subdevice *s, comedi_cmd * static int ni_gpct_cancel(comedi_device *dev, comedi_subdevice *s) { struct ni_gpct *counter = s->private; - unsigned long flags; int retval; - comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags); retval = ni_tio_cancel(counter); - comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags); - ni_e_series_enable_second_irq(dev, counter->counter_index, 0); ni_set_bits(dev, Gi_Interrupt_Enable_Register(counter->counter_index), Gi_Gate_Interrupt_Enable_Bit(counter->counter_index), 0); diff --git a/comedi/drivers/ni_tio.c b/comedi/drivers/ni_tio.c index 4da555e8..2ec6a341 100644 --- a/comedi/drivers/ni_tio.c +++ b/comedi/drivers/ni_tio.c @@ -362,6 +362,52 @@ static inline enum ni_gpct_register NITIO_Gi_ABZ_Reg(int counter_index) return 0; } +static inline enum ni_gpct_register NITIO_Gi_Interrupt_Acknowledge_Reg(int counter_index) +{ + switch(counter_index) + { + case 0: + return NITIO_G0_Interrupt_Acknowledge_Reg; + break; + case 1: + return NITIO_G1_Interrupt_Acknowledge_Reg; + break; + case 2: + return NITIO_G2_Interrupt_Acknowledge_Reg; + break; + case 3: + return NITIO_G3_Interrupt_Acknowledge_Reg; + break; + default: + BUG(); + break; + } + return 0; +} + +static inline enum ni_gpct_register NITIO_Gi_Status_Reg(int counter_index) +{ + switch(counter_index) + { + case 0: + return NITIO_G0_Status_Reg; + break; + case 1: + return NITIO_G1_Status_Reg; + break; + case 2: + return NITIO_G2_Status_Reg; + break; + case 3: + return NITIO_G3_Status_Reg; + break; + default: + BUG(); + break; + } + return 0; +} + enum Gi_Auto_Increment_Reg_Bits { Gi_Auto_Increment_Mask = 0xff @@ -700,7 +746,9 @@ enum Gxx_Status_Bits G0_No_Load_Between_Gates_Bit = 0x400, G1_No_Load_Between_Gates_Bit = 0x800, G0_TC_Error_Bit = 0x1000, - G1_TC_Error_Bit = 0x2000 + G1_TC_Error_Bit = 0x2000, + G0_Gate_Error_Bit = 0x4000, + G1_Gate_Error_Bit = 0x8000 }; static inline unsigned Gi_Counting_Bit(unsigned counter_index) { @@ -717,6 +765,16 @@ static inline unsigned Gi_Next_Load_Source_Bit(unsigned counter_index) if(counter_index % 2) return G1_Next_Load_Source_Bit; return G0_Next_Load_Source_Bit; } +static inline unsigned Gi_TC_Error_Bit(unsigned counter_index) +{ + if(counter_index % 2) return G1_TC_Error_Bit; + return G0_TC_Error_Bit; +} +static inline unsigned Gi_Gate_Error_Bit(unsigned counter_index) +{ + if(counter_index % 2) return G1_Gate_Error_Bit; + return G0_Gate_Error_Bit; +} /* joint reset register bits */ static inline unsigned Gi_Reset_Bit(unsigned counter_index) @@ -731,6 +789,49 @@ enum Gi_DMA_Config_Reg_Bits Gi_DMA_Int_Bit = 0x4 }; +enum Gi_DMA_Status_Reg_Bits +{ + Gi_DMA_Readbank_Bit = 0x2000, + Gi_DRQ_Error_Bit = 0x4000, + Gi_DRQ_Status_Bit = 0x8000 +}; + +enum G02_Interrupt_Acknowledge_Bits +{ + G0_Gate_Error_Confirm_Bit = 0x20, + G0_TC_Error_Confirm_Bit = 0x40 +}; +enum G13_Interrupt_Acknowledge_Bits +{ + G1_Gate_Error_Confirm_Bit = 0x2, + G1_TC_Error_Confirm_Bit = 0x4 +}; +static inline unsigned Gi_Gate_Error_Confirm_Bit(unsigned counter_index) +{ + if(counter_index % 2) + return G1_Gate_Error_Confirm_Bit; + return G0_Gate_Error_Confirm_Bit; +} +static inline unsigned Gi_TC_Error_Confirm_Bit(unsigned counter_index) +{ + if(counter_index % 2) + return G1_TC_Error_Confirm_Bit; + return G0_TC_Error_Confirm_Bit; +} +// bits that are the same in G0/G2 and G1/G3 interrupt acknowledge registers +enum Gxx_Interrupt_Acknowledge_Bits +{ + Gi_TC_Interrupt_Ack_Bit = 0x4000, + Gi_Gate_Interrupt_Ack_Bit = 0x8000 +}; + +enum Gi_Status_Bits +{ + Gi_Gate_Interrupt_Bit = 0x4, + Gi_TC_Bit = 0x8, + Gi_Interrupt_Bit = 0x8000 +}; + static const lsampl_t counter_status_mask = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING; static int __init ni_tio_init_module(void) @@ -767,6 +868,7 @@ struct ni_gpct_device* ni_gpct_device_construct(comedi_device *dev, for(i = 0; i < num_counters; ++i) { counter_dev->counters[i].counter_dev = counter_dev; + spin_lock_init(&counter_dev->counters[i].lock); } counter_dev->num_counters = num_counters; return counter_dev; @@ -2225,7 +2327,9 @@ int ni_tio_cmd(struct ni_gpct *counter, comedi_async *async) { comedi_cmd *cmd = &async->cmd; int retval; + unsigned long flags; + comedi_spin_lock_irqsave(&counter->lock, flags); if(counter->mite_chan == NULL) { rt_printk("ni_tio: commands only supported with DMA. Interrupt-driven commands not yet implemented.\n"); @@ -2240,6 +2344,7 @@ int ni_tio_cmd(struct ni_gpct *counter, comedi_async *async) retval = ni_tio_input_cmd(counter, async); } } + comedi_spin_unlock_irqrestore(&counter->lock, flags); return retval; } @@ -2250,15 +2355,127 @@ int ni_tio_cmdtest(struct ni_gpct *counter) int ni_tio_cancel(struct ni_gpct *counter) { + unsigned long flags; + ni_tio_arm(counter, 0, 0); + comedi_spin_lock_irqsave(&counter->lock, flags); if(counter->mite_chan) { mite_dma_disarm(counter->mite_chan); } + comedi_spin_unlock_irqrestore(&counter->lock, flags); ni_tio_configure_dma(counter, 0, 0); return 0; } + /* During buffered input counter operation for e-series, the gate interrupt is acked + automatically by the dma controller, due to the Gi_Read/Write_Acknowledges_IRQ bits + in the input select register. */ +static int should_ack_gate(struct ni_gpct *counter) +{ + unsigned long flags; + int retval = 0; + + switch(counter->counter_dev->variant) + { + case ni_gpct_variant_m_series: + case ni_gpct_variant_660x: // not sure if 660x really supports gate interrupts (the bits are not listed in register-level manual) + return 1; + break; + case ni_gpct_variant_e_series: + comedi_spin_lock_irqsave(&counter->lock, flags); + { + if(counter->mite_chan == NULL || + counter->mite_chan->dir != COMEDI_INPUT || + (mite_done(counter->mite_chan))) + { + retval = 1; + } + } + comedi_spin_unlock_irqrestore(&counter->lock, flags); + break; + } + return retval; +} + +static unsigned acknowledge_and_confirm(struct ni_gpct *counter, int *gate_error, int *tc_error) +{ + const unsigned short gxx_status = counter->counter_dev->read_register(counter, NITIO_Gxx_Status_Reg(counter->counter_index)); + const unsigned short gi_status = counter->counter_dev->read_register(counter, NITIO_Gi_Status_Reg(counter->counter_index)); + unsigned ack = 0; + + if(gate_error) *gate_error = 0; + if(tc_error) *tc_error = 0; + + if(gxx_status & Gi_Gate_Error_Bit(counter->counter_index)) + { + ack |= Gi_Gate_Error_Confirm_Bit(counter->counter_index); + if(gate_error) *gate_error = 1; + } + if(gxx_status & Gi_TC_Error_Bit(counter->counter_index)) + { + ack |= Gi_TC_Error_Confirm_Bit(counter->counter_index); + if(tc_error) *tc_error = 1; + } + if(gi_status & Gi_TC_Bit) + { + ack |= Gi_TC_Interrupt_Ack_Bit; + } + if(gi_status & Gi_Gate_Interrupt_Bit) + { + if(should_ack_gate(counter)) + ack |= Gi_Gate_Interrupt_Ack_Bit; + } + if(ack) counter->counter_dev->write_register(counter, ack, NITIO_Gi_Interrupt_Acknowledge_Reg(counter->counter_index)); + return gxx_status; +} + +void ni_tio_handle_interrupt(struct ni_gpct *counter, comedi_subdevice *s) +{ + unsigned gpct_mite_status; + unsigned long flags; + int gate_error; + int tc_error; + + acknowledge_and_confirm(counter, &gate_error, &tc_error); + if(gate_error) + s->async->events |= COMEDI_CB_OVERFLOW; + switch(counter->counter_dev->variant) + { + case ni_gpct_variant_m_series: + case ni_gpct_variant_660x: + if(counter->counter_dev->read_register(counter, NITIO_Gi_DMA_Status_Reg(counter->counter_index)) & + Gi_DRQ_Error_Bit) + s->async->events |= COMEDI_CB_OVERFLOW; + break; + case ni_gpct_variant_e_series: + break; + } + comedi_spin_lock_irqsave(&counter->lock, flags); + if(counter->mite_chan == NULL) + { + comedi_spin_unlock_irqrestore(&counter->lock, flags); + return; + } + gpct_mite_status = mite_get_status(counter->mite_chan); + if(gpct_mite_status & CHSR_LINKC) + { + writel(CHOR_CLRLC, counter->mite_chan->mite->mite_io_addr + MITE_CHOR(counter->mite_chan->channel)); + } + mite_sync_input_dma(counter->mite_chan, s->async); + + comedi_spin_unlock_irqrestore(&counter->lock, flags); +} + +void ni_tio_set_mite_channel(struct ni_gpct *counter, struct mite_channel *mite_chan) +{ + unsigned long flags; + + comedi_spin_lock_irqsave(&counter->lock, flags); + counter->mite_chan = mite_chan; + comedi_spin_unlock_irqrestore(&counter->lock, flags); +} + EXPORT_SYMBOL_GPL(ni_tio_rinsn); EXPORT_SYMBOL_GPL(ni_tio_winsn); EXPORT_SYMBOL_GPL(ni_tio_cmd); @@ -2268,3 +2485,5 @@ EXPORT_SYMBOL_GPL(ni_tio_insn_config); EXPORT_SYMBOL_GPL(ni_tio_init_counter); EXPORT_SYMBOL_GPL(ni_gpct_device_construct); EXPORT_SYMBOL_GPL(ni_gpct_device_destroy); +EXPORT_SYMBOL_GPL(ni_tio_handle_interrupt); +EXPORT_SYMBOL_GPL(ni_tio_set_mite_channel); diff --git a/comedi/drivers/ni_tio.h b/comedi/drivers/ni_tio.h index 6145558a..2701f813 100644 --- a/comedi/drivers/ni_tio.h +++ b/comedi/drivers/ni_tio.h @@ -89,6 +89,14 @@ enum ni_gpct_register NITIO_G3_DMA_Status_Reg, NITIO_G0_ABZ_Reg, NITIO_G1_ABZ_Reg, + NITIO_G0_Interrupt_Acknowledge_Reg, + NITIO_G1_Interrupt_Acknowledge_Reg, + NITIO_G2_Interrupt_Acknowledge_Reg, + NITIO_G3_Interrupt_Acknowledge_Reg, + NITIO_G0_Status_Reg, + NITIO_G1_Status_Reg, + NITIO_G2_Status_Reg, + NITIO_G3_Status_Reg, NITIO_Num_Registers, }; @@ -108,6 +116,7 @@ struct ni_gpct unsigned chip_index; uint64_t clock_period_ps; /* clock period in picoseconds */ struct mite_channel *mite_chan; + spinlock_t lock; }; struct ni_gpct_device @@ -139,6 +148,8 @@ extern int ni_tio_winsn(struct ni_gpct *counter, extern int ni_tio_cmd(struct ni_gpct *counter, comedi_async *async); extern int ni_tio_cmdtest(struct ni_gpct *counter); extern int ni_tio_cancel(struct ni_gpct *counter); +extern void ni_tio_handle_interrupt(struct ni_gpct *counter, comedi_subdevice *s); +extern void ni_tio_set_mite_channel(struct ni_gpct *counter, struct mite_channel *mite_chan); #endif /* _COMEDI_NI_TIO_H */ diff --git a/include/linux/comedidev.h b/include/linux/comedidev.h index 4b7b4177..7d87a592 100644 --- a/include/linux/comedidev.h +++ b/include/linux/comedidev.h @@ -108,7 +108,8 @@ struct comedi_subdevice_struct{ void *lock; void *busy; - volatile unsigned int runflags; + unsigned runflags; + spinlock_t runflags_lock; int io_bits; @@ -362,10 +363,14 @@ static inline void comedi_proc_cleanup(void) {} #endif /* subdevice runflags */ -#define SRF_USER 0x00000001 -#define SRF_RT 0x00000002 -/* indicates an COMEDI_CB_ERROR event has occurred since the last command was started */ -#define SRF_ERROR 0x00000004 +enum subdevice_runflags +{ + SRF_USER = 0x00000001, + SRF_RT = 0x00000002, + /* indicates an COMEDI_CB_ERROR event has occurred since the last command was started */ + SRF_ERROR = 0x00000004, + SRF_RUNNING = 0x08000000 +}; /* various internal comedi functions @@ -373,6 +378,8 @@ static inline void comedi_proc_cleanup(void) {} int do_rangeinfo_ioctl(comedi_device *dev,comedi_rangeinfo *arg); int check_chanlist(comedi_subdevice *s,int n,unsigned int *chanlist); +void comedi_set_subdevice_runflags(comedi_subdevice *s, unsigned mask, unsigned bits); +unsigned comedi_get_subdevice_runflags(comedi_subdevice *s); /* range stuff */ @@ -422,6 +429,7 @@ static inline int alloc_subdevices(comedi_device *dev, unsigned int num_subdevic { dev->subdevices[i].device = dev; dev->subdevices[i].async_dma_dir = DMA_NONE; + spin_lock_init(&dev->subdevices[i].runflags_lock); } return 0; }