Added some ack'ing of b interrupts, and do acks before handling
[comedi.git] / comedi / drivers / ni_mio_common.c
index 5e75ac9051aab4f28214627312217f1b411c93ed..0bab766abd9250b0eb5a673556e3f17d5c6c920c 100644 (file)
@@ -260,8 +260,6 @@ static int ni_ao_reset(comedi_device *dev,comedi_subdevice *s);
 
 static int ni_8255_callback(int dir,int port,int data,unsigned long arg);
 
-static int ni_ns_to_timer(comedi_device *dev, int *nanosec, int round_mode);
-
 static int ni_gpct_insn_write(comedi_device *dev,comedi_subdevice *s,
        comedi_insn *insn,lsampl_t *data);
 static int ni_gpct_insn_read(comedi_device *dev,comedi_subdevice *s,
@@ -300,18 +298,179 @@ enum aimodes
 
 static const int num_adc_stages_611x = 3;
 
-static void handle_a_interrupt(comedi_device *dev,unsigned short status,
-       unsigned int m_status);
-static void handle_b_interrupt(comedi_device *dev,unsigned short status,
-       unsigned int m_status);
+static void handle_a_interrupt(comedi_device *dev, unsigned short status,
+       unsigned ai_mite_status, unsigned gpct0_mite_status);
+static void handle_b_interrupt(comedi_device *dev, unsigned short status,
+       unsigned ao_mite_status, unsigned gpct1_mite_status);
 static void get_last_sample_611x( comedi_device *dev );
 static void get_last_sample_6143( comedi_device *dev );
 #ifdef PCIDMA
 //static void mite_handle_interrupt(comedi_device *dev,unsigned int status);
 static int ni_ai_drain_dma(comedi_device *dev );
-#endif
 
-static void ni_flush_ai_fifo(comedi_device *dev){
+/* DMA channel setup */
+
+// negative channel means no channel
+static inline void ni_set_ai_dma_channel(comedi_device *dev, int channel)
+{
+       unsigned long flags;
+
+       comedi_spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
+       devpriv->ai_ao_select_reg &= ~AI_DMA_Select_Mask;
+       if(channel >= 0)
+       {
+               devpriv->ai_ao_select_reg |= (ni_stc_dma_channel_select_bitfield(channel) << AI_DMA_Select_Shift) & AI_DMA_Select_Mask;
+       }
+       ni_writeb(devpriv->ai_ao_select_reg, AI_AO_Select);
+       mmiowb();
+       comedi_spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
+}
+
+// negative channel means no channel
+static inline void ni_set_ao_dma_channel(comedi_device *dev, int channel)
+{
+       unsigned long flags;
+
+       comedi_spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
+       devpriv->ai_ao_select_reg &= ~AO_DMA_Select_Mask;
+       if(channel >= 0)
+       {
+               devpriv->ai_ao_select_reg |= (ni_stc_dma_channel_select_bitfield(channel) << AO_DMA_Select_Shift) & AO_DMA_Select_Mask;
+       }
+       ni_writeb(devpriv->ai_ao_select_reg, AI_AO_Select);
+       mmiowb();
+       comedi_spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
+}
+
+// negative mite_channel means no channel
+static inline void ni_set_gpct_dma_channel(comedi_device *dev, unsigned gpct_index, int mite_channel)
+{
+       unsigned long flags;
+
+       comedi_spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
+       devpriv->g0_g1_select_reg &= ~GPCT_DMA_Select_Mask(gpct_index);
+       if(mite_channel >= 0)
+       {
+               devpriv->g0_g1_select_reg |= GPCT_DMA_Select_Bits(gpct_index, mite_channel);
+       }
+       ni_writeb(devpriv->g0_g1_select_reg, G0_G1_Select);
+       mmiowb();
+       comedi_spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
+}
+
+static int ni_request_ai_mite_channel(comedi_device *dev)
+{
+       unsigned long flags;
+
+       comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+       BUG_ON(devpriv->ai_mite_chan);
+       devpriv->ai_mite_chan = mite_request_channel(devpriv->mite, devpriv->ai_mite_ring);
+       if(devpriv->ai_mite_chan == NULL)
+       {
+               comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+               comedi_error(dev, "failed to reserve mite dma channel for analog input.");
+               return -EBUSY;
+       }
+       ni_set_ai_dma_channel(dev, devpriv->ai_mite_chan->channel);
+       comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+       return 0;
+}
+
+static int ni_request_ao_mite_channel(comedi_device *dev)
+{
+       unsigned long flags;
+
+       comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+       BUG_ON(devpriv->ao_mite_chan);
+       devpriv->ao_mite_chan = mite_request_channel(devpriv->mite, devpriv->ao_mite_ring);
+       if(devpriv->ao_mite_chan == NULL)
+       {
+               comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+               comedi_error(dev, "failed to reserve mite dma channel for analog outut.");
+               return -EBUSY;
+       }
+       ni_set_ao_dma_channel(dev, devpriv->ao_mite_chan->channel);
+       comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+       return 0;
+}
+
+static int ni_request_gpct_mite_channel(comedi_device *dev, unsigned gpct_index)
+{
+       unsigned long flags;
+
+       BUG_ON(gpct_index >= NUM_GPCT);
+       comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+       BUG_ON(devpriv->gpct_mite_chan[gpct_index]);
+       devpriv->gpct_mite_chan[gpct_index] = mite_request_channel(devpriv->mite, devpriv->gpct_mite_ring[gpct_index]);
+       if(devpriv->gpct_mite_chan[gpct_index] == 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->gpct_mite_chan[gpct_index]->channel);
+       comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+       return 0;
+}
+
+#endif // PCIDMA
+
+static void ni_release_ai_mite_channel(comedi_device *dev)
+{
+#ifdef PCIDMA
+       unsigned long flags;
+
+       comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+       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;
+       }
+       comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+#endif // PCIDMA
+}
+
+static void ni_release_ao_mite_channel(comedi_device *dev)
+{
+#ifdef PCIDMA
+       unsigned long flags;
+
+       comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+       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;
+       }
+       comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+#endif // PCIDMA
+}
+
+void ni_release_gpct_mite_channel(comedi_device *dev, unsigned gpct_index)
+{
+#ifdef PCIDMA
+       unsigned long flags;
+
+       BUG_ON(gpct_index >= NUM_GPCT);
+       comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+       if(devpriv->gpct_mite_chan[gpct_index])
+       {
+               ni_set_gpct_dma_channel(dev, gpct_index, -1);
+               mite_dma_disarm(devpriv->gpct_mite_chan[gpct_index]);
+               mite_dma_reset(devpriv->gpct_mite_chan[gpct_index]);
+               mite_release_channel(devpriv->gpct_mite_chan[gpct_index]);
+               devpriv->gpct_mite_chan[gpct_index] = NULL;
+       }
+       comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+#endif // PCIDMA
+}
+
+static void ni_clear_ai_fifo(comedi_device *dev){
        if(boardtype.reg_type == ni_reg_6143){
                // Flush the 6143 data FIFO
                ni_writel(0x10, AIFIFO_Control_6143);           // Flush fifo
@@ -323,9 +482,13 @@ static void ni_flush_ai_fifo(comedi_device *dev){
                {
                        ni_writeb(0, M_Offset_Static_AI_Control(0));
                        ni_writeb(1, M_Offset_Static_AI_Control(0));
+#if 0
+               /* the NI example code does 3 convert pulses for 625x boards,
+               but that appears to be wrong in practice. */
                        devpriv->stc_writew(dev, AI_CONVERT_Pulse, AI_Command_1_Register);
                        devpriv->stc_writew(dev, AI_CONVERT_Pulse, AI_Command_1_Register);
                        devpriv->stc_writew(dev, AI_CONVERT_Pulse, AI_Command_1_Register);
+#endif
                }
        }
 }
@@ -391,14 +554,13 @@ static inline void ni_set_bits(comedi_device *dev, int reg, int bits, int value)
 {
        unsigned long flags;
 
-       comedi_spin_lock_irqsave( &devpriv->window_lock, flags );
+       comedi_spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
        switch (reg){
                case Interrupt_A_Enable_Register:
                        if(value)
                                devpriv->int_a_enable_reg |= bits;
                        else
                                devpriv->int_a_enable_reg &= ~bits;
-                       comedi_spin_unlock_irqrestore( &devpriv->window_lock, flags );
                        devpriv->stc_writew(dev, devpriv->int_a_enable_reg,Interrupt_A_Enable_Register);
                        break;
                case Interrupt_B_Enable_Register:
@@ -406,7 +568,6 @@ static inline void ni_set_bits(comedi_device *dev, int reg, int bits, int value)
                                devpriv->int_b_enable_reg |= bits;
                        else
                                devpriv->int_b_enable_reg &= ~bits;
-                       comedi_spin_unlock_irqrestore( &devpriv->window_lock, flags );
                        devpriv->stc_writew(dev, devpriv->int_b_enable_reg,Interrupt_B_Enable_Register);
                        break;
                case IO_Bidirection_Pin_Register:
@@ -414,15 +575,14 @@ static inline void ni_set_bits(comedi_device *dev, int reg, int bits, int value)
                                devpriv->io_bidirection_pin_reg |= bits;
                        else
                                devpriv->io_bidirection_pin_reg &= ~bits;
-                       comedi_spin_unlock_irqrestore( &devpriv->window_lock, flags );
                        devpriv->stc_writew(dev, devpriv->io_bidirection_pin_reg,IO_Bidirection_Pin_Register);
                        break;
                default:
                        rt_printk("Warning ni_set_bits() called with invalid arguments\n");
                        rt_printk("reg is %d\n",reg);
-                       comedi_spin_unlock_irqrestore( &devpriv->window_lock, flags );
                        break;
        }
+       comedi_spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags );
 }
 
 
@@ -431,112 +591,69 @@ static irqreturn_t ni_E_interrupt(int irq, void *d PT_REGS_ARG)
        comedi_device *dev=d;
        unsigned short a_status;
        unsigned short b_status;
-       unsigned int m0_status;
-       unsigned int m1_status;
+       unsigned int ai_mite_status = 0;
+       unsigned int ao_mite_status = 0;
+       unsigned gpct0_mite_status = 0;
+       unsigned gpct1_mite_status = 0;
        unsigned long flags;
-#ifdef PCIDMA
        struct mite_struct *mite = devpriv->mite;
-#endif
 
        if(dev->attached == 0) return IRQ_NONE;
+       smp_mb();       // make sure dev->attached is checked before handler does anything else.
+
        // lock to avoid race with comedi_poll
        comedi_spin_lock_irqsave(&dev->spinlock, flags);
-       a_status=devpriv->stc_readw(dev, AI_Status_1_Register);
-       b_status=devpriv->stc_readw(dev, AO_Status_1_Register);
-#ifdef PCIDMA
-       m0_status=readl(mite->mite_io_addr + MITE_CHSR(AI_DMA_CHAN));
-       m1_status=readl(mite->mite_io_addr + MITE_CHSR(AO_DMA_CHAN));
-#else
-       m0_status = 0;
-       m1_status = 0;
-#endif
-
-       if(a_status&Interrupt_A_St || m0_status & CHSR_INT )
-               handle_a_interrupt(dev, a_status, m0_status);
-       if(b_status&Interrupt_B_St || m1_status & CHSR_INT )
-               handle_b_interrupt(dev, b_status, m1_status);
+       a_status = devpriv->stc_readw(dev, AI_Status_1_Register);
+       b_status = devpriv->stc_readw(dev, AO_Status_1_Register);
+       if(mite)
+       {
+               unsigned long flags_too;
+
+               comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags_too);
+               if(devpriv->ai_mite_chan)
+                       ai_mite_status = readl(mite->mite_io_addr + MITE_CHSR(devpriv->ai_mite_chan->channel));
+               if(devpriv->ao_mite_chan)
+                       ao_mite_status = readl(mite->mite_io_addr + MITE_CHSR(devpriv->ao_mite_chan->channel));
+               if(devpriv->gpct_mite_chan[0])
+                       gpct0_mite_status = readl(mite->mite_io_addr + MITE_CHSR(devpriv->gpct_mite_chan[0]->channel));
+               if(devpriv->gpct_mite_chan[1])
+                       gpct1_mite_status = readl(mite->mite_io_addr + MITE_CHSR(devpriv->gpct_mite_chan[1]->channel));
+               comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags_too);
+       }
+       if((a_status & Interrupt_A_St) || ((ai_mite_status | gpct0_mite_status) & CHSR_INT))
+               handle_a_interrupt(dev, a_status, ai_mite_status, gpct0_mite_status);
+       if((b_status & Interrupt_B_St) || ((ao_mite_status | gpct1_mite_status) & CHSR_INT))
+               handle_b_interrupt(dev, b_status, ao_mite_status, gpct1_mite_status);
        comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
        return IRQ_HANDLED;
 }
 
 #ifdef PCIDMA
-static void ni_sync_ai_dma(struct mite_struct *mite, comedi_device *dev)
+static void ni_sync_ai_dma(comedi_device *dev)
 {
-       int count;
        comedi_subdevice *s = dev->subdevices + 0;
-       comedi_async *async = s->async;
-       unsigned int nbytes, old_alloc_count;
-       unsigned int bytes_per_scan = bytes_per_sample(s) * async->cmd.chanlist_len;
-
-       old_alloc_count = async->buf_write_alloc_count;
-       // write alloc as much as we can
-       comedi_buf_write_alloc(s->async, s->async->prealloc_bufsz);
-
-       nbytes = mite_bytes_written_to_memory_lb(mite, AI_DMA_CHAN);
-       rmb();
-       if( (int)(mite_bytes_written_to_memory_ub(mite, AI_DMA_CHAN) - old_alloc_count) > 0 ){
-               rt_printk("ni_mio_common: DMA overwrite of free area\n");
-               ni_ai_reset(dev,s);
-               async->events |= COMEDI_CB_OVERFLOW;
-               return;
-       }
-
-       count = nbytes - async->buf_write_count;
-       if( count <= 0 ){
-               /* it's possible count will be negative due to
-                * conservative value returned by mite_bytes_transferred */
-               return;
-       }
-       comedi_buf_write_free(async, count);
+       unsigned long flags;
 
-       async->scan_progress += count;
-       if( async->scan_progress >= bytes_per_scan )
-       {
-               async->scan_progress %= bytes_per_scan;
-               async->events |= COMEDI_CB_EOS;
-       }
-       async->events |= COMEDI_CB_BLOCK;
+       comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+       if(devpriv->ai_mite_chan)
+               mite_sync_input_dma(devpriv->ai_mite_chan, s->async);
+       comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
 }
 
 static void mite_handle_b_linkc(struct mite_struct *mite, comedi_device *dev)
 {
-       int count;
        comedi_subdevice *s = dev->subdevices + 1;
-       comedi_async *async = s->async;
-       u32 nbytes_ub, nbytes_lb;
-       unsigned int new_write_count;
-       u32 stop_count = async->cmd.stop_arg * sizeof(sampl_t);
-
-       writel(CHOR_CLRLC, mite->mite_io_addr + MITE_CHOR(AO_DMA_CHAN));
-
-       new_write_count = async->buf_write_count;
-       mb();
-       nbytes_lb = mite_bytes_read_from_memory_lb(mite, AO_DMA_CHAN);
-       if(async->cmd.stop_src == TRIG_COUNT &&
-               (int) (nbytes_lb - stop_count) > 0)
-               nbytes_lb = stop_count;
-       mb();
-       nbytes_ub = mite_bytes_read_from_memory_ub(mite, AO_DMA_CHAN);
-       if(async->cmd.stop_src == TRIG_COUNT &&
-               (int) (nbytes_ub - stop_count) > 0)
-               nbytes_ub = stop_count;
-       if((int)(nbytes_ub - devpriv->last_buf_write_count) > 0){
-               rt_printk("ni_mio_common: DMA underrun\n");
-               ni_ao_reset(dev,s);
-               async->events |= COMEDI_CB_OVERFLOW;
-               return;
-       }
-       mb();
-       devpriv->last_buf_write_count = new_write_count;
 
-       count = nbytes_lb - async->buf_read_count;
-       if(count < 0){
+       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)
+       {
+               s->async->events |= COMEDI_CB_ERROR;
                return;
        }
-       comedi_buf_read_free(async, count);
-
-       async->events |= COMEDI_CB_BLOCK;
 }
+
 // #define DEBUG_DMA_TIMING
 static int ni_ao_wait_for_dma_load( comedi_device *dev )
 {
@@ -593,7 +710,7 @@ static void ni_handle_eos(comedi_device *dev, comedi_subdevice *s)
 
                for(i = 0; i < timeout; i++)
                {
-                       ni_sync_ai_dma(devpriv->mite, dev);
+                       ni_sync_ai_dma(dev);
                        if((s->async->events & COMEDI_CB_EOS)) break;
                        comedi_udelay(1);
                }
@@ -605,7 +722,6 @@ static void ni_handle_eos(comedi_device *dev, comedi_subdevice *s)
        /* handle special case of single scan using AI_End_On_End_Of_Scan */
        if((devpriv->ai_cmd2 & AI_End_On_End_Of_Scan)){
                shutdown_ai_command( dev );
-               ni_ai_reset(dev, s);
        }
 }
 
@@ -615,52 +731,103 @@ static void shutdown_ai_command( comedi_device *dev )
 
 #ifdef PCIDMA
        ni_ai_drain_dma( dev );
-       mite_dma_disarm(devpriv->mite, AI_DMA_CHAN);
 #endif
        ni_handle_fifo_dregs(dev);
        get_last_sample_611x(dev);
        get_last_sample_6143(dev);
 
-       ni_set_bits(dev, Interrupt_A_Enable_Register,
-               AI_SC_TC_Interrupt_Enable | AI_START1_Interrupt_Enable|
-               AI_START2_Interrupt_Enable| AI_START_Interrupt_Enable|
-               AI_STOP_Interrupt_Enable| AI_Error_Interrupt_Enable|
-               AI_FIFO_Interrupt_Enable,0);
-
        s->async->events |= COMEDI_CB_EOA;
 }
 
-static void handle_a_interrupt(comedi_device *dev,unsigned short status,
-       unsigned int m_status)
+static void handle_gpct_interrupt(comedi_device *dev, struct mite_channel *mite_chan, unsigned short is_terminal_count)
+{
+       unsigned gpct_mite_status;
+
+       gpct_mite_status = readl(mite_chan->mite->mite_io_addr + MITE_CHSR(mite_chan->channel));
+       if(gpct_mite_status & CHSR_INT)
+       {}
+}
+
+static void ni_event(comedi_device *dev, comedi_subdevice *s, unsigned events)
+{
+       if(events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW | COMEDI_CB_EOA))
+       {
+               switch(s->type)
+               {
+               case COMEDI_SUBD_AI:
+                       ni_ai_reset(dev, s);
+                       break;
+               case COMEDI_SUBD_AO:
+                       ni_ao_reset(dev, s);
+                       break;
+               default:
+                       break;
+               }
+       }
+       comedi_event(dev, s, events);
+}
+
+static void ack_a_interrupt(comedi_device *dev, unsigned short a_status)
+{
+       unsigned short ack = 0;
+
+       /* test for all uncommon interrupt events at the same time */
+       if(a_status & AI_SC_TC_St)
+       {
+               ack |= AI_SC_TC_Interrupt_Ack;
+       }
+       if(a_status & AI_START1_St)
+       {
+                       ack |= AI_START1_Interrupt_Ack;
+       }
+       if(a_status & AI_START_St)
+       {
+               ack |= AI_START_Interrupt_Ack;
+       }
+       if(a_status & AI_STOP_St)
+       {
+               /* 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)
+       {
+               ack |= G0_Gate_Interrupt_Ack;
+       }
+       if(ack) devpriv->stc_writew(dev, ack, Interrupt_A_Ack_Register);
+}
+
+static void handle_a_interrupt(comedi_device *dev, unsigned short status,
+       unsigned ai_mite_status, unsigned gpct0_mite_status)
 {
        comedi_subdevice *s=dev->subdevices+0;
-       unsigned short ack=0;
 
        s->async->events = 0;
 
 #ifdef DEBUG_INTERRUPT
-       rt_printk("ni_mio_common: interrupt: a_status=%04x m0_status=%08x\n",
-               status, m_status);
+       rt_printk("ni_mio_common: interrupt: a_status=%04x ai_mite_status=%08x\n",
+               status, ai_mite_status);
        ni_mio_print_status_a(status);
 #endif
-
-
+       ack_a_interrupt(dev, status);
 #ifdef PCIDMA
        /* Currently, mite.c requires us to handle LINKC and DONE */
-       if(m_status & CHSR_LINKC){
-               writel(CHOR_CLRLC, devpriv->mite->mite_io_addr + MITE_CHOR(AI_DMA_CHAN));
-               ni_sync_ai_dma(devpriv->mite, dev);
+       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);
        }
 
-       if(m_status & CHSR_DONE){
-               writel(CHOR_CLRDONE, devpriv->mite->mite_io_addr + MITE_CHOR(AI_DMA_CHAN));
+       if(ai_mite_status & CHSR_DONE){
+               writel(CHOR_CLRDONE, devpriv->mite->mite_io_addr + MITE_CHOR(devpriv->ai_mite_chan->channel));
        }
 
-       if(m_status & ~(CHSR_INT | CHSR_LINKC | CHSR_DONE | CHSR_MRDY | CHSR_DRDY | CHSR_DRQ1 | CHSR_DRQ0 | CHSR_ERROR | CHSR_SABORT | CHSR_XFERR | CHSR_LxERR_mask)){
-               rt_printk("unknown mite interrupt, ack! (m_status=%08x)\n", m_status);
-               //mite_print_chsr(m_status);
-               mite_dma_disarm(devpriv->mite, AI_DMA_CHAN );
-               writel(CHOR_DMARESET, devpriv->mite->mite_io_addr + MITE_CHOR(AI_DMA_CHAN));
+       if(ai_mite_status & ~(CHSR_INT | CHSR_LINKC | CHSR_DONE | CHSR_MRDY | CHSR_DRDY | CHSR_DRQ1 | CHSR_DRQ0 | CHSR_ERROR | CHSR_SABORT | CHSR_XFERR | CHSR_LxERR_mask)){
+               rt_printk("unknown mite interrupt, ack! (ai_mite_status=%08x)\n", ai_mite_status);
+               //mite_print_chsr(ai_mite_status);
+               s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
                //disable_irq(dev->irq);
        }
 #endif
@@ -673,7 +840,7 @@ static void handle_a_interrupt(comedi_device *dev,unsigned short status,
                         * so it's a good idea to be careful. */
                        if(s->subdev_flags&SDF_RUNNING){
                                s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
-                               //comedi_event(dev,s,s->async->events);
+                               ni_event(dev, s, s->async->events);
                        }
                        return;
                }
@@ -682,16 +849,13 @@ static void handle_a_interrupt(comedi_device *dev,unsigned short status,
                                status);
                        ni_mio_print_status_a(status);
 
-                       ni_ai_reset(dev,dev->subdevices);
-
-
                        shutdown_ai_command( dev );
 
                        s->async->events |= COMEDI_CB_ERROR;
                        if(status & (AI_Overrun_St | AI_Overflow_St))
                                s->async->events |= COMEDI_CB_OVERFLOW;
 
-                       comedi_event(dev,s,s->async->events);
+                       ni_event(dev, s, s->async->events);
 
                        return;
                }
@@ -700,12 +864,8 @@ static void handle_a_interrupt(comedi_device *dev,unsigned short status,
                        rt_printk("ni_mio_common: SC_TC interrupt\n");
 #endif
                        if(!devpriv->ai_continuous){
-                               shutdown_ai_command( dev );
+                               shutdown_ai_command(dev);
                        }
-                       ack|=AI_SC_TC_Interrupt_Ack;
-               }
-               if(status&AI_START1_St){
-                       ack|=AI_START1_Interrupt_Ack;
                }
        }
 #ifndef PCIDMA
@@ -725,19 +885,19 @@ static void handle_a_interrupt(comedi_device *dev,unsigned short status,
 
        if( (status & AI_STOP_St) ){
                ni_handle_eos(dev, s);
-               /* we need to ack the START, also */
-               ack |= AI_STOP_Interrupt_Ack|AI_START_Interrupt_Ack;
        }
-#if 0
-       if(devpriv->aimode==AIMODE_SAMPLE){
-               ni_handle_fifo_dregs(dev);
 
-               //s->async->events |= COMEDI_CB_SAMPLE;
+       if(status & (G0_TC_St | G0_Gate_Interrupt_St))
+       {
+               unsigned long flags;
+
+               comedi_spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+               if(devpriv->gpct_mite_chan)
+                       handle_gpct_interrupt(dev, devpriv->gpct_mite_chan[0], (status & G0_TC_St));
+               comedi_spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
        }
-#endif
-       if(ack) devpriv->stc_writew(dev, ack,Interrupt_A_Ack_Register);
 
-       comedi_event(dev,s,s->async->events);
+       ni_event(dev,s,s->async->events);
 
 #ifdef DEBUG_INTERRUPT
        status=devpriv->stc_readw(dev, AI_Status_1_Register);
@@ -747,45 +907,85 @@ static void handle_a_interrupt(comedi_device *dev,unsigned short status,
 #endif
 }
 
-static void handle_b_interrupt(comedi_device *dev,unsigned short b_status, unsigned int m_status)
+static void ack_b_interrupt(comedi_device *dev, unsigned short b_status)
+{
+       unsigned short ack = 0;
+       if(b_status & AO_BC_TC_St)
+       {
+               ack |= AO_BC_TC_Interrupt_Ack;
+       }
+       if(b_status & AO_Overrun_St)
+       {
+               ack |= AO_Error_Interrupt_Ack;
+       }
+       if(b_status & AO_START_St)
+       {
+               ack |= AO_START_Interrupt_Ack;
+       }
+       if(b_status & AO_START1_St)
+       {
+               ack |= AO_START1_Interrupt_Ack;
+       }
+       if(b_status & AO_UC_TC_St)
+       {
+               ack |= AO_UC_TC_Interrupt_Ack;
+       }
+       if(b_status & AO_UI2_TC_St)
+       {
+               ack |= AO_UI2_TC_Interrupt_Ack;
+       }
+       if(b_status & AO_UPDATE_St)
+       {
+               ack |= AO_UPDATE_Interrupt_Ack;
+       }
+       if(b_status & G1_Gate_Interrupt_St)
+       {
+               ack |= G1_Gate_Interrupt_Ack;
+       }
+       if(b_status & G1_TC_St)
+       {
+               ack |= G1_TC_Interrupt_Ack;
+       }
+       if(ack) devpriv->stc_writew(dev, ack, Interrupt_B_Ack_Register);
+}
+
+static void handle_b_interrupt(comedi_device *dev, unsigned short b_status,
+       unsigned ao_mite_status, unsigned gpct1_mite_status)
 {
        comedi_subdevice *s=dev->subdevices+1;
        //unsigned short ack=0;
 #ifdef DEBUG_INTERRUPT
        rt_printk("ni_mio_common: interrupt: b_status=%04x m1_status=%08x\n",
-               b_status,m_status);
+               b_status,ao_mite_status);
        ni_mio_print_status_b(b_status);
 #endif
-
+       ack_b_interrupt(dev, b_status);
 
 #ifdef PCIDMA
        /* Currently, mite.c requires us to handle LINKC and DONE */
-       if(m_status & CHSR_LINKC){
+       if(ao_mite_status & CHSR_LINKC){
                mite_handle_b_linkc(devpriv->mite, dev);
        }
 
-       if(m_status & CHSR_DONE){
-               writel(CHOR_CLRDONE, devpriv->mite->mite_io_addr + MITE_CHOR(AO_DMA_CHAN));
+       if(ao_mite_status & CHSR_DONE){
+               writel(CHOR_CLRDONE, devpriv->mite->mite_io_addr + MITE_CHOR(devpriv->ao_mite_chan->channel));
        }
 
-       if(m_status & ~(CHSR_INT | CHSR_LINKC | CHSR_DONE | CHSR_MRDY | CHSR_DRDY | CHSR_DRQ1 | CHSR_DRQ0 | CHSR_ERROR | CHSR_SABORT | CHSR_XFERR | CHSR_LxERR_mask)){
-               rt_printk("unknown mite interrupt, ack! (m_status=%08x)\n", m_status);
-               //mite_print_chsr(m_status);
-               mite_dma_disarm(devpriv->mite, AO_DMA_CHAN );
-               writel(CHOR_DMARESET, devpriv->mite->mite_io_addr + MITE_CHOR(AO_DMA_CHAN));
-       }
+       if(ao_mite_status & ~(CHSR_INT | CHSR_LINKC | CHSR_DONE | CHSR_MRDY | CHSR_DRDY | CHSR_DRQ1 | CHSR_DRQ0 | CHSR_ERROR | CHSR_SABORT | CHSR_XFERR | CHSR_LxERR_mask)){
+               rt_printk("unknown mite interrupt, ack! (ao_mite_status=%08x)\n", ao_mite_status);
+               //mite_print_chsr(ao_mite_status);
+               s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+       }
 #endif
 
        if(b_status==0xffff)return;
        if(b_status&AO_Overrun_St){
                rt_printk("ni_mio_common: AO FIFO underrun status=0x%04x status2=0x%04x\n",b_status,devpriv->stc_readw(dev, AO_Status_2_Register));
-               ni_ao_reset(dev,s);
                s->async->events |= COMEDI_CB_OVERFLOW;
        }
 
        if(b_status&AO_BC_TC_St){
                MDPRINTK("ni_mio_common: AO BC_TC status=0x%04x status2=0x%04x\n",b_status,devpriv->stc_readw(dev, AO_Status_2_Register));
-               ni_ao_reset(dev,s);
                s->async->events |= COMEDI_CB_EOA;
        }
 
@@ -803,18 +1003,7 @@ static void handle_b_interrupt(comedi_device *dev,unsigned short b_status, unsig
        }
 #endif
 
-       b_status=devpriv->stc_readw(dev, AO_Status_1_Register);
-       if(b_status&Interrupt_B_St){
-               if(b_status&AO_FIFO_Request_St){
-                       rt_printk("ni_mio_common: AO buffer underrun\n");
-               }
-               rt_printk("Ack! didn't clear AO interrupt. b_status=0x%04x\n",b_status);
-               ni_set_bits(dev,Interrupt_B_Enable_Register,~0,0);
-               ni_ao_reset(dev,s);
-               s->async->events |= COMEDI_CB_OVERFLOW;
-       }
-
-       comedi_event(dev,s,s->async->events);
+       ni_event(dev,s,s->async->events);
 }
 
 #ifdef DEBUG_STATUS_A
@@ -926,7 +1115,7 @@ static int ni_ao_fifo_half_empty(comedi_device *dev,comedi_subdevice *s)
 {
        int n;
 
-       n = comedi_buf_read_n_available(s);
+       n = comedi_buf_read_n_available(s->async);
        if(n==0){
                s->async->events |= COMEDI_CB_OVERFLOW;
                return 0;
@@ -953,7 +1142,7 @@ static int ni_ao_prep_fifo(comedi_device *dev,comedi_subdevice *s)
                ni_ao_win_outl(dev, 0x6, AO_FIFO_Offset_Load_611x);
 
        /* load some data */
-       n = comedi_buf_read_n_available(s);
+       n = comedi_buf_read_n_available(s->async);
        if(n==0)return 0;
 
        n /= sizeof(sampl_t);
@@ -1036,14 +1225,14 @@ static void ni_handle_fifo_half_full(comedi_device *dev)
 #ifdef PCIDMA
 static int ni_ai_drain_dma(comedi_device *dev )
 {
-       struct mite_struct *mite = devpriv->mite;
        int i;
        static const int timeout = 10000;
 
+       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(mite, AI_DMA_CHAN) == 0)
+                       mite_bytes_in_transit(devpriv->ai_mite_chan) == 0)
                        break;
                comedi_udelay(2);
        }
@@ -1051,11 +1240,11 @@ static int ni_ai_drain_dma(comedi_device *dev )
        {
                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(mite, AI_DMA_CHAN), devpriv->stc_readw(dev, AI_Status_1_Register));
+                       mite_bytes_in_transit(devpriv->ai_mite_chan), devpriv->stc_readw(dev, AI_Status_1_Register));
                return -1;
        }
 
-       ni_sync_ai_dma( mite, dev );
+       ni_sync_ai_dma(dev);
 
        return 0;
 }
@@ -1177,54 +1366,62 @@ static void ni_ai_munge(comedi_device *dev, comedi_subdevice *s,
 
 #ifdef PCIDMA
 
-static void ni_ai_setup_MITE_dma(comedi_device *dev,comedi_cmd *cmd)
+static int ni_ai_setup_MITE_dma(comedi_device *dev)
 {
-       struct mite_struct *mite = devpriv->mite;
-       struct mite_channel *mite_chan = &mite->channels[ AI_DMA_CHAN ];
        comedi_subdevice *s = dev->subdevices + 0;
+       int retval;
+
+       retval = ni_request_ai_mite_channel(dev);
+       if(retval) return retval;
+//     rt_printk("comedi_debug: using mite channel %i for ai.\n", devpriv->ai_mite_chan->channel);
 
        /* write alloc the entire buffer */
        comedi_buf_write_alloc(s->async, s->async->prealloc_bufsz);
 
-       mite_chan->current_link = 0;
-       mite_chan->dir = COMEDI_INPUT;
+       devpriv->ai_mite_chan->dir = COMEDI_INPUT;
        switch(boardtype.reg_type)
        {
        case ni_reg_611x:
        case ni_reg_6143:
-               mite_prep_dma(mite, AI_DMA_CHAN, 32, 16);
+               mite_prep_dma(devpriv->ai_mite_chan, 32, 16);
                break;
        case ni_reg_628x:
-               mite_prep_dma(mite, AI_DMA_CHAN, 32, 32);
+               mite_prep_dma(devpriv->ai_mite_chan, 32, 32);
                break;
        default:
-               mite_prep_dma(mite, AI_DMA_CHAN, 16, 16);
+               mite_prep_dma(devpriv->ai_mite_chan, 16, 16);
                break;
        };
        /*start the MITE*/
-       mite_dma_arm(mite, AI_DMA_CHAN);
+       mite_dma_arm(devpriv->ai_mite_chan);
+       return 0;
 }
 
-static void ni_ao_setup_MITE_dma(comedi_device *dev,comedi_cmd *cmd)
+static int ni_ao_setup_MITE_dma(comedi_device *dev)
 {
-       struct mite_struct *mite = devpriv->mite;
-       struct mite_channel *mite_chan = &mite->channels[ AO_DMA_CHAN ];
        comedi_subdevice *s = dev->subdevices + 1;
+       int retval;
+
+       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);
 
-       devpriv->last_buf_write_count = s->async->buf_write_count;
-       mite_chan->current_link = 0;
-       mite_chan->dir = COMEDI_OUTPUT;
+       /* read alloc the entire buffer */
+       comedi_buf_read_alloc(s->async, s->async->prealloc_bufsz);
+
+       devpriv->ao_mite_chan->dir = COMEDI_OUTPUT;
        if(boardtype.reg_type & (ni_reg_611x | ni_reg_6713))
        {
-               mite_prep_dma(mite, AO_DMA_CHAN, 32, 32);
+               mite_prep_dma(devpriv->ao_mite_chan, 32, 32);
        }else
        {
                /* doing 32 instead of 16 bit wide transfers from memory
                 makes the mite do 32 bit pci transfers, doubling pci bandwidth. */
-               mite_prep_dma(mite, AO_DMA_CHAN, 16, 32);
+               mite_prep_dma(devpriv->ao_mite_chan, 16, 32);
        }
        /*start the MITE*/
-       mite_dma_arm(mite, AO_DMA_CHAN);
+       mite_dma_arm(devpriv->ao_mite_chan);
+       return 0;
 }
 
 #endif // PCIDMA
@@ -1237,9 +1434,7 @@ static void ni_ao_setup_MITE_dma(comedi_device *dev,comedi_cmd *cmd)
 
 static int ni_ai_reset(comedi_device *dev,comedi_subdevice *s)
 {
-#ifdef PCIDMA
-       mite_dma_disarm(devpriv->mite, AI_DMA_CHAN);
-#endif
+       ni_release_ai_mite_channel(dev);
        /* ai configuration */
        devpriv->stc_writew(dev, AI_Configuration_Start | AI_Reset, Joint_Reset_Register);
 
@@ -1249,7 +1444,7 @@ static int ni_ai_reset(comedi_device *dev,comedi_subdevice *s)
                AI_STOP_Interrupt_Enable|   AI_Error_Interrupt_Enable|
                AI_FIFO_Interrupt_Enable,0);
 
-       ni_flush_ai_fifo(dev);
+       ni_clear_ai_fifo(dev);
 
        if(boardtype.reg_type != ni_reg_6143)
                ni_writeb(0, Misc_Command);
@@ -1323,7 +1518,7 @@ static int ni_ai_poll(comedi_device *dev,comedi_subdevice *s)
 #ifndef PCIDMA
        ni_handle_fifo_dregs(dev);
 #else
-       ni_sync_ai_dma(devpriv->mite, dev);
+       ni_sync_ai_dma(dev);
 #endif
        count = s->async->buf_write_count - s->async->buf_read_count;
        if(in_interrupt() == 0)
@@ -1343,7 +1538,7 @@ static int ni_ai_insn_read(comedi_device *dev,comedi_subdevice *s,comedi_insn *i
 
        ni_load_channelgain_list(dev,1,&insn->chanspec);
 
-       ni_flush_ai_fifo(dev);
+       ni_clear_ai_fifo(dev);
 
        signbits=devpriv->ai_offset[0];
        if(boardtype.reg_type == ni_reg_611x){
@@ -1440,7 +1635,6 @@ static void ni_m_series_load_channelgain_list(comedi_device *dev,unsigned int n_
 {
        unsigned int chan, range, aref;
        unsigned int i;
-       unsigned config_bits = 0;
        unsigned offset;
        unsigned int dither;
        unsigned range_code;
@@ -1473,6 +1667,7 @@ static void ni_m_series_load_channelgain_list(comedi_device *dev,unsigned int n_
        offset = 0;
        for(i = 0; i < n_chan; i++)
        {
+               unsigned config_bits = 0;
                chan = CR_CHAN(list[i]);
                aref = CR_AREF(list[i]);
                range = CR_RANGE(list[i]);
@@ -1480,7 +1675,6 @@ static void ni_m_series_load_channelgain_list(comedi_device *dev,unsigned int n_
 
                range_code = ni_gainlkup[boardtype.gainlkup][range];
                devpriv->ai_offset[i] = offset;
-
                switch( aref )
                {
                        case AREF_DIFF:
@@ -1644,27 +1838,46 @@ static void ni_load_channelgain_list(comedi_device *dev,unsigned int n_chan,
        }
 }
 
-static int ni_ns_to_timer(comedi_device *dev, int *nanosec, int round_mode)
+static int ni_ns_to_timer(const comedi_device *dev, unsigned nanosec, int round_mode)
 {
        int divider;
        switch(round_mode)
        {
        case TRIG_ROUND_NEAREST:
        default:
-               divider = (*nanosec + devpriv->clock_ns / 2) / devpriv->clock_ns;
+               divider = (nanosec + devpriv->clock_ns / 2) / devpriv->clock_ns;
                break;
        case TRIG_ROUND_DOWN:
-               divider = (*nanosec) / devpriv->clock_ns;
+               divider = (nanosec) / devpriv->clock_ns;
                break;
        case TRIG_ROUND_UP:
-               divider=(*nanosec + devpriv->clock_ns - 1) / devpriv->clock_ns;
+               divider=(nanosec + devpriv->clock_ns - 1) / devpriv->clock_ns;
                break;
        }
-
-       *nanosec = devpriv->clock_ns * divider;
        return divider - 1;
 }
 
+static unsigned ni_timer_to_ns(const comedi_device *dev, int timer)
+{
+       return devpriv->clock_ns * (timer + 1);
+}
+
+static unsigned ni_min_ai_scan_period_ns(comedi_device *dev, unsigned num_channels)
+{
+       switch(boardtype.reg_type)
+       {
+       case ni_reg_611x:
+       case ni_reg_6143:
+       // simultaneously-sampled inputs
+               return boardtype.ai_speed;
+               break;
+       default:
+       // multiplexed inputs
+               break;
+       };
+       return boardtype.ai_speed * num_channels;
+}
+
 static int ni_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd)
 {
        int err=0;
@@ -1734,8 +1947,9 @@ static int ni_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd)
                }
        }
        if(cmd->scan_begin_src==TRIG_TIMER){
-               if(cmd->scan_begin_arg<boardtype.ai_speed){
-                       cmd->scan_begin_arg=boardtype.ai_speed;
+               if(cmd->scan_begin_arg < ni_min_ai_scan_period_ns(dev, cmd->chanlist_len))
+               {
+                       cmd->scan_begin_arg = ni_min_ai_scan_period_ns(dev, cmd->chanlist_len);
                        err++;
                }
                if(cmd->scan_begin_arg > devpriv->clock_ns * 0xffffff){
@@ -1822,16 +2036,17 @@ static int ni_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd)
 
        if(cmd->scan_begin_src==TRIG_TIMER){
                tmp=cmd->scan_begin_arg;
-               ni_ns_to_timer(dev, &cmd->scan_begin_arg, cmd->flags&TRIG_ROUND_MASK);
+               cmd->scan_begin_arg = ni_timer_to_ns(dev, ni_ns_to_timer(dev, cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK));
                if(tmp!=cmd->scan_begin_arg)err++;
        }
        if(cmd->convert_src==TRIG_TIMER){
                if((boardtype.reg_type != ni_reg_611x) && (boardtype.reg_type != ni_reg_6143)){
                        tmp=cmd->convert_arg;
-                       ni_ns_to_timer(dev, &cmd->convert_arg, cmd->flags&TRIG_ROUND_MASK);
+                       cmd->convert_arg = ni_timer_to_ns(dev, ni_ns_to_timer(dev, cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK));
                        if(tmp!=cmd->convert_arg)err++;
                        if(cmd->scan_begin_src==TRIG_TIMER &&
-                       cmd->scan_begin_arg<cmd->convert_arg*cmd->scan_end_arg){
+                               cmd->scan_begin_arg<cmd->convert_arg*cmd->scan_end_arg)
+                       {
                                cmd->scan_begin_arg=cmd->convert_arg*cmd->scan_end_arg;
                                err++;
                        }
@@ -1845,7 +2060,7 @@ static int ni_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd)
 
 static int ni_ai_cmd(comedi_device *dev,comedi_subdevice *s)
 {
-       comedi_cmd *cmd=&s->async->cmd;
+       const comedi_cmd *cmd=&s->async->cmd;
        int timer;
        int mode1=0; /* mode1 is needed for both stop and convert */
        int mode2=0;
@@ -1859,7 +2074,7 @@ static int ni_ai_cmd(comedi_device *dev,comedi_subdevice *s)
                comedi_error(dev, "cannot run command without an irq");
                return -EIO;
        }
-       ni_flush_ai_fifo(dev);
+       ni_clear_ai_fifo(dev);
 
        ni_load_channelgain_list(dev,cmd->chanlist_len,cmd->chanlist);
 
@@ -1976,7 +2191,7 @@ static int ni_ai_cmd(comedi_device *dev,comedi_subdevice *s)
                devpriv->stc_writew(dev, mode2, AI_Mode_2_Register);
 
                /* load SI */
-               timer = ni_ns_to_timer(dev, &cmd->scan_begin_arg, TRIG_ROUND_NEAREST);
+               timer = ni_ns_to_timer(dev, cmd->scan_begin_arg, TRIG_ROUND_NEAREST);
                devpriv->stc_writel(dev, timer,AI_SI_Load_A_Registers);
                devpriv->stc_writew(dev, AI_SI_Load,AI_Command_1_Register);
                break;
@@ -2000,7 +2215,7 @@ static int ni_ai_cmd(comedi_device *dev,comedi_subdevice *s)
                if( cmd->convert_arg == 0 || cmd->convert_src == TRIG_NOW )
                        timer = 1;
                else
-                       timer = ni_ns_to_timer(dev, &cmd->convert_arg, TRIG_ROUND_NEAREST);
+                       timer = ni_ns_to_timer(dev, cmd->convert_arg, TRIG_ROUND_NEAREST);
                devpriv->stc_writew(dev, 1,AI_SI2_Load_A_Register); /* 0,0 does not work. */
                devpriv->stc_writew(dev, timer,AI_SI2_Load_B_Register);
 
@@ -2101,7 +2316,10 @@ static int ni_ai_cmd(comedi_device *dev,comedi_subdevice *s)
        }
 
 #ifdef PCIDMA
-       ni_ai_setup_MITE_dma(dev,cmd);
+       {
+               int retval = ni_ai_setup_MITE_dma(dev);
+               if(retval) return retval;
+       }
        //mite_dump_regs(devpriv->mite);
 #endif
 
@@ -2476,7 +2694,7 @@ static int ni_ao_inttrig(comedi_device *dev,comedi_subdevice *s,
        int i;
        static const int timeout = 1000;
 
-       if(trignum!=0)return -EINVAL;
+       if(trignum!=0) return -EINVAL;
 
        ni_set_bits(dev, Interrupt_B_Enable_Register, AO_FIFO_Interrupt_Enable | AO_Error_Interrupt_Enable, 0);
        interrupt_b_bits = AO_Error_Interrupt_Enable;
@@ -2484,7 +2702,8 @@ static int ni_ao_inttrig(comedi_device *dev,comedi_subdevice *s,
        devpriv->stc_writew(dev, 1, DAC_FIFO_Clear);
        if(boardtype.reg_type & ni_reg_6xxx_mask)
                ni_ao_win_outl(dev, 0x6, AO_FIFO_Offset_Load_611x);
-       ni_ao_setup_MITE_dma(dev, &s->async->cmd);
+       ret = ni_ao_setup_MITE_dma(dev);
+       if(ret) return ret;
        ret = ni_ao_wait_for_dma_load(dev);
        if(ret < 0) return ret;
 
@@ -2526,7 +2745,7 @@ static int ni_ao_inttrig(comedi_device *dev,comedi_subdevice *s,
 
 static int ni_ao_cmd(comedi_device *dev,comedi_subdevice *s)
 {
-       comedi_cmd *cmd = &s->async->cmd;
+       const comedi_cmd *cmd = &s->async->cmd;
        int trigvar;
        int bits;
        int i;
@@ -2536,7 +2755,7 @@ static int ni_ao_cmd(comedi_device *dev,comedi_subdevice *s)
                comedi_error(dev, "cannot run command without an irq");
                return -EIO;
        }
-       trigvar = ni_ns_to_timer(dev, &cmd->scan_begin_arg, TRIG_ROUND_NEAREST);
+       trigvar = ni_ns_to_timer(dev, cmd->scan_begin_arg, TRIG_ROUND_NEAREST);
 
        devpriv->stc_writew(dev, AO_Configuration_Start,Joint_Reset_Register);
 
@@ -2654,8 +2873,12 @@ static int ni_ao_cmd(comedi_device *dev,comedi_subdevice *s)
                bits |= AO_FIFO_Enable;
        else
                bits |= AO_DMA_PIO_Control;
+#if 0
+       /* F Hess: windows driver does not set AO_Number_Of_DAC_Packages bit for 6281,
+       verified with bus analyzer. */
        if(boardtype.reg_type & ni_reg_m_series_mask)
-               bits |= AO_Number_Of_DAC_Packages/* | AO_Multiple_DACS_Per_Package*/;
+               bits |= AO_Number_Of_DAC_Packages;
+#endif
        devpriv->stc_writew(dev, bits, AO_Personal_Register);
        // enable sending of ao dma requests
        devpriv->stc_writew(dev, AO_AOFREQ_Enable, AO_Start_Select_Register);
@@ -2715,15 +2938,12 @@ static int ni_ao_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd)
                cmd->start_arg=0;
                err++;
        }
-#if 0
-       /* XXX need ao_speed */
-       if(cmd->scan_begin_arg<boardtype.ao_speed){
-               cmd->scan_begin_arg=boardtype.ao_speed;
+       if(cmd->scan_begin_arg < boardtype.ao_speed){
+               cmd->scan_begin_arg = boardtype.ao_speed;
                err++;
        }
-#endif
-       if(cmd->scan_begin_arg>devpriv->clock_ns*0xffffff){ /* XXX check */
-               cmd->scan_begin_arg=devpriv->clock_ns*0xffffff;
+       if(cmd->scan_begin_arg > devpriv->clock_ns * 0xffffff){ /* XXX check */
+               cmd->scan_begin_arg = devpriv->clock_ns * 0xffffff;
                err++;
        }
        if(cmd->convert_arg!=0){
@@ -2752,7 +2972,7 @@ static int ni_ao_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd)
        /* step 4: fix up any arguments */
 
        tmp = cmd->scan_begin_arg;
-       ni_ns_to_timer(dev, &cmd->scan_begin_arg, cmd->flags&TRIG_ROUND_MASK);
+       cmd->scan_begin_arg = ni_timer_to_ns(dev, ni_ns_to_timer(dev, cmd->scan_begin_arg, cmd->flags&TRIG_ROUND_MASK));
        if(tmp!=cmd->scan_begin_arg)err++;
 
        if(err)return 4;
@@ -2773,10 +2993,7 @@ static int ni_ao_reset(comedi_device *dev,comedi_subdevice *s)
        //devpriv->ao1p=AO_Channel(1);
        //ni_writew(devpriv->ao1p,AO_Configuration);
 
-#ifdef PCIDMA
-       mite_dma_disarm(devpriv->mite, AO_DMA_CHAN);
-       writel(CHOR_DMARESET | CHOR_FRESET, devpriv->mite->mite_io_addr + MITE_CHOR(AO_DMA_CHAN));
-#endif
+       ni_release_ao_mite_channel(dev);
 
        devpriv->stc_writew(dev, AO_Configuration_Start,Joint_Reset_Register);
        devpriv->stc_writew(dev, AO_Disarm,AO_Command_1_Register);
@@ -3117,7 +3334,7 @@ static void init_ao_67xx(comedi_device *dev, comedi_subdevice *s)
                ni_ao_win_outw(dev, AO_Channel(i) | 0x0, AO_Configuration_2_67xx);
 }
 
-unsigned ni_gpct_to_stc_register(enum ni_gpct_register reg)
+static unsigned ni_gpct_to_stc_register(enum ni_gpct_register reg)
 {
        unsigned stc_register;
        switch(reg)
@@ -3212,6 +3429,13 @@ static void ni_gpct_write_register(struct ni_gpct *counter, unsigned bits, enum
        case NITIO_G1_Second_Gate_Reg:
                ni_writew(bits, M_Offset_G1_Second_Gate);
                break;
+       case NITIO_G0_ABZ_Reg:
+               ni_writew(bits, M_Offset_G0_MSeries_ABZ);
+               break;
+       case NITIO_G1_ABZ_Reg:
+               ni_writew(bits, M_Offset_G1_MSeries_ABZ);
+               break;
+
        /* 32 bit registers */
        case NITIO_G0_LoadA_Reg:
        case NITIO_G1_LoadA_Reg:
@@ -3261,6 +3485,8 @@ static int ni_alloc_private(comedi_device *dev)
        if(ret < 0) return ret;
 
        spin_lock_init(&devpriv->window_lock);
+       spin_lock_init(&devpriv->soft_reg_copy_lock);
+       spin_lock_init(&devpriv->mite_channel_lock);
 
        return 0;
 };
@@ -3268,7 +3494,6 @@ static int ni_alloc_private(comedi_device *dev)
 static int ni_E_init(comedi_device *dev,comedi_devconfig *it)
 {
        comedi_subdevice *s;
-       int bits;
        unsigned j;
 
        if(boardtype.n_aochan > MAX_N_AO_CHAN)
@@ -3297,50 +3522,54 @@ static int ni_E_init(comedi_device *dev,comedi_devconfig *it)
                s->len_chanlist=512;
                s->maxdata=(1<<boardtype.adbits)-1;
                s->range_table=ni_range_lkup[boardtype.gainlkup];
-               s->insn_read=ni_ai_insn_read;
-               s->insn_config=ni_ai_insn_config;
-               s->do_cmdtest=ni_ai_cmdtest;
-               s->do_cmd=ni_ai_cmd;
-               s->cancel=ni_ai_reset;
-               s->poll=ni_ai_poll;
-               s->munge=ni_ai_munge;
+               s->insn_read = &ni_ai_insn_read;
+               s->insn_config = &ni_ai_insn_config;
+               s->do_cmdtest = &ni_ai_cmdtest;
+               s->do_cmd = &ni_ai_cmd;
+               s->cancel = &ni_ai_reset;
+               s->poll = &ni_ai_poll;
+               s->munge = &ni_ai_munge;
+#ifdef PCIDMA
+               s->async_dma_dir = DMA_FROM_DEVICE;
+#endif
        }else{
                s->type=COMEDI_SUBD_UNUSED;
        }
 
        /* analog output subdevice */
 
-       s=dev->subdevices+1;
+       s = dev->subdevices + 1;
        if(boardtype.n_aochan){
-               s->type=COMEDI_SUBD_AO;
-               s->subdev_flags=SDF_WRITABLE|SDF_DEGLITCH|SDF_GROUND;
+               s->type = COMEDI_SUBD_AO;
+               s->subdev_flags = SDF_WRITABLE | SDF_DEGLITCH | SDF_GROUND;
                if(boardtype.reg_type & ni_reg_m_series_mask)
                        s->subdev_flags |= SDF_SOFT_CALIBRATED;
-               s->n_chan=boardtype.n_aochan;
-               s->maxdata=(1<<boardtype.aobits)-1;
+               s->n_chan = boardtype.n_aochan;
+               s->maxdata = (1 << boardtype.aobits) - 1;
                s->range_table = boardtype.ao_range_table;
-               s->insn_read=ni_ao_insn_read;
+               s->insn_read = &ni_ao_insn_read;
                if(boardtype.reg_type & ni_reg_6xxx_mask){
-                       s->insn_write=ni_ao_insn_write_671x;
+                       s->insn_write = &ni_ao_insn_write_671x;
                }else{
-                       s->insn_write=ni_ao_insn_write;
+                       s->insn_write = &ni_ao_insn_write;
                }
 #ifdef PCIDMA
                if(boardtype.n_aochan){
+                       s->async_dma_dir = DMA_TO_DEVICE;
 #else
                if(boardtype.ao_fifo_depth){
 #endif
                        dev->write_subdev=s;
                        s->subdev_flags |= SDF_CMD_WRITE;
-                       s->do_cmd=ni_ao_cmd;
-                       s->do_cmdtest=ni_ao_cmdtest;
+                       s->do_cmd = &ni_ao_cmd;
+                       s->do_cmdtest = &ni_ao_cmdtest;
                        s->len_chanlist = boardtype.n_aochan;
                        if((boardtype.reg_type & ni_reg_m_series_mask) == 0)
                                s->munge=ni_ao_munge;
                }
-               s->cancel=ni_ao_reset;
+               s->cancel = &ni_ao_reset;
        }else{
-               s->type=COMEDI_SUBD_UNUSED;
+               s->type = COMEDI_SUBD_UNUSED;
        }
        if((boardtype.reg_type & ni_reg_67xx_mask))
                init_ao_67xx(dev, s);
@@ -3353,15 +3582,14 @@ static int ni_E_init(comedi_device *dev,comedi_devconfig *it)
        s->maxdata=1;
        s->io_bits=0;           /* all bits input */
        s->range_table=&range_digital;
+       s->n_chan = boardtype.num_p0_dio_channels;
        if(boardtype.reg_type & ni_reg_m_series_mask)
        {
-               s->n_chan = 32;
                s->insn_bits = ni_m_series_dio_insn_bits;
                s->insn_config=ni_m_series_dio_insn_config;
                ni_writel(s->io_bits, M_Offset_DIO_Direction);
        }else
        {
-               s->n_chan=8;
                s->insn_bits=ni_dio_insn_bits;
                s->insn_config=ni_dio_insn_config;
                devpriv->dio_control = DIO_Pins_Dir(s->io_bits);
@@ -3494,6 +3722,10 @@ static int ni_E_init(comedi_device *dev,comedi_devconfig *it)
                s->insn_read = ni_gpct_insn_read;
                s->insn_write = ni_gpct_insn_write;
                s->insn_config = ni_gpct_insn_config;
+               s->do_cmd = ni_gpct_cmd;
+               s->do_cmdtest = ni_gpct_cmdtest;
+               s->cancel = ni_gpct_cancel;
+               s->async_dma_dir = DMA_BIDIRECTIONAL;
                s->private = &devpriv->counters[j];
 
                devpriv->counters[j].dev = dev;
@@ -3509,6 +3741,7 @@ static int ni_E_init(comedi_device *dev,comedi_devconfig *it)
                        devpriv->counters[j].variant = ni_gpct_variant_e_series;
                }
                devpriv->counters[j].clock_period_ps = 0;
+               devpriv->counters[j].mite_chan = NULL;
                ni_tio_init_counter(&devpriv->counters[j]);
        }
 
@@ -3547,15 +3780,8 @@ static int ni_E_init(comedi_device *dev,comedi_devconfig *it)
        }
 
        /* DMA setup */
-       /* tell the STC which dma channels to use for AI and AO */
-       bits = 1 << ( AI_DMA_CHAN );
-       bits |= 1 << ( AO_DMA_CHAN + 4 );
-       ni_writeb( bits, AI_AO_Select);
-       /* tell the STC which dma channels to use for
-        * General purpose counters 0 and 1 */
-       bits = 1 << ( GPC0_DMA_CHAN );
-       bits |= 1 << ( GPC1_DMA_CHAN + 4 );
-       ni_writeb( bits, G0_G1_Select);
+       ni_writeb(devpriv->ai_ao_select_reg, AI_AO_Select);
+       ni_writeb(devpriv->g0_g1_select_reg, G0_G1_Select);
 
        if(boardtype.reg_type & ni_reg_6xxx_mask)
        {
@@ -4038,17 +4264,35 @@ static int ni_gpct_insn_write(comedi_device *dev, comedi_subdevice *s,
 
 static int ni_gpct_cmd(comedi_device *dev, comedi_subdevice *s)
 {
-       return 0;
+// XXX set M_Offset_GX_DMA_Config for m-series
+#ifdef PCIDMA
+       struct ni_gpct *counter = s->private;
+       const comedi_cmd *cmd = &s->async->cmd;
+       int retval = ni_request_gpct_mite_channel(dev, counter->counter_index);
+       if(retval)
+       {
+               comedi_error(dev, "no dma channel available for use by counter");
+               return retval;
+       }
+       return ni_tio_cmd(counter, s->async);
+#else
+       return -ENOTSUPP;
+#endif
 }
 
 static int ni_gpct_cmdtest(comedi_device *dev, comedi_subdevice *s, comedi_cmd *cmd)
 {
-       return 0;
+       struct ni_gpct *counter = s->private;
+       //XXX check chanlist_len == 1
+       return ni_tio_cmdtest(counter);
 }
 
 static int ni_gpct_cancel(comedi_device *dev, comedi_subdevice *s)
 {
-       return 0;
+       struct ni_gpct *counter = s->private;
+       int retval = ni_tio_cancel(counter);
+       ni_release_gpct_mite_channel(dev, counter->counter_index);
+       return retval;
 }
 
 /*