-\r
-/*\r
- comedi/drivers/ni_mio_common.c\r
- Hardware driver for DAQ-STC based boards\r
-\r
- COMEDI - Linux Control and Measurement Device Interface\r
- Copyright (C) 1997-2001 David A. Schleef <ds@schleef.org>\r
-\r
- This program is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- This program is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with this program; if not, write to the Free Software\r
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
-\r
-*/\r
-\r
-/*\r
- This file is meant to be included by another file, e.g.,\r
- ni_atmio.c or ni_pcimio.c.\r
-\r
- Interrupt support originally added by Truxton Fulton\r
- <trux@truxton.com>\r
-\r
- References (from ftp://ftp.natinst.com/support/manuals):\r
- \r
- 340747b.pdf AT-MIO E series Register Level Programmer Manual\r
- 341079b.pdf PCI E Series RLPM\r
- 340934b.pdf DAQ-STC reference manual\r
-\r
- Other possibly relevant info:\r
- \r
- 320517c.pdf User manual (obsolete)\r
- 320517f.pdf User manual (new)\r
- 320889a.pdf delete\r
- 320906c.pdf maximum signal ratings\r
- 321066a.pdf about 16x\r
- 321791a.pdf discontinuation of at-mio-16e-10 rev. c\r
- 321808a.pdf about at-mio-16e-10 rev P\r
- 321837a.pdf discontinuation of at-mio-16de-10 rev d\r
- 321838a.pdf about at-mio-16de-10 rev N\r
- \r
- ISSUES:\r
-\r
- - the interrupt routine needs to be cleaned up\r
- - many printk's need to be changed to rt_printk()\r
-*/\r
-\r
-//#define DEBUG_INTERRUPT\r
-//#define TRY_BLOCK\r
-#define DEBUG_STATUS_A\r
-//#define DEBUG_STATUS_B\r
-\r
-#include "8255.h"\r
-\r
-#ifndef MDPRINTK\r
-#define MDPRINTK(format,args...)\r
-#endif\r
-\r
-/* reference: ground, common, differential, other */\r
-static int ni_modebits1[4]={ 0x3000, 0x2000, 0x1000, 0 };\r
-static int ni_modebits2[4]={ 0x3f, 0x3f, 0x37, 0x37 };\r
-\r
-static int ni_gainlkup[][16]={\r
- { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },\r
- { 1, 2, 4, 7, 9, 10, 12, 15, 0,0,0,0,0,0,0,0 },\r
- { 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 0,0 },\r
- { 0, 1, 4, 7, 8, 9, 12, 15, 0, 0, 0, 0, 0, 0, 0, 0 },\r
- { 0, 1, 4, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }\r
-};\r
-\r
-static comedi_lrange range_ni_E_ai={ 16, {\r
- RANGE( -10, 10 ),\r
- RANGE( -5, 5 ),\r
- RANGE( -2.5, 2.5 ),\r
- RANGE( -1, 1 ),\r
- RANGE( -0.5, 0.5 ),\r
- RANGE( -0.25, 0.25 ),\r
- RANGE( -0.1, 0.1 ),\r
- RANGE( -0.05, 0.05 ),\r
- RANGE( 0, 20 ),\r
- RANGE( 0, 10 ),\r
- RANGE( 0, 5 ),\r
- RANGE( 0, 2 ),\r
- RANGE( 0, 1 ),\r
- RANGE( 0, 0.5 ),\r
- RANGE( 0, 0.2 ),\r
- RANGE( 0, 0.1 ),\r
-}};\r
-static comedi_lrange range_ni_E_ai_limited={ 8, {\r
- RANGE( -10, 10 ),\r
- RANGE( -5, 5 ),\r
- RANGE( -1, 1 ),\r
- RANGE( -0.1, 0.1 ),\r
- RANGE( 0, 10 ),\r
- RANGE( 0, 5 ),\r
- RANGE( 0, 1 ),\r
- RANGE( 0, 0.1 ),\r
-}};\r
-static comedi_lrange range_ni_E_ai_limited14={ 14, {\r
- RANGE( -10, 10 ),\r
- RANGE( -5, 5 ),\r
- RANGE( -2, 2 ),\r
- RANGE( -1, 1 ),\r
- RANGE( -0.5, 0.5 ),\r
- RANGE( -0.2, 0.2 ),\r
- RANGE( -0.1, 0.1 ),\r
- RANGE( 0, 10 ),\r
- RANGE( 0, 5 ),\r
- RANGE( 0, 2 ),\r
- RANGE( 0, 1 ),\r
- RANGE( 0, 0.5 ),\r
- RANGE( 0, 0.2 ),\r
- RANGE( 0, 0.1 ),\r
-}};\r
-static comedi_lrange range_ni_E_ai_bipolar4={ 4, {\r
- RANGE( -10, 10 ),\r
- RANGE( -5, 5 ),\r
- RANGE( -0.5, 0.5 ),\r
- RANGE( -0.05, 0.05 ),\r
-}};\r
-#if 0\r
-static comedi_lrange range_ni_E_ao = { 2, {\r
- RANGE( -10, 10 ),\r
- RANGE( 0, 10 ),\r
-}};\r
-#endif\r
-static comedi_lrange range_ni_E_ao_ext = { 4, {\r
- RANGE( -10, 10 ),\r
- RANGE( 0, 10 ),\r
- RANGE_ext( -1, 1 ),\r
- RANGE_ext( 0, 1 ),\r
-}};\r
-\r
-static comedi_lrange *ni_range_lkup[]={\r
- &range_ni_E_ai,\r
- &range_ni_E_ai_limited,\r
- &range_ni_E_ai_limited14,\r
- &range_ni_E_ai_bipolar4,\r
-};\r
-\r
-\r
-\r
-static int ni_dio_insn_config(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data);\r
-static int ni_dio_insn_bits(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data);\r
-\r
-static int ni_calib_insn_read(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data);\r
-static int ni_calib_insn_write(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data);\r
-\r
-static int ni_eeprom_insn_read(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data);\r
-\r
-static void caldac_setup(comedi_device *dev,comedi_subdevice *s);\r
-static int ni_read_eeprom(comedi_device *dev,int addr);\r
-\r
-#ifdef DEBUG_STATUS_A\r
-static void ni_mio_print_status_a(int status);\r
-#else\r
-#define ni_mio_print_status_a(a)\r
-#endif\r
-#ifdef DEBUG_STATUS_B\r
-static void ni_mio_print_status_b(int status);\r
-#else\r
-#define ni_mio_print_status_b(a)\r
-#endif\r
-\r
-static int ni_ai_reset(comedi_device *dev,comedi_subdevice *s);\r
-static void ni_handle_fifo_half_full(comedi_device *dev);\r
-static void ni_handle_fifo_dregs(comedi_device *dev);\r
-#ifdef TRY_BLOCK\r
-static void ni_handle_block(comedi_device *dev);\r
-#endif\r
-#ifdef PCIDMA\r
-static void ni_handle_block_dma(comedi_device *dev);\r
-#endif\r
-static int ni_ai_inttrig(comedi_device *dev,comedi_subdevice *s,\r
- unsigned int trignum);\r
-\r
-static int ni_ao_fifo_half_empty(comedi_device *dev,comedi_subdevice *s);\r
-\r
-static int ni_8255_callback(int dir,int port,int data,void *arg);\r
-\r
-static int ni_ns_to_timer(int *nanosec,int round_mode);\r
-\r
-//static int gpct_setup(comedi_device *dev,comedi_subdevice *s);\r
-static int ni_gpct_insn_config(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data);\r
-static int ni_gpct_insn_read(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data);\r
-\r
-static void pfi_setup(comedi_device *dev);\r
-\r
-/*GPCT function def's*/\r
-int GPCT_G_Watch(comedi_device *dev, int chan);\r
-\r
-void GPCT_Reset(comedi_device *dev, int chan);\r
-void GPCT_Gen_Cont_Pulse(comedi_device *dev, int chan, unsigned int length);\r
-void GPCT_Gen_Single_Pulse(comedi_device *dev, int chan, unsigned int length);\r
-void GPCT_Period_Meas(comedi_device *dev, int chan);\r
-void GPCT_Pulse_Width_Meas(comedi_device *dev, int chan);\r
-void GPCT_Event_Counting(comedi_device *dev,int chan);\r
-int GPCT_Set_Direction(comedi_device *dev,int chan,int direction);\r
-int GPCT_Set_Gate(comedi_device *dev,int chan ,int gate);\r
-int GPCT_Set_Source(comedi_device *dev,int chan ,int source);\r
-\r
-static int ni_gpct_insn_write(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data);\r
-static int ni_gpct_insn_read(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data);\r
-static int ni_gpct_insn_config(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data);\r
-\r
-#undef DEBUG\r
-\r
-#define AIMODE_NONE 0\r
-#define AIMODE_HALF_FULL 1\r
-#define AIMODE_SCAN 2\r
-#define AIMODE_SAMPLE 3\r
-\r
-static void handle_a_interrupt(comedi_device *dev,unsigned short status);\r
-static void handle_b_interrupt(comedi_device *dev,unsigned short status);\r
-#ifdef PCIDMA\r
-/*status must be long because the CHSR is 32 bits and the high bits\r
-are important to us */\r
-static void mite_handle_interrupt(comedi_device *dev,unsigned long status);\r
-void ni_munge(comedi_device *dev,comedi_subdevice *s,sampl_t *start, sampl_t *stop);\r
-#endif\r
-\r
-/* ni_set_bits( ) allows different parts of the ni_mio_common driver to \r
-* share registers (such as Interrupt_A_Register) without interfering with\r
-* each other. Use comedi_spin_lock_irqsave() and comedi_spin_unlock_irqrestore()\r
-* if you use this to modify the interrupt enable registers...which are sometimes\r
-* changed in ISRs\r
-*\r
-* NOTE: the switch/case statements are optimized out for a constant argument\r
-* so this is actually quite fast--- If you must wrap another function around this\r
-* make it inline to avoid a large speed penalty.\r
-*\r
-* value should only be 1 or 0.\r
-*/\r
-void inline ni_set_bits(comedi_device *dev, int reg, int bits, int value) {\r
- \r
- switch (reg){\r
- case Interrupt_A_Enable_Register:\r
- if(value)\r
- devpriv->int_a_enable_reg |= bits;\r
- else\r
- devpriv->int_a_enable_reg &= ~bits;\r
- win_out(devpriv->int_a_enable_reg,Interrupt_A_Enable_Register);\r
- break;\r
- case Interrupt_B_Enable_Register:\r
- if(value)\r
- devpriv->int_b_enable_reg |= bits;\r
- else\r
- devpriv->int_b_enable_reg &= ~bits;\r
- win_out(devpriv->int_b_enable_reg,Interrupt_B_Enable_Register);\r
- break;\r
- case IO_Bidirection_Pin_Register:\r
- if(value)\r
- devpriv->io_bidirection_pin_reg |= bits;\r
- else\r
- devpriv->io_bidirection_pin_reg &= ~bits;\r
- win_out(devpriv->io_bidirection_pin_reg,IO_Bidirection_Pin_Register);\r
- break;\r
- default:\r
- printk("Warning ni_set_bits() called with invalid arguments\n");\r
- printk("reg is %d\n",reg);\r
- break;\r
- }\r
-}\r
-\r
-\r
-static\r
-void ni_E_interrupt(int irq,void *d,struct pt_regs * regs)\r
-{\r
- comedi_device *dev=d;\r
- unsigned short a_status;\r
- unsigned short b_status;\r
- int wsave;\r
-#ifdef PCIDMA\r
- /* m_status must be long because the CHSR is a 32 bit register and we are\r
- interested in several high bits */\r
- unsigned long m_status;\r
-#endif\r
-\r
- MDPRINTK("ni_E_Interrupt\n");\r
-/*\r
- If you want to use windowed registers in an interrupt, it is\r
- important that you restore the window address register. If\r
- you change certain modes, e.g., AI_Configuration_Start/End,\r
- you need to set up software flags for non-interrupt routines.\r
-*/\r
- wsave=win_save();\r
- \r
- a_status=ni_readw(AI_Status_1);\r
- b_status=ni_readw(AO_Status_1);\r
-#ifdef PCIDMA\r
- m_status=readl(devpriv->mite->mite_io_addr+MITE_CHSR+CHAN_OFFSET(0));\r
-#endif\r
-#ifdef DEBUG_INTERRUPT\r
- rt_printk("ni_mio_common: interrupt: a_status=%04x b_status=%04x\n",\r
- a_status,b_status);\r
- ni_mio_print_status_a(a_status);\r
- ni_mio_print_status_b(b_status);\r
-#endif\r
-#ifdef PCIDMA\r
- //rt_printk("mite status=0x%08lx\n",m_status);\r
- if(m_status&CHSR_INT)mite_handle_interrupt(dev,m_status);\r
-#endif\r
- if(a_status&Interrupt_A_St)handle_a_interrupt(dev,a_status);\r
- if(b_status&Interrupt_B_St)handle_b_interrupt(dev,b_status);\r
- \r
- win_restore(wsave);\r
- MDPRINTK("exit ni_E_Interrupt\n");\r
-}\r
-\r
-#ifdef PCIDMA\r
-static void mite_handle_interrupt(comedi_device *dev,unsigned long m_status)\r
-{\r
- comedi_subdevice *s=dev->subdevices+0;\r
- \r
- comedi_event(dev,s,COMEDI_CB_BLOCK);\r
-\r
- MDPRINTK("mite_handle_interrupt\n");\r
- writel(CHOR_CLRLC, devpriv->mite->mite_io_addr+MITE_CHOR+CHAN_OFFSET(0));\r
-#if 0\r
- //Don't munge the data, just update the user's status variables\r
- s->async->buf_int_count=mite_bytes_transferred(devpriv->mite, 0);\r
- s->async->buf_int_ptr= s->async->buf_int_count % s->async->prealloc_bufsz; \r
-#else\r
- //Munge the ADC data to change its format from twos complement to unsigned int\r
- //This is slow but makes it more compatible with other cards\r
- { \r
- unsigned int raw_ptr;\r
- s->async->buf_int_count = mite_bytes_transferred(devpriv->mite, 0);\r
- raw_ptr = s->async->buf_int_count % s->async->prealloc_bufsz;\r
- if(s->async->buf_int_ptr > raw_ptr) {\r
- ni_munge(dev,s,s->async->buf_int_ptr+s->async->prealloc_buf, \r
- s->async->prealloc_buf+s->async->prealloc_bufsz);\r
- s->async->buf_int_ptr = 0;\r
- }\r
- ni_munge(dev,s,s->async->buf_int_ptr+s->async->prealloc_buf, \r
- raw_ptr+s->async->prealloc_buf);\r
- s->async->buf_int_ptr = raw_ptr;\r
- }\r
-#endif\r
- MDPRINTK("CHSR is 0x%08lx, count is %d\n",m_status,s->async->buf_int_count);\r
- if(m_status&CHSR_DONE){\r
- writel(CHOR_CLRDONE, devpriv->mite->mite_io_addr+MITE_CHOR+CHAN_OFFSET(0));\r
- //printk("buf_int_count is %d, buf_int_ptr is %d\n",\r
- // s->async->buf_int_count,s->async->buf_int_ptr);\r
- ni_handle_block_dma(dev);\r
- } \r
- MDPRINTK("exit mite_handle_interrupt\n");\r
- return; \r
-} \r
-\r
-#endif //PCIDMA\r
-\r
-static void handle_a_interrupt(comedi_device *dev,unsigned short status)\r
-{\r
- comedi_subdevice *s=dev->subdevices+0;\r
- unsigned short ack=0;\r
-\r
- s->async->events = 0;\r
-\r
- /* uncommon interrupt events */\r
- if(status&(AI_Overrun_St|AI_Overflow_St|AI_SC_TC_Error_St|AI_SC_TC_St|AI_START1_St)){\r
- if(status==0xffff){\r
- rt_printk("ni_mio_common: a_status=0xffff. Card removed?\n");\r
- /* we probably aren't even running a command now,\r
- * so it's a good idea to be careful. */\r
- if(s->subdev_flags&SDF_RUNNING)comedi_done(dev,s);\r
- return;\r
- }\r
- if(status&(AI_Overrun_St|AI_Overflow_St|AI_SC_TC_Error_St)){\r
- rt_printk("ni_mio_common: ai error a_status=%04x\n",\r
- status);\r
- ni_mio_print_status_a(status);\r
- \r
- //TIM 5/11/01\r
- win_out(AI_Error_Interrupt_Ack, Interrupt_A_Ack_Register);\r
- \r
- #ifndef PCIDMA\r
- ni_handle_fifo_dregs(dev);\r
- #endif \r
- \r
- //TIM 4/17/01\r
- //win_out(0x0000,Interrupt_A_Enable_Register);\r
- //turn off all AI interrupts\r
- ni_set_bits(dev, Interrupt_A_Enable_Register,\r
- AI_SC_TC_Interrupt_Enable | AI_START1_Interrupt_Enable|\r
- AI_START2_Interrupt_Enable| AI_START_Interrupt_Enable|\r
- AI_STOP_Interrupt_Enable| AI_Error_Interrupt_Enable|\r
- AI_FIFO_Interrupt_Enable,0);\r
- \r
- ni_ai_reset(dev,dev->subdevices);//added by tim\r
- comedi_done(dev,s);\r
- return;\r
- }\r
- if(status&AI_SC_TC_St){\r
-#ifdef DEBUG_INTERRUPT\r
- rt_printk("ni_mio_common: SC_TC interrupt\n");\r
-#endif\r
-#ifdef TRY_BLOCK\r
- ni_handle_block(dev);\r
-#else\r
- //for MITE DMA ignore the terminal count from the STC\r
- //instead finish up when the MITE asserts DONE\r
-#ifndef PCIDMA\r
- \r
- if(!devpriv->ai_continuous){\r
- ni_handle_fifo_dregs(dev);\r
- //win_out(0x0000,Interrupt_A_Enable_Register); TIM 4/17/01\r
- ni_set_bits(dev, Interrupt_A_Enable_Register,\r
- AI_SC_TC_Interrupt_Enable | AI_START1_Interrupt_Enable|\r
- AI_START2_Interrupt_Enable| AI_START_Interrupt_Enable|\r
- AI_STOP_Interrupt_Enable| AI_Error_Interrupt_Enable|\r
- AI_FIFO_Interrupt_Enable,0);\r
-\r
- comedi_done(dev,s);\r
- }\r
-#endif //PCIDMA\r
-#endif //TRY_BLOCK\r
- ack|=AI_SC_TC_Interrupt_Ack;\r
- }\r
- if(status&AI_START1_St){\r
- ack|=AI_START1_Interrupt_Ack;\r
- }\r
- }\r
-#ifndef PCIDMA\r
- if(status&AI_FIFO_Half_Full_St){\r
- ni_handle_fifo_half_full(dev);\r
- }\r
-#endif //PCIDMA\r
- if(devpriv->aimode==AIMODE_SCAN && status&AI_STOP_St){\r
- ni_handle_fifo_dregs(dev);\r
-\r
- s->async->events |= COMEDI_CB_EOS;\r
-\r
- /* we need to ack the START, also */\r
- ack|=AI_STOP_Interrupt_Ack|AI_START_Interrupt_Ack;\r
- }\r
- if(devpriv->aimode==AIMODE_SAMPLE){\r
- ni_handle_fifo_dregs(dev);\r
-\r
- //s->async->events |= COMEDI_CB_SAMPLE;\r
- }\r
-\r
- if(ack) ni_writew(ack,Interrupt_A_Ack);\r
-\r
- comedi_event(dev,s,s->async->events);\r
-}\r
-\r
-static void handle_b_interrupt(comedi_device *dev,unsigned short b_status)\r
-{\r
- comedi_subdevice *s=dev->subdevices+1;\r
- //unsigned short ack=0;\r
-\r
- if(b_status==0xffff)return;\r
- if(b_status&AO_Overrun_St){\r
- rt_printk("ni-E: AO FIFO underrun status=0x%04x status2=0x%04x\n",b_status,ni_readw(AO_Status_2));\r
- }\r
-\r
- if(b_status&AO_BC_TC_St){\r
- rt_printk("ni-E: AO BC_TC status=0x%04x status2=0x%04x\n",b_status,ni_readw(AO_Status_2));\r
- }\r
-\r
- if(b_status&AO_FIFO_Request_St)\r
- ni_ao_fifo_half_empty(dev,s);\r
-\r
- b_status=ni_readw(AO_Status_1);\r
- if(b_status&Interrupt_B_St){\r
- if(b_status&AO_FIFO_Request_St){\r
- rt_printk("ni_mio_common: AO buffer underrun\n");\r
- }\r
- rt_printk("Ack! didn't clear AO interrupt. b_status=0x%04x\n",b_status);\r
- win_out(0,Interrupt_B_Enable_Register);\r
- }\r
-}\r
-\r
-#ifdef DEBUG_STATUS_A\r
-static char *status_a_strings[]={\r
- "passthru0","fifo","G0_gate","G0_TC",\r
- "stop","start","sc_tc","start1",\r
- "start2","sc_tc_error","overflow","overrun",\r
- "fifo_empty","fifo_half_full","fifo_full","interrupt_a"\r
-};\r
-\r
-static void ni_mio_print_status_a(int status)\r
-{\r
- int i;\r
-\r
- rt_printk("A status:");\r
- for(i=15;i>=0;i--){\r
- if(status&(1<<i)){\r
- rt_printk(" %s",status_a_strings[i]);\r
- }\r
- }\r
- rt_printk("\n");\r
-}\r
-#endif\r
-\r
-#ifdef DEBUG_STATUS_B\r
-static char *status_b_strings[]={\r
- "passthru1","fifo","G1_gate","G1_TC",\r
- "UI2_TC","UPDATE","UC_TC","BC_TC",\r
- "start1","overrun","start","bc_tc_error",\r
- "fifo_empty","fifo_half_full","fifo_full","interrupt_b"\r
-};\r
-\r
-static void ni_mio_print_status_b(int status)\r
-{\r
- int i;\r
-\r
- rt_printk("B status:");\r
- for(i=15;i>=0;i--){\r
- if(status&(1<<i)){\r
- rt_printk(" %s",status_b_strings[i]);\r
- }\r
- }\r
- rt_printk("\n");\r
-}\r
-#endif\r
-\r
-static void ni_ai_fifo_read(comedi_device *dev,comedi_subdevice *s,\r
- sampl_t *data,int n)\r
-{\r
- comedi_async *async = s->async;\r
- int i,j;\r
- sampl_t d;\r
- unsigned int mask;\r
-\r
- mask=(1<<boardtype.adbits)-1;\r
- j=async->cur_chan;\r
- for(i=0;i<n;i++){\r
- d=ni_readw(ADC_FIFO_Data_Register);\r
- d^=devpriv->ai_xorlist[j];\r
- d&=mask;\r
- data[i]=d;\r
- j++;\r
- if(j>=async->cur_chanlist_len){\r
- j=0;\r
- async->events |= COMEDI_CB_EOS;\r
- }\r
- }\r
- async->cur_chan=j;\r
-}\r
-\r
-#ifdef PCIDMA\r
-void ni_munge(comedi_device *dev,comedi_subdevice *s,sampl_t *start, sampl_t *stop)\r
-{\r
- comedi_async *async = s->async;\r
- int j;\r
- sampl_t *i;\r
- unsigned int mask;\r
-\r
- mask=(1<<boardtype.adbits)-1;\r
- j=async->cur_chan;\r
- for(i=start;i<stop;i++){\r
- *i ^=devpriv->ai_xorlist[j];\r
- *i &=mask;\r
- j++;\r
- j %= async->cur_chanlist_len;\r
- }\r
- async->cur_chan=j;\r
-}\r
-\r
-static void ni_handle_block_dma(comedi_device *dev)\r
-{\r
- MDPRINTK("ni_handle_block_dma\n");\r
- //mite_dump_regs(devpriv->mite); \r
- mite_dma_disarm(devpriv->mite);\r
- //TIM 4/17/01 win_out(0x0000,Interrupt_A_Enable_Register);\r
- ni_set_bits(dev, Interrupt_A_Enable_Register,\r
- AI_SC_TC_Interrupt_Enable | AI_START1_Interrupt_Enable|\r
- AI_START2_Interrupt_Enable| AI_START_Interrupt_Enable|\r
- AI_STOP_Interrupt_Enable| AI_Error_Interrupt_Enable|\r
- AI_FIFO_Interrupt_Enable,0);\r
-\r
- ni_ai_reset(dev,dev->subdevices);\r
- comedi_done(dev,dev->subdevices);\r
- MDPRINTK("exit ni_handle_block_dma\n");\r
-}\r
-#endif\r
-\r
-#ifdef TRY_BLOCK\r
-/* Blocked mode is used to get interrupts at convenient places\r
- * to do DMA. It is also useful when you want to count greater\r
- * than 16M scans.\r
- */\r
-static void ni_handle_block(comedi_device *dev)\r
-{\r
- int n;\r
-\r
- if(devpriv->ai_continuous){\r
- n = devpriv->blocksize;\r
- }else{\r
- if(devpriv->n_left==0){\r
- ni_handle_fifo_dregs(dev);\r
- printk("end\n");\r
- //TIM 4/17/01 win_out(0x0000,Interrupt_A_Enable_Register);\r
- ni_set_bits(dev, Interrupt_A_Enable_Register,\r
- AI_SC_TC_Interrupt_Enable | AI_Start1_Interrupt_Enable|\r
- AI_Start2_Interrupt_Enable| AI_Start_Interrupt_Enable|\r
- AI_Stop_Interrupt_Enable| AI_Error_Interrupt_Enable|\r
- AI_FIFO_Interrupt_Enable,0);\r
- ni_ai_reset(dev,dev->subdevices);\r
- comedi_done(dev,dev->subdevices);\r
- }else if(devpriv->n_left<=devpriv->blocksize){\r
- printk("last block %d\n",devpriv->n_left);\r
- n = devpriv->n_left;\r
- devpriv->n_left = 0;\r
- }else{\r
- printk("block %d\n",devpriv->n_left);\r
- n = devpriv->blocksize;\r
- devpriv->n_left -= devpriv->blocksize;\r
- }\r
- }\r
-#if 0\r
- {\r
- int size=0x10000;\r
-\r
- /* stage number of scans */\r
- win_out((size-1)>>16,AI_SC_Load_A_Registers);\r
- win_out((size-1)&0xffff,AI_SC_Load_A_Registers+1);\r
-\r
- //mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Continuous;\r
- mode1 |= 0xe;\r
- win_out(mode1,AI_Mode_1_Register);\r
-\r
- /* load SC (Scan Count) */\r
- win_out(AI_SC_Load,AI_Command_1_Register);\r
-\r
- }\r
-#endif\r
-}\r
-#endif\r
-\r
-static void ni_handle_fifo_half_full(comedi_device *dev)\r
-{\r
- int n,m;\r
- comedi_subdevice *s=dev->subdevices+0;\r
- comedi_async *async=s->async;\r
-\r
- /*\r
- if we got a fifo_half_full interrupt, we can transfer fifo/2\r
- samples without checking the empty flag. It doesn't matter if\r
- we transfer the rest of the samples, the performance trade-off\r
- is minimal (checking empty flag for a few samples vs. having\r
- 1% more interrupts.) At really high speeds, it's better to\r
- ignore them.\r
-\r
- */\r
-\r
- n=boardtype.ai_fifo_depth/2;\r
-\r
- /* this makes the assumption that the buffer length is\r
- greater than the half-fifo depth. */\r
-\r
- if(async->buf_int_ptr+n*sizeof(sampl_t)>=async->data_len){\r
- m=(async->data_len-async->buf_int_ptr)/sizeof(sampl_t);\r
- ni_ai_fifo_read(dev,s,async->data+async->buf_int_ptr,m);\r
- async->buf_int_count+=m*sizeof(sampl_t);\r
- n-=m;\r
- async->buf_int_ptr=0;\r
-\r
- async->events |= COMEDI_CB_EOBUF;\r
- }\r
- ni_ai_fifo_read(dev,s,async->data+async->buf_int_ptr,n);\r
- async->buf_int_count+=n*sizeof(sampl_t);\r
- async->buf_int_ptr+=n*sizeof(sampl_t);\r
-\r
- async->events |= COMEDI_CB_BLOCK;\r
-}\r
-\r
-/*\r
- Empties the AI fifo\r
-*/\r
-static void ni_handle_fifo_dregs(comedi_device *dev)\r
-{\r
- comedi_subdevice *s=dev->subdevices+0;\r
- sampl_t *data,d;\r
- int i,n;\r
- int j;\r
- unsigned int mask;\r
-\r
- mask=(1<<boardtype.adbits)-1;\r
- j=s->async->cur_chan;\r
- data=s->async->data+s->async->buf_int_ptr;\r
- while(1){\r
- n=(s->async->data_len-s->async->buf_int_ptr)/sizeof(sampl_t);\r
- for(i=0;i<n;i++){\r
- if(ni_readw(AI_Status_1)&AI_FIFO_Empty_St){\r
- s->async->cur_chan=j;\r
- return;\r
- }\r
- d=ni_readw(ADC_FIFO_Data_Register);\r
- d^=devpriv->ai_xorlist[j];\r
- d&=mask;\r
- *data=d;\r
- j++;\r
- if(j>=s->async->cur_chanlist_len){\r
- j=0;\r
- //s->events |= COMEDI_CB_EOS;\r
- }\r
- data++;\r
- s->async->buf_int_ptr+=sizeof(sampl_t);\r
- s->async->buf_int_count+=sizeof(sampl_t);\r
- }\r
- s->async->buf_int_ptr=0;\r
- data=s->async->data;\r
- s->async->events |= COMEDI_CB_EOBUF;\r
- }\r
-}\r
-\r
-#ifdef PCIDMA\r
-int ni_ai_setup_block_dma(comedi_device *dev,int frob,int mode1)\r
-{\r
- int n;\r
- int len;\r
- unsigned long ll_start;\r
- comedi_cmd *cmd=&dev->subdevices->async->cmd;\r
- \r
- MDPRINTK("ni_ai_setup_block_dma\n");\r
- \r
- /*Build MITE linked list and configure the MITE\r
- * ******WARNING******\r
- * There is no error handling here, \r
- * the memory buffer *Must* be mlock'ed by the user*/\r
-\r
- len = sizeof(sampl_t)*cmd->stop_arg*cmd->scan_end_arg;\r
-\r
- /*use kvmem if no user buf specified */\r
- ll_start = mite_ll_from_kvmem(devpriv->mite,dev->subdevices->async,\r
- len);\r
-\r
- mite_setregs(devpriv->mite, ll_start,0,COMEDI_INPUT);\r
-\r
- /*tell the STC to use DMA0 for AI. \r
- * Select the MITE DMA channel to use, 0x01=A*/\r
- ni_writeb(0x01,AI_AO_Select);\r
-\r
- /* stage number of scans */\r
- n = cmd->stop_arg;\r
- win_out((n-1)>>16,AI_SC_Load_A_Registers);\r
- win_out((n-1)&0xffff,AI_SC_Load_A_Registers+1);\r
- win_out((n-1)>>16,AI_SC_Load_B_Registers);\r
- win_out((n-1)&0xffff,AI_SC_Load_B_Registers+1);\r
- \r
- /* load SC (Scan Count) */\r
- win_out(AI_SC_Load,AI_Command_1_Register);\r
-\r
- mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Continuous;\r
- win_out(mode1,AI_Mode_1_Register);\r
- \r
- /*start the MITE*/\r
- mite_dma_arm(devpriv->mite);\r
- \r
- MDPRINTK("exit ni_ai_setup_block_dma\n");\r
-\r
- return mode1;\r
-}\r
-#endif\r
-\r
-#ifdef TRY_BLOCK\r
-int ni_ai_setup_block(comedi_device *dev,int frob,int mode1)\r
-{\r
- int n;\r
- int last=0;\r
-\r
-printk("n_left = %d\n",devpriv->n_left);\r
- if(devpriv->ai_continuous){\r
- n=devpriv->blocksize;\r
- last=0;\r
- }else{\r
- n=devpriv->n_left;\r
- if(n>devpriv->blocksize){\r
- n=devpriv->blocksize;\r
- last=0;\r
- }else{\r
- last=1;\r
- }\r
- devpriv->n_left -= n;\r
- }\r
-\r
- if(frob){\r
- /* stage number of scans */\r
- win_out((n-1)>>16,AI_SC_Load_A_Registers);\r
- win_out((n-1)&0xffff,AI_SC_Load_A_Registers+1);\r
- win_out((n-1)>>16,AI_SC_Load_B_Registers);\r
- win_out((n-1)&0xffff,AI_SC_Load_B_Registers+1);\r
-\r
- /* load SC (Scan Count) */\r
- win_out(AI_SC_Load,AI_Command_1_Register);\r
-#if 0\r
- if(!last){\r
- mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Continuous;\r
- }else{\r
- mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Trigger_Once;\r
- }\r
-#endif\r
- mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Continuous;\r
- win_out(mode1,AI_Mode_1_Register);\r
-\r
- }\r
-\r
- return mode1;\r
-}\r
-#endif\r
-\r
-#ifdef PCIDMA\r
-int ni_ai_setup_MITE_dma(comedi_device *dev,comedi_cmd *cmd,int mode1)\r
-{\r
- int n,len;\r
- unsigned long ll_start;\r
- comedi_async *async_mite;\r
- \r
- len = sizeof(sampl_t)*cmd->stop_arg*cmd->scan_end_arg;\r
- async_mite=dev->subdevices[cmd->subdev].async;\r
- ll_start = mite_ll_from_kvmem(devpriv->mite, async_mite,len);\r
- mite_setregs(devpriv->mite, ll_start,0,COMEDI_INPUT);\r
-\r
- /*tell the STC to use DMA0 for AI.\r
- Select the MITE DMA channel to use, 0x01=A*/\r
- ni_writeb(0x01,AI_AO_Select);\r
-\r
- /* stage number of scans */\r
- n = cmd->stop_arg;\r
- win_out((n-1)>>16,AI_SC_Load_A_Registers);\r
- win_out((n-1)&0xffff,AI_SC_Load_A_Registers+1);\r
- win_out((n-1)>>16,AI_SC_Load_B_Registers);\r
- win_out((n-1)&0xffff,AI_SC_Load_B_Registers+1);\r
- \r
- /* load SC (Scan Count) */\r
- win_out(AI_SC_Load,AI_Command_1_Register);\r
-\r
- mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Continuous;\r
- win_out(mode1,AI_Mode_1_Register);\r
- \r
- /*start the MITE*/\r
- mite_dma_arm(devpriv->mite);\r
- return mode1;\r
-}\r
-#endif\r
-\r
-/*\r
- used for both cancel ioctl and board initialization\r
-\r
- this is pretty harsh for a cancel, but it works...\r
- */\r
-static int ni_ai_reset(comedi_device *dev,comedi_subdevice *s)\r
-{\r
-#ifdef PCIDMA\r
- mite_dma_disarm(devpriv->mite);\r
-#endif\r
- //TIM 4/17/01 win_out(0x0000,Interrupt_A_Enable_Register);\r
- ni_set_bits(dev, Interrupt_A_Enable_Register,\r
- AI_SC_TC_Interrupt_Enable | AI_START1_Interrupt_Enable|\r
- AI_START2_Interrupt_Enable| AI_START_Interrupt_Enable|\r
- AI_STOP_Interrupt_Enable| AI_Error_Interrupt_Enable|\r
- AI_FIFO_Interrupt_Enable,0);\r
-\r
- win_out(AI_Reset,Joint_Reset_Register);\r
-\r
- win_out(1,ADC_FIFO_Clear);\r
-\r
- /* ai configuration */\r
-\r
- win_out(AI_Configuration_Start,Joint_Reset_Register);\r
-\r
- win_out(0x0000,AI_Command_1_Register); /* reset pulses */\r
- win_out(0x000d,AI_Mode_1_Register);\r
- win_out(0x0000,AI_Mode_2_Register);\r
-#if 0\r
- win_out((1<<6)|0x0000,AI_Mode_3_Register); /* generate FIFO interrupts on half full */\r
-#else\r
- win_out((0<<6)|0x0000,AI_Mode_3_Register); /* generate FIFO interrupts on non-empty */\r
-#endif\r
- /* TIM 5/11/01 \r
- 0xA4A0 causes overrun errors at high speeds. 0xA420 fixes it,\r
- but I haven't tested to see if it breaks something else. I don't think it would*/\r
- #ifdef PCIDMA \r
- win_out(0xA420,AI_Personal_Register); \r
- #else\r
- win_out(0xa4a0,AI_Personal_Register); /* ? */\r
- #endif\r
- win_out(0x032e,AI_Output_Control_Register);\r
- win_out(0x0060,AI_Trigger_Select_Register); /* trigger source */\r
-\r
- /* this should be done in _ai_modeX() */\r
- win_out(0x29e0,AI_START_STOP_Select_Register);\r
-\r
- /* the following registers should not be changed, because there\r
- * are no backup registers in devpriv. If you want to change\r
- * any of these, add a backup register and other appropriate code:\r
- * Clock_and_FOUT_Register\r
- * AI_Mode_1_Register\r
- * AI_Mode_3_Register\r
- * AI_Personal_Register\r
- * AI_Output_Control_Register\r
- * AI_Trigger_Select_Register\r
- */\r
- win_out(0x3f80,Interrupt_A_Ack_Register); /* clear interrupts */\r
-\r
- win_out(AI_Configuration_End,Joint_Reset_Register);\r
-\r
- return 0;\r
-}\r
-\r
-static int ni_ai_poll(comedi_device *dev,comedi_subdevice *s)\r
-{\r
- unsigned long flags;\r
-\r
- comedi_spin_lock_irqsave(&dev->spinlock,flags);\r
- ni_handle_fifo_dregs(dev);\r
- comedi_spin_unlock_irqrestore(&dev->spinlock,flags);\r
-\r
- comedi_event(dev,s,s->async->events);\r
-\r
- return s->async->buf_int_count-s->async->buf_user_count;\r
-}\r
-\r
-static void ni_load_channelgain_list(comedi_device *dev,unsigned int n_chan,unsigned int *list,int dither);\r
-\r
-#define NI_TIMEOUT 1000\r
-\r
-\r
-static int ni_ai_insn_read(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)\r
-{\r
- int i,n;\r
- int wsave;\r
- unsigned int mask,sign;\r
-\r
- wsave=win_save();\r
-\r
- win_out(1,ADC_FIFO_Clear);\r
-\r
- /* interrupt on errors */\r
- //TIM 4/17/01 win_out(0x0020,Interrupt_A_Enable_Register);\r
- ni_set_bits(dev, Interrupt_A_Enable_Register, AI_Error_Interrupt_Enable,1);\r
-\r
-\r
- //ni_load_channelgain_list(dev,1,&insn->chanspec,(insn->flags&TRIG_DITHER)==TRIG_DITHER);\r
- ni_load_channelgain_list(dev,1,&insn->chanspec,0);\r
-\r
-#if 0\r
-#define NI_TIMEOUT 1000\r
-#endif\r
- mask=(1<<boardtype.adbits)-1;\r
- sign=devpriv->ai_xorlist[0];\r
- for(n=0;n<insn->n;n++){\r
- win_out(1,AI_Command_1_Register);\r
- for(i=0;i<NI_TIMEOUT;i++){\r
- if(!(ni_readw(AI_Status_1)&AI_FIFO_Empty_St))\r
- break;\r
- }\r
- if(i==NI_TIMEOUT){\r
- rt_printk("ni_E: timeout 2\n");\r
- win_restore(wsave);\r
- return -ETIME;\r
- }\r
- data[n]=(ni_readw(ADC_FIFO_Data_Register)&mask)^sign;\r
- }\r
- win_restore(wsave);\r
- return insn->n;\r
-}\r
-\r
-\r
-static void ni_load_channelgain_list(comedi_device *dev,unsigned int n_chan,unsigned int *list,int dither)\r
-{\r
- unsigned int chan,range,aref;\r
- unsigned int i;\r
- unsigned int hi,lo;\r
- unsigned short sign;\r
-\r
- if(n_chan==1){\r
- if(devpriv->changain_state && devpriv->changain_spec==list[0]){\r
- // ready to go.\r
- return;\r
- }\r
- devpriv->changain_state=1;\r
- devpriv->changain_spec=list[0];\r
- }else{\r
- devpriv->changain_state=0;\r
- }\r
-\r
- win_out(1,Configuration_Memory_Clear);\r
-\r
- sign=1<<(boardtype.adbits-1);\r
- for(i=0;i<n_chan;i++){\r
- chan=CR_CHAN(list[i]);\r
- range=CR_RANGE(list[i]);\r
- aref=CR_AREF(list[i]);\r
-\r
- /* fix the external/internal range differences */\r
- range=ni_gainlkup[boardtype.gainlkup][range];\r
- devpriv->ai_xorlist[i]=(range<8)?sign:0;\r
-\r
- hi=ni_modebits1[aref]|(chan&ni_modebits2[aref]);\r
- ni_writew(hi,Configuration_Memory_High);\r
-\r
- lo=((i==n_chan-1)?0x8000:0) | ((range&0x8)<<5) | (range&0x7) | (dither<<9);\r
- ni_writew(lo,Configuration_Memory_Low);\r
- }\r
-\r
- /* prime the channel/gain list */\r
-\r
- win_out(1,AI_Command_1_Register);\r
- for(i=0;i<1000;i++){\r
- if(!(ni_readw(AI_Status_1)&AI_FIFO_Empty_St)){\r
- win_out(1,ADC_FIFO_Clear);\r
- return;\r
- }\r
- //udelay(25);\r
- }\r
- rt_printk("ni_E: timeout 1\n");\r
-}\r
-\r
-#define TIMER_BASE 50 /* 20 Mhz base */\r
-\r
-static int ni_ns_to_timer(int *nanosec,int round_mode)\r
-{\r
- int divider,base;\r
-\r
- base=TIMER_BASE;\r
-\r
- switch(round_mode){\r
- case TRIG_ROUND_NEAREST:\r
- default:\r
- divider=(*nanosec+base/2)/base;\r
- break;\r
- case TRIG_ROUND_DOWN:\r
- divider=(*nanosec)/base;\r
- break;\r
- case TRIG_ROUND_UP:\r
- divider=(*nanosec+base-1)/base;\r
- break;\r
- }\r
-\r
- *nanosec=base*divider;\r
- return divider-1;\r
-}\r
-\r
-static int ni_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd)\r
-{\r
- int err=0;\r
- int tmp;\r
-\r
- /* step 1: make sure trigger sources are trivially valid */\r
-\r
- tmp=cmd->start_src;\r
- cmd->start_src &= TRIG_NOW|TRIG_INT;\r
- if(!cmd->start_src || tmp!=cmd->start_src)err++;\r
-\r
- tmp=cmd->scan_begin_src;\r
- cmd->scan_begin_src &= TRIG_TIMER|TRIG_EXT;\r
- if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;\r
-\r
- tmp=cmd->convert_src;\r
- cmd->convert_src &= TRIG_TIMER|TRIG_EXT;\r
- if(!cmd->convert_src || tmp!=cmd->convert_src)err++;\r
-\r
- tmp=cmd->scan_end_src;\r
- cmd->scan_end_src &= TRIG_COUNT;\r
- if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++;\r
-\r
- tmp=cmd->stop_src;\r
- cmd->stop_src &= TRIG_COUNT|TRIG_NONE;\r
- if(!cmd->stop_src || tmp!=cmd->stop_src)err++;\r
-\r
- if(err)return 1;\r
-\r
- /* step 2: make sure trigger sources are unique and mutually compatible */\r
-\r
- /* note that mutual compatiblity is not an issue here */\r
- if(cmd->start_src!=TRIG_NOW &&\r
- cmd->start_src!=TRIG_INT)err++;\r
- if(cmd->scan_begin_src!=TRIG_TIMER &&\r
- cmd->scan_begin_src!=TRIG_EXT)err++;\r
- if(cmd->convert_src!=TRIG_TIMER &&\r
- cmd->convert_src!=TRIG_EXT)err++;\r
- if(cmd->stop_src!=TRIG_COUNT &&\r
- cmd->stop_src!=TRIG_NONE)err++;\r
-\r
- if(err)return 2;\r
-\r
- /* step 3: make sure arguments are trivially compatible */\r
-\r
- if(cmd->start_arg!=0){\r
- /* true for both TRIG_NOW and TRIG_INT */\r
- cmd->start_arg=0;\r
- err++;\r
- }\r
- if(cmd->scan_begin_src==TRIG_TIMER){\r
- if(cmd->scan_begin_arg<boardtype.ai_speed){\r
- cmd->scan_begin_arg=boardtype.ai_speed;\r
- err++;\r
- }\r
- if(cmd->scan_begin_arg>TIMER_BASE*0xffffff){\r
- cmd->scan_begin_arg=TIMER_BASE*0xffffff;\r
- err++;\r
- }\r
- }else{\r
- /* external trigger */\r
- /* should be level/edge, hi/lo specification here */\r
- /* should specify multiple external triggers */\r
-#if 0\r
-/* XXX This is disabled, since we want to use bits 30 and 31 to\r
- * refer to edge/level and hi/lo triggering. */\r
- if(cmd->scan_begin_arg>9){\r
- cmd->scan_begin_arg=9;\r
- err++;\r
- }\r
-#endif\r
- }\r
- if(cmd->convert_src==TRIG_TIMER){\r
- if(cmd->convert_arg<boardtype.ai_speed){\r
- cmd->convert_arg=boardtype.ai_speed;\r
- err++;\r
- }\r
- if(cmd->convert_arg>TIMER_BASE*0xffff){\r
- cmd->convert_arg=TIMER_BASE*0xffff;\r
- err++;\r
- }\r
- }else{\r
- /* external trigger */\r
- /* see above */\r
-#if 0\r
-/* XXX This is disabled, since we want to use bits 30 and 31 to\r
- * refer to edge/level and hi/lo triggering. */\r
- if(cmd->convert_arg>9){\r
- cmd->convert_arg=9;\r
- err++;\r
- }\r
-#endif\r
- }\r
-\r
- if(cmd->scan_end_arg!=cmd->chanlist_len){\r
- cmd->scan_end_arg=cmd->chanlist_len;\r
- err++;\r
- }\r
- if(cmd->stop_src==TRIG_COUNT){\r
- if(cmd->stop_arg>0x00ffffff){\r
- cmd->stop_arg=0x00ffffff;\r
- err++;\r
- }\r
- }else{\r
- /* TRIG_NONE */\r
- if(cmd->stop_arg!=0){\r
- cmd->stop_arg=0;\r
- err++;\r
- }\r
- }\r
-\r
- if(err)return 3;\r
-\r
- /* step 4: fix up any arguments */\r
-\r
- if(cmd->scan_begin_src==TRIG_TIMER){\r
- tmp=cmd->scan_begin_arg;\r
- ni_ns_to_timer(&cmd->scan_begin_arg,cmd->flags&TRIG_ROUND_MASK);\r
- if(tmp!=cmd->scan_begin_arg)err++;\r
- }\r
- if(cmd->convert_src==TRIG_TIMER){\r
- tmp=cmd->convert_arg;\r
- ni_ns_to_timer(&cmd->convert_arg,cmd->flags&TRIG_ROUND_MASK);\r
- if(tmp!=cmd->convert_arg)err++;\r
- if(cmd->scan_begin_src==TRIG_TIMER &&\r
- cmd->scan_begin_arg<cmd->convert_arg*cmd->scan_end_arg){\r
- cmd->scan_begin_arg=cmd->convert_arg*cmd->scan_end_arg;\r
- err++;\r
- }\r
- }\r
-\r
- if(err)return 4;\r
-\r
- return 0;\r
-}\r
-\r
-static int ni_ai_cmd(comedi_device *dev,comedi_subdevice *s)\r
-{\r
- int wsave;\r
- comedi_cmd *cmd=&s->async->cmd;\r
- int timer;\r
- int mode1=0; /* mode1 is needed for both stop and convert */\r
- int mode2=0;\r
-\r
- MDPRINTK("ni_ai_cmd\n");\r
- wsave = win_save();\r
-\r
- win_out(1,ADC_FIFO_Clear);\r
-\r
- ni_load_channelgain_list(dev,cmd->chanlist_len,cmd->chanlist,\r
- (cmd->flags&TRIG_DITHER)==TRIG_DITHER);\r
-\r
- /* start configuration */\r
- win_out(AI_Configuration_Start,Joint_Reset_Register);\r
-\r
-#ifndef TRY_BLOCK\r
- #ifdef PCIDMA\r
- ni_ai_setup_MITE_dma(dev,cmd,mode1); \r
- #else\r
- switch(cmd->stop_src){\r
- case TRIG_COUNT:\r
- /* stage number of scans */\r
- win_out((cmd->stop_arg-1)>>16,AI_SC_Load_A_Registers);\r
- win_out((cmd->stop_arg-1)&0xffff,AI_SC_Load_A_Registers+1);\r
-\r
- mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Trigger_Once;\r
- win_out(mode1,AI_Mode_1_Register);\r
-\r
- /* load SC (Scan Count) */\r
- win_out(AI_SC_Load,AI_Command_1_Register);\r
-\r
- devpriv->ai_continuous = 0;\r
-\r
- break;\r
- case TRIG_NONE:\r
- /* stage number of scans */\r
- win_out(0,AI_SC_Load_A_Registers);\r
- win_out(0,AI_SC_Load_A_Registers+1);\r
-\r
- mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Continuous;\r
- win_out(mode1,AI_Mode_1_Register);\r
-\r
- /* load SC (Scan Count) */\r
- win_out(AI_SC_Load,AI_Command_1_Register);\r
-\r
- devpriv->ai_continuous = 1;\r
-\r
- break;\r
- }\r
- #endif\r
-#else\r
- devpriv->blocksize = 0x4000;\r
- switch(cmd->stop_src){\r
- case TRIG_COUNT:\r
- devpriv->ai_continuous = 0;\r
- devpriv->n_left = cmd->stop_arg;\r
- break;\r
- case TRIG_NONE:\r
- devpriv->ai_continuous = 1;\r
- devpriv->n_left = 0;\r
- break;\r
- }\r
-\r
- mode1 = ni_ai_setup_block(dev,1,mode1);\r
-#endif\r
-\r
-\r
- switch(cmd->scan_begin_src){\r
- case TRIG_TIMER:\r
- /*\r
- AI_SI_Special_Trigger_Delay=0\r
- AI_Pre_Trigger=0\r
- AI_START_STOP_Select_Register:\r
- AI_START_Polarity=0 (?) rising edge\r
- AI_START_Edge=1 edge triggered\r
- AI_START_Sync=1 (?)\r
- AI_START_Select=0 SI_TC\r
- AI_STOP_Polarity=0 rising edge\r
- AI_STOP_Edge=0 level\r
- AI_STOP_Sync=1\r
- AI_STOP_Select=19 external pin (configuration mem)\r
- */\r
- win_out(AI_START_Edge|AI_START_Sync|\r
- AI_STOP_Select(19)|AI_STOP_Sync,\r
- AI_START_STOP_Select_Register);\r
-\r
- timer=ni_ns_to_timer(&cmd->scan_begin_arg,TRIG_ROUND_NEAREST);\r
- win_out((timer>>16),AI_SI_Load_A_Registers);\r
- win_out((timer&0xffff),AI_SI_Load_A_Registers+1);\r
-\r
- /* AI_SI_Initial_Load_Source=A */\r
- mode2 |= AI_SI_Initial_Load_Source&0;\r
-//mode2 |= AI_SC_Reload_Mode;\r
- win_out(mode2,AI_Mode_2_Register);\r
-\r
- /* load SI */\r
- win_out(AI_SI_Load,AI_Command_1_Register);\r
-\r
- /* stage freq. counter into SI B */\r
- win_out((timer>>16),AI_SI_Load_B_Registers);\r
- win_out((timer&0xffff),AI_SI_Load_B_Registers+1);\r
-\r
- break;\r
- case TRIG_EXT:\r
- {\r
- unsigned int reg = 0;\r
-\r
-/* XXX duh, these should be moved, but I need to think about it\r
- * a bit first. --ds */\r
-#define COMEDI_TRIG_LEVEL 0\r
-#define COMEDI_TRIG_EDGE (1<<31)\r
-#define COMEDI_TRIG_FALLING 0\r
-#define COMEDI_TRIG_RISING (1<<30)\r
-\r
- if(cmd->scan_begin_arg&COMEDI_TRIG_EDGE)\r
- reg |= AI_START_Edge;\r
- /* AI_START_Polarity==1 is falling edge */\r
- if(!(cmd->scan_begin_arg&COMEDI_TRIG_RISING))\r
- reg |= AI_START_Polarity;\r
- reg |= AI_START_Sync;\r
- reg |= AI_START_Select(1+(cmd->scan_begin_arg&0xf));\r
- reg |= AI_STOP_Select(19)|AI_STOP_Sync;\r
-\r
- win_out(reg,AI_START_STOP_Select_Register);\r
- break;\r
- }\r
- }\r
-\r
- switch(cmd->convert_src){\r
- case TRIG_TIMER:\r
- timer=ni_ns_to_timer(&cmd->convert_arg,TRIG_ROUND_NEAREST);\r
- win_out(1,AI_SI2_Load_A_Register); /* 0,0 does not work. */\r
- win_out(timer,AI_SI2_Load_B_Register);\r
-\r
- /* AI_SI2_Reload_Mode = alternate */\r
- /* AI_SI2_Initial_Load_Source = A */\r
- win_out((AI_SI2_Initial_Load_Source&0)|\r
- (AI_SI2_Reload_Mode),\r
- AI_Mode_2_Register);\r
-\r
- /* AI_SI2_Load */\r
- win_out(AI_SI2_Load,AI_Command_1_Register);\r
-\r
- //mode2 |= AI_SI_Reload_Mode(0);\r
- /* XXX the AI_SI stuff should go to the scan_begin_src area */\r
- mode2 |= AI_SI_Reload_Mode(1);\r
- //mode2 |= 0&AI_SI_Initial_Load_Source;\r
- mode2 |= AI_SI_Initial_Load_Source;\r
- mode2 |= AI_SI2_Reload_Mode; // alternate\r
- mode2 |= AI_SI2_Initial_Load_Source; // B\r
-\r
- win_out(mode2,AI_Mode_2_Register);\r
-\r
- break;\r
- case TRIG_EXT:\r
- mode1 |= AI_CONVERT_Source_Select(1+cmd->convert_arg) |\r
- AI_CONVERT_Source_Polarity;\r
- win_out(mode1,AI_Mode_1_Register);\r
-\r
- win_out(mode2 | AI_SI2_Reload_Mode,AI_Mode_2_Register);\r
-\r
- mode2 |= AI_SI_Reload_Mode(0);\r
- mode2 |= 0&AI_SI_Initial_Load_Source;\r
- mode2 |= AI_SI2_Reload_Mode; // alternate\r
- mode2 |= AI_SI2_Initial_Load_Source; // B\r
-\r
- win_out(mode2,AI_Mode_2_Register);\r
-\r
- break;\r
- }\r
-\r
- if(dev->irq){\r
- int bits;\r
-\r
- /* interrupt on FIFO, errors, SC_TC */\r
- bits= AI_Error_Interrupt_Enable|\r
- AI_SC_TC_Interrupt_Enable;\r
-\r
-#ifndef PCIDMA\r
- bits|=AI_FIFO_Interrupt_Enable;\r
-#endif\r
-\r
- if(s->async->cb_mask&COMEDI_CB_EOS){\r
- /* wake on end-of-scan */\r
- devpriv->aimode=AIMODE_SCAN;\r
- }else{\r
- devpriv->aimode=AIMODE_HALF_FULL;\r
- }\r
-\r
- switch(devpriv->aimode){\r
- case AIMODE_HALF_FULL:\r
- /*generate FIFO interrupts on half-full */\r
- win_out(AI_FIFO_Mode_HF|0x0000,AI_Mode_3_Register);\r
- break;\r
- case AIMODE_SAMPLE:\r
- /*generate FIFO interrupts on non-empty */\r
- win_out(AI_FIFO_Mode_NE|0x0000,AI_Mode_3_Register);\r
- break;\r
- case AIMODE_SCAN:\r
- /*generate FIFO interrupts on half-full */\r
- win_out(AI_FIFO_Mode_HF|0x0000,AI_Mode_3_Register);\r
- bits|=AI_STOP_Interrupt_Enable;\r
- break;\r
- default:\r
- break;\r
- }\r
-\r
- win_out(0x3f80,Interrupt_A_Ack_Register); /* clear interrupts */\r
-\r
- //TIM 4/17/01 win_out(bits,Interrupt_A_Enable_Register) ;\r
- ni_set_bits(dev, Interrupt_A_Enable_Register, bits, 1);\r
- \r
- MDPRINTK("Interrupt_A_Enable_Register = 0x%04x\n",bits);\r
- }else{\r
- /* interrupt on nothing */\r
- win_out(0x0000,Interrupt_A_Enable_Register) ;\r
-\r
- /* XXX start polling if necessary */\r
- MDPRINTK("interrupting on nothing\n");\r
- }\r
-\r
- /* end configuration */\r
- win_out(AI_Configuration_End,Joint_Reset_Register);\r
-\r
- switch(cmd->scan_begin_src){\r
- case TRIG_TIMER:\r
- /* AI_SI2_Arm, AI_SI_Arm, AI_DIV_Arm, AI_SC_Arm */\r
- win_out(0x1540,AI_Command_1_Register);\r
- break;\r
- case TRIG_EXT:\r
- /* AI_SI2_Arm, AI_DIV_Arm, AI_SC_Arm */\r
- win_out(0x1540,AI_Command_1_Register);\r
- break;\r
- }\r
-\r
- if(cmd->start_src==TRIG_NOW){\r
- /* TRIG_NOW */\r
- /* AI_START1_Pulse */\r
- win_out(AI_START1_Pulse,AI_Command_2_Register);\r
- s->async->inttrig=NULL;\r
- }else{\r
- /* TRIG_INT */\r
- s->async->inttrig=ni_ai_inttrig;\r
- }\r
-\r
- win_restore(wsave);\r
-\r
- //mite_dump_regs(devpriv->mite);\r
- MDPRINTK("exit ni_ai_cmd\n");\r
- \r
- return 0;\r
-}\r
-\r
-static int ni_ai_inttrig(comedi_device *dev,comedi_subdevice *s,\r
- unsigned int trignum)\r
-{\r
- int wsave;\r
-\r
- if(trignum!=0)return -EINVAL;\r
-\r
- wsave = win_save();\r
-\r
- win_out(AI_START1_Pulse,AI_Command_2_Register);\r
- s->async->inttrig=NULL;\r
-\r
- win_restore(wsave);\r
-\r
- return 1;\r
-}\r
-\r
-static void ni_ao_fifo_load(comedi_device *dev,comedi_subdevice *s,\r
- sampl_t *data,int n)\r
-{\r
- int i;\r
-\r
- for(i=0;i<n;i++){\r
- ni_writew(data[i],DAC_FIFO_Data);\r
- }\r
-}\r
-\r
-\r
-/*\r
- * There's a small problem if the FIFO gets really low and we\r
- * don't have the data to fill it. Basically, if after we fill\r
- * the FIFO with all the data available, the FIFO is _still_\r
- * less than half full, we never clear the interrupt. If the\r
- * IRQ is in edge mode, we never get another interrupt, because\r
- * this one wasn't cleared. If in level mode, we get flooded\r
- * with interrupts that we can't fulfill, because nothing ever\r
- * gets put into the buffer.\r
- *\r
- * This kind of situation is recoverable, but it is easier to\r
- * just pretend we had a FIFO underrun, since there is a good\r
- * chance it will happen anyway. This is _not_ the case for\r
- * RT code, as RT code might purposely be running close to the\r
- * metal. Needs to be fixed eventually.\r
- */\r
-static int ni_ao_fifo_half_empty(comedi_device *dev,comedi_subdevice *s)\r
-{\r
- int n,m;\r
-\r
- n=(s->async->buf_int_count-s->async->buf_user_count)/sizeof(sampl_t);\r
- if(n==0)return 0;\r
- if(n>boardtype.ao_fifo_depth/2)\r
- n=boardtype.ao_fifo_depth/2;\r
-\r
- if(s->async->buf_int_ptr+n*sizeof(sampl_t)>s->async->data_len){\r
- m=(s->async->data_len-s->async->buf_int_ptr)/sizeof(sampl_t);\r
- ni_ao_fifo_load(dev,s,s->async->data+s->async->buf_int_ptr,m);\r
- s->async->buf_int_count+=m*sizeof(sampl_t);\r
- s->async->buf_int_ptr=0;\r
- n-=m;\r
- }\r
- ni_ao_fifo_load(dev,s,s->async->data+s->async->buf_int_ptr,n);\r
- s->async->buf_int_count+=n*sizeof(sampl_t);\r
- s->async->buf_int_ptr+=n*sizeof(sampl_t);\r
-\r
- comedi_bufcheck(dev,s);\r
-\r
- return 1;\r
-}\r
-\r
-static int ni_ao_prep_fifo(comedi_device *dev,comedi_subdevice *s)\r
-{\r
- int n;\r
-\r
- /* reset fifo */\r
- win_out(0,DAC_FIFO_Clear);\r
-\r
- /* load some data */\r
- n=(s->async->buf_int_count-s->async->buf_user_count)/sizeof(sampl_t);\r
- if(n==0)return 0;\r
- if(n>boardtype.ao_fifo_depth)\r
- n=boardtype.ao_fifo_depth;\r
-\r
- ni_ao_fifo_load(dev,s,s->async->data+s->async->buf_int_ptr,n);\r
- s->async->buf_int_count+=n*sizeof(sampl_t);\r
- s->async->buf_int_ptr+=n*sizeof(sampl_t);\r
-\r
- return n;\r
-}\r
-\r
-\r
-static int ni_ao_insn_read(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data)\r
-{\r
- data[0] = devpriv->ao[CR_CHAN(insn->chanspec)];\r
-\r
- return 1;\r
-}\r
-\r
-static int ni_ao_insn_write(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data)\r
-{\r
- unsigned int conf;\r
- unsigned int chan;\r
- unsigned int range;\r
- unsigned int dat = data[0];\r
-\r
- chan=CR_CHAN(insn->chanspec);\r
-\r
- conf=chan<<8;\r
-\r
- /* XXX check range with current range in flaglist[chan] */\r
- /* should update calibration if range changes (ick) */\r
-\r
- range = CR_RANGE(insn->chanspec);\r
- if(boardtype.ao_unipolar){\r
- conf |= (range&1)^1;\r
- }\r
- conf |= (range&2)<<1;\r
-\r
-#if 0\r
- /* XXX oops. forgot flags in insn! */\r
- /* not all boards can deglitch, but this shouldn't hurt */\r
- if(insn->flags & TRIG_DEGLITCH)\r
- conf |= 2;\r
-#endif\r
-\r
- /* analog reference */\r
- /* AREF_OTHER connects AO ground to AI ground, i think */\r
- conf |= (CR_AREF(insn->chanspec)==AREF_OTHER)? 8 : 0;\r
-\r
- ni_writew(conf,AO_Configuration);\r
-\r
- devpriv->ao[chan] = dat;\r
-\r
- if(((range&1)==0) || !boardtype.ao_unipolar)\r
- dat^=(1<<(boardtype.aobits-1));\r
-\r
- ni_writew(dat,(chan)? DAC1_Direct_Data : DAC0_Direct_Data);\r
-\r
- return 1;\r
-}\r
-\r
-static int ni_ao_cmd(comedi_device *dev,comedi_subdevice *s)\r
-{\r
- comedi_cmd *cmd = &s->async->cmd;\r
- unsigned int conf;\r
- unsigned int chan;\r
- unsigned int range;\r
- int trigvar;\r
- int i;\r
- \r
- trigvar = ni_ns_to_timer(&cmd->scan_begin_arg,TRIG_ROUND_NEAREST);\r
-\r
- win_out(AO_Disarm,AO_Command_1_Register);\r
-\r
- for(i=0;i<cmd->chanlist_len;i++){\r
- chan=CR_CHAN(cmd->chanlist[i]);\r
-\r
- conf=chan<<8;\r
- \r
- /* XXX check range with current range in flaglist[chan] */\r
- /* should update calibration if range changes (ick) */\r
-\r
- range = CR_RANGE(cmd->chanlist[i]);\r
- conf |= (range&1);\r
- conf |= (range&2)<<1;\r
- \r
- /* not all boards can deglitch, but this shouldn't hurt */\r
- if(cmd->flags & TRIG_DEGLITCH) /* XXX ? */\r
- conf |= 2;\r
-\r
- /* analog reference */\r
- /* AREF_OTHER connects AO ground to AI ground, i think */\r
- conf |= (CR_AREF(cmd->chanlist[i])==AREF_OTHER)? 8 : 0;\r
-\r
- ni_writew(conf,AO_Configuration);\r
- }\r
-\r
- /* user is supposed to write() to buffer before triggering */\r
- if(ni_ao_prep_fifo(dev,s)==0)\r
- return -EIO;\r
-\r
- win_out(AO_Configuration_Start,Joint_Reset_Register);\r
-\r
- devpriv->ao_mode1|=AO_Trigger_Once;\r
- win_out(devpriv->ao_mode1,AO_Mode_1_Register);\r
- devpriv->ao_trigger_select&=~(AO_START1_Polarity|AO_START1_Select(-1));\r
- devpriv->ao_trigger_select|=AO_START1_Edge|AO_START1_Sync;\r
- win_out(devpriv->ao_trigger_select,AO_Trigger_Select_Register);\r
- devpriv->ao_mode3&=~AO_Trigger_Length;\r
- win_out(devpriv->ao_mode3,AO_Mode_3_Register);\r
-\r
- if(cmd->stop_src==TRIG_NOW){\r
- devpriv->ao_mode1|=AO_Continuous;\r
- }else{\r
- devpriv->ao_mode1&=~AO_Continuous;\r
- }\r
- win_out(devpriv->ao_mode1,AO_Mode_1_Register);\r
- devpriv->ao_mode2&=~AO_BC_Initial_Load_Source;\r
- win_out(devpriv->ao_mode2,AO_Mode_2_Register);\r
- if(cmd->stop_src==TRIG_NOW){\r
- win_out(0xff,AO_BC_Load_A_Register_High);\r
- win_out(0xffff,AO_BC_Load_A_Register_Low);\r
- }else{\r
- win_out(0,AO_BC_Load_A_Register_High);\r
- win_out(0,AO_BC_Load_A_Register_Low);\r
- }\r
- win_out(AO_BC_Load,AO_Command_1_Register);\r
- devpriv->ao_mode2&=~AO_UC_Initial_Load_Source;\r
- win_out(devpriv->ao_mode2,AO_Mode_2_Register);\r
- if(cmd->stop_src==TRIG_NOW){\r
- win_out(0xff,AO_UC_Load_A_Register_High);\r
- win_out(0xffff,AO_UC_Load_A_Register_Low);\r
- win_out(AO_UC_Load,AO_Command_1_Register);\r
- win_out(0xff,AO_UC_Load_A_Register_High);\r
- win_out(0xffff,AO_UC_Load_A_Register_Low);\r
- }else{\r
- win_out(0,AO_UC_Load_A_Register_High);\r
- win_out(0,AO_UC_Load_A_Register_Low);\r
- win_out(AO_UC_Load,AO_Command_1_Register);\r
- win_out((cmd->stop_arg-1)>>16,AO_UC_Load_A_Register_High);\r
- win_out((cmd->stop_arg-1)&0xffff,AO_UC_Load_A_Register_Low);\r
- }\r
-\r
- devpriv->ao_cmd2&=~AO_BC_Gate_Enable;\r
- ni_writew(devpriv->ao_cmd2,AO_Command_2);\r
- devpriv->ao_mode1&=~(AO_UI_Source_Select(0x1f)|AO_UI_Source_Polarity);\r
- win_out(devpriv->ao_mode1,AO_Mode_1_Register);\r
- devpriv->ao_mode2&=~(AO_UI_Reload_Mode(3)|AO_UI_Initial_Load_Source);\r
- win_out(devpriv->ao_mode2,AO_Mode_2_Register);\r
- win_out(0,AO_UI_Load_A_Register_High);\r
- win_out(1,AO_UI_Load_A_Register_Low);\r
- win_out(AO_UI_Load,AO_Command_1_Register);\r
- win_out((trigvar>>16),AO_UI_Load_A_Register_High);\r
- win_out((trigvar&0xffff),AO_UI_Load_A_Register_Low);\r
-\r
- if(cmd->scan_end_arg>1){\r
- devpriv->ao_mode1|=AO_Multiple_Channels;\r
- win_out(AO_Number_Of_Channels(cmd->scan_end_arg-1)|\r
- AO_UPDATE_Output_Select(1),\r
- AO_Output_Control_Register);\r
- }else{\r
- devpriv->ao_mode1&=~AO_Multiple_Channels;\r
- win_out(AO_Number_Of_Channels(CR_CHAN(cmd->chanlist[0]))|\r
- AO_UPDATE_Output_Select(1),\r
- AO_Output_Control_Register);\r
- }\r
- win_out(devpriv->ao_mode1,AO_Mode_1_Register);\r
-\r
- win_out(AO_DAC0_Update_Mode|AO_DAC1_Update_Mode,AO_Command_1_Register);\r
-\r
- devpriv->ao_mode3|=AO_Stop_On_Overrun_Error;\r
- win_out(devpriv->ao_mode3,AO_Mode_3_Register);\r
-\r
-devpriv->ao_mode2|=AO_FIFO_Mode(1);\r
- devpriv->ao_mode2&=~AO_FIFO_Retransmit_Enable;\r
- win_out(devpriv->ao_mode2,AO_Mode_2_Register);\r
-\r
- win_out(AO_Configuration_End,Joint_Reset_Register);\r
-\r
- win_out(devpriv->ao_mode3|AO_Not_An_UPDATE,AO_Mode_3_Register);\r
- win_out(devpriv->ao_mode3,AO_Mode_3_Register);\r
-\r
- /* wait for DACs to be loaded */\r
- udelay(100);\r
-\r
- win_out(devpriv->ao_cmd1|AO_UI_Arm|AO_UC_Arm|AO_BC_Arm|AO_DAC1_Update_Mode|AO_DAC0_Update_Mode,\r
- AO_Command_1_Register);\r
-\r
- //TIM 4/17/01 win_out(AO_FIFO_Interrupt_Enable|AO_Error_Interrupt_Enable,Interrupt_B_Enable_Register);\r
- ni_set_bits(dev, Interrupt_B_Enable_Register,\r
- AO_FIFO_Interrupt_Enable|AO_Error_Interrupt_Enable, 1);\r
-\r
- ni_writew(devpriv->ao_cmd2|AO_START1_Pulse,AO_Command_2);\r
- \r
- return 0;\r
-}\r
-\r
-static int ni_ao_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd)\r
-{\r
- int err=0;\r
- int tmp;\r
-\r
- /* step 1: make sure trigger sources are trivially valid */\r
-\r
- tmp=cmd->start_src;\r
- cmd->start_src &= TRIG_NOW;\r
- if(!cmd->start_src || tmp!=cmd->start_src)err++;\r
-\r
- tmp=cmd->scan_begin_src;\r
- cmd->scan_begin_src &= TRIG_TIMER;\r
- if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;\r
-\r
- tmp=cmd->convert_src;\r
- cmd->convert_src &= TRIG_NOW;\r
- if(!cmd->convert_src || tmp!=cmd->convert_src)err++;\r
-\r
- tmp=cmd->scan_end_src;\r
- cmd->scan_end_src &= TRIG_COUNT;\r
- if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++;\r
-\r
- tmp=cmd->stop_src;\r
- cmd->stop_src &= TRIG_COUNT|TRIG_NONE;\r
- if(!cmd->stop_src || tmp!=cmd->stop_src)err++;\r
-\r
- if(err)return 1;\r
-\r
- /* step 2: make sure trigger sources are unique and mutually compatible */\r
-\r
- if(cmd->stop_src!=TRIG_COUNT &&\r
- cmd->stop_src!=TRIG_NONE)err++;\r
-\r
- if(err)return 2;\r
-\r
- /* step 3: make sure arguments are trivially compatible */\r
-\r
- if(cmd->start_arg!=0){\r
- cmd->start_arg=0;\r
- err++;\r
- }\r
-#if 0\r
- /* XXX need ao_speed */\r
- if(cmd->scan_begin_arg<boardtype.ao_speed){\r
- cmd->scan_begin_arg=boardtype.ao_speed;\r
- err++;\r
- }\r
-#endif\r
- if(cmd->scan_begin_arg>TIMER_BASE*0xffffff){ /* XXX check */\r
- cmd->scan_begin_arg=TIMER_BASE*0xffffff;\r
- err++;\r
- }\r
- if(cmd->convert_arg!=0){\r
- cmd->convert_arg=0;\r
- err++;\r
- }\r
- if(cmd->scan_end_arg!=cmd->chanlist_len){\r
- cmd->scan_end_arg=cmd->chanlist_len;\r
- err++;\r
- }\r
- if(cmd->stop_src==TRIG_COUNT){ /* XXX check */\r
- if(cmd->stop_arg>0x00ffffff){\r
- cmd->stop_arg=0x00ffffff;\r
- err++;\r
- }\r
- }else{\r
- /* TRIG_NONE */\r
- if(cmd->stop_arg!=0){\r
- cmd->stop_arg=0;\r
- err++;\r
- }\r
- }\r
-\r
- if(err)return 3;\r
-\r
- /* step 4: fix up any arguments */\r
-\r
- tmp = cmd->scan_begin_arg;\r
- ni_ns_to_timer(&cmd->scan_begin_arg,cmd->flags&TRIG_ROUND_MASK);\r
- if(tmp!=cmd->scan_begin_arg)err++;\r
- \r
- if(err)return 4;\r
-\r
- return 0;\r
-}\r
-\r
-\r
-static int ni_dio_insn_config(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data)\r
-{\r
-#ifdef DEBUG_DIO\r
- printk("ni_dio_insn_config() chan=%d io=%d\n",\r
- CR_CHAN(insn->chanspec),data[0]);\r
-#endif\r
- if(insn->n!=1)return -EINVAL;\r
- switch(data[0]){\r
- case COMEDI_OUTPUT:\r
- s->io_bits |= 1<<CR_CHAN(insn->chanspec);\r
- break;\r
- case COMEDI_INPUT:\r
- s->io_bits &= ~(1<<CR_CHAN(insn->chanspec));\r
- break;\r
- default:\r
- return -EINVAL;\r
- }\r
-\r
- devpriv->dio_control &= ~DIO_Pins_Dir_Mask;\r
- devpriv->dio_control |= DIO_Pins_Dir(s->io_bits);\r
- win_out(devpriv->dio_control,DIO_Control_Register);\r
-\r
- return 1;\r
-}\r
-\r
-static int ni_dio_insn_bits(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data)\r
-{\r
-#ifdef DEBUG_DIO\r
- printk("ni_dio_insn_bits() mask=0x%x bits=0x%x\n",data[0],data[1]);\r
-#endif\r
- if(insn->n!=2)return -EINVAL;\r
- if(data[0]){\r
- s->state &= ~data[0];\r
- s->state |= (data[0]&data[1]);\r
- devpriv->dio_output &= ~DIO_Parallel_Data_Mask;\r
- devpriv->dio_output |= DIO_Parallel_Data_Out(s->state);\r
- win_out(devpriv->dio_output,DIO_Output_Register);\r
- }\r
- data[1] = ni_readw(DIO_Parallel_Input);\r
-\r
- return 2;\r
-}\r
-\r
-static void mio_common_detach(comedi_device *dev)\r
-{\r
- if(dev->subdevices && boardtype.has_8255)\r
- subdev_8255_cleanup(dev,dev->subdevices+3);\r
-}\r
-\r
-static int ni_E_init(comedi_device *dev,comedi_devconfig *it)\r
-{\r
- comedi_subdevice *s;\r
- \r
- dev->n_subdevices=7;\r
- \r
- if(alloc_subdevices(dev)<0)\r
- return -ENOMEM;\r
- \r
- /* analog input subdevice */\r
-\r
- s=dev->subdevices+0;\r
- dev->read_subdev=s;\r
- s->type=COMEDI_SUBD_AI;\r
- s->subdev_flags=SDF_READABLE|SDF_RT|SDF_GROUND|SDF_COMMON|SDF_DIFF|SDF_OTHER;\r
- s->subdev_flags|=SDF_DITHER;\r
- s->n_chan=boardtype.n_adchan;\r
- s->len_chanlist=512;\r
- s->maxdata=(1<<boardtype.adbits)-1;\r
- s->range_table=ni_range_lkup[boardtype.gainlkup];\r
- s->insn_read=ni_ai_insn_read;\r
- s->do_cmdtest=ni_ai_cmdtest;\r
- s->do_cmd=ni_ai_cmd;\r
- s->cancel=ni_ai_reset;\r
- s->poll=ni_ai_poll;\r
-\r
- /* analog output subdevice */\r
-\r
- s=dev->subdevices+1;\r
- if(boardtype.n_aochan){\r
- dev->write_subdev=s;\r
- s->type=COMEDI_SUBD_AO;\r
- s->subdev_flags=SDF_WRITEABLE|SDF_RT|SDF_DEGLITCH|SDF_GROUND|SDF_OTHER;\r
- s->n_chan=boardtype.n_aochan;\r
- s->maxdata=(1<<boardtype.aobits)-1;\r
- if(boardtype.ao_unipolar){\r
- s->range_table=&range_ni_E_ao_ext; /* XXX wrong for some boards */\r
- }else{\r
- s->range_table=&range_bipolar10;\r
- }\r
- s->insn_read=ni_ao_insn_read;\r
- s->insn_write=ni_ao_insn_write;\r
- s->do_cmd=ni_ao_cmd;\r
- s->do_cmdtest=ni_ao_cmdtest;\r
- s->len_chanlist = 2;\r
- }else{\r
- s->type=COMEDI_SUBD_UNUSED;\r
- }\r
- \r
- /* digital i/o subdevice */\r
- \r
- s=dev->subdevices+2;\r
- s->type=COMEDI_SUBD_DIO;\r
- s->subdev_flags=SDF_WRITEABLE|SDF_READABLE|SDF_RT;\r
- s->n_chan=8;\r
- s->maxdata=1;\r
- s->range_table=&range_digital;\r
- s->io_bits=0; /* all bits input */\r
- s->insn_bits=ni_dio_insn_bits;\r
- s->insn_config=ni_dio_insn_config;\r
-\r
- /* dio setup */\r
- devpriv->dio_control = DIO_Pins_Dir(s->io_bits);\r
- win_out(devpriv->dio_control,DIO_Control_Register);\r
- \r
- /* 8255 device */\r
- s=dev->subdevices+3;\r
- if(boardtype.has_8255){\r
- subdev_8255_init(dev,s,ni_8255_callback,dev);\r
- }else{\r
- s->type=COMEDI_SUBD_UNUSED;\r
- }\r
- /* XXX */\r
- \r
- /* general purpose counter/timer device */\r
- s=dev->subdevices+4;\r
- s->type=COMEDI_SUBD_COUNTER;\r
- s->subdev_flags=SDF_READABLE|SDF_WRITEABLE;\r
- s->insn_read=ni_gpct_insn_read;\r
- s->insn_config=ni_gpct_insn_config;\r
- s->n_chan=1; /* XXX */\r
- s->maxdata=1;\r
- \r
- s=dev->subdevices+4;\r
- s->type=COMEDI_SUBD_COUNTER;\r
- s->subdev_flags=SDF_READABLE|SDF_WRITEABLE;\r
- s->insn_read= ni_gpct_insn_read;\r
- s->insn_write= ni_gpct_insn_write;\r
- s->insn_config=ni_gpct_insn_config;\r
- s->n_chan=2;\r
- s->maxdata=1;\r
- devpriv->an_trig_etc_reg = 0;\r
- GPCT_Reset(dev,0);\r
- GPCT_Reset(dev,1);\r
- \r
- /* calibration subdevice -- ai and ao */\r
- s=dev->subdevices+5;\r
- s->type=COMEDI_SUBD_CALIB;\r
- s->subdev_flags=SDF_WRITEABLE|SDF_INTERNAL;\r
- caldac_setup(dev,s);\r
- s->insn_read=ni_calib_insn_read;\r
- s->insn_write=ni_calib_insn_write;\r
- \r
- /* EEPROM */\r
- s=dev->subdevices+6;\r
- s->type=COMEDI_SUBD_MEMORY;\r
- s->subdev_flags=SDF_READABLE|SDF_INTERNAL;\r
- s->n_chan=512;\r
- s->maxdata=0xff;\r
- s->insn_read=ni_eeprom_insn_read;\r
- \r
- /* ai configuration */\r
- ni_ai_reset(dev,dev->subdevices+0);\r
- win_out(0x1ba0,Clock_and_FOUT_Register);\r
-\r
-\r
- /* analog output configuration */\r
- \r
- devpriv->ao0p=0x0000;\r
- ni_writew(devpriv->ao0p,AO_Configuration);\r
- devpriv->ao1p=0x0100;\r
- ni_writew(devpriv->ao1p,AO_Configuration);\r
- win_out(AO_Configuration_Start,Joint_Reset_Register);\r
- win_out(AO_Disarm,AO_Command_1_Register);\r
- win_out(0,Interrupt_B_Enable_Register);\r
- win_out(0x0010,AO_Personal_Register);\r
- ni_writew(0x3f98,Interrupt_B_Ack);\r
- win_out(0x1430,AO_Personal_Register);\r
- win_out(0,AO_Output_Control_Register);\r
- win_out(0,AO_Start_Select_Register);\r
- devpriv->ao_cmd1=0;\r
- win_out(devpriv->ao_cmd1,AO_Command_1_Register);\r
- devpriv->ao_cmd2=0;\r
- devpriv->ao_mode1=0;\r
- devpriv->ao_mode2=0;\r
- devpriv->ao_mode3=0;\r
- devpriv->ao_trigger_select=0;\r
-\r
- if(dev->irq){\r
- win_out((IRQ_POLARITY<<0) | /* polarity : active high */\r
- (0<<1) | /* no interrupt on 3 pins */\r
- (1<<11) | /* enable int A */\r
- (1<<15) | /* enable int B */\r
- (interrupt_pin(dev->irq)<<8) | /* interrupt output pin A */\r
- (interrupt_pin(dev->irq)<<12) , /* interrupt output pin B */\r
- Interrupt_Control_Register\r
- );\r
- }\r
-\r
- pfi_setup(dev);\r
-\r
- printk("\n");\r
-\r
- return 0;\r
-}\r
-\r
-\r
-\r
-\r
-/*\r
- presents the EEPROM as a subdevice\r
-*/\r
-\r
-static int ni_eeprom_insn_read(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data)\r
-{\r
- data[0]=ni_read_eeprom(dev,CR_CHAN(insn->chanspec));\r
-\r
- return 1;\r
-}\r
-\r
-/*\r
- reads bytes out of eeprom\r
-*/\r
-\r
-static int ni_read_eeprom(comedi_device *dev,int addr)\r
-{\r
- int bit;\r
- int bitstring;\r
-\r
- bitstring=0x0300|((addr&0x100)<<3)|(addr&0xff);\r
- ni_writeb_p(0x04,Serial_Command);\r
- for(bit=0x8000;bit;bit>>=1){\r
- ni_writeb_p(0x04|((bit&bitstring)?0x02:0),Serial_Command);\r
- ni_writeb_p(0x05|((bit&bitstring)?0x02:0),Serial_Command);\r
- }\r
- bitstring=0;\r
- for(bit=0x80;bit;bit>>=1){\r
- ni_writeb_p(0x04,Serial_Command);\r
- ni_writeb_p(0x05,Serial_Command);\r
- bitstring|=((ni_readb_p(XXX_Status)&0x01)?bit:0);\r
- }\r
- ni_writeb_p(0x00,Serial_Command);\r
- \r
- return bitstring;\r
-}\r
-\r
-static void ni_write_caldac(comedi_device *dev,int addr,int val);\r
-/*\r
- calibration subdevice\r
-*/\r
-static int ni_calib_insn_write(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data)\r
-{\r
- ni_write_caldac(dev,CR_CHAN(insn->chanspec),data[0]);\r
- devpriv->caldacs[CR_CHAN(insn->chanspec)] = data[0];\r
-\r
- return 1;\r
-}\r
-\r
-static int ni_calib_insn_read(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data)\r
-{\r
- data[0] = devpriv->caldacs[CR_CHAN(insn->chanspec)];\r
-\r
- return 1;\r
-}\r
-\r
-static int pack_mb88341(int addr,int val,int *bitstring);\r
-static int pack_dac8800(int addr,int val,int *bitstring);\r
-static int pack_dac8043(int addr,int val,int *bitstring);\r
-#ifdef PCIMIO\r
-static int pack_ad8522(int addr,int val,int *bitstring);\r
-#endif\r
-\r
-struct caldac_struct{\r
- int n_chans;\r
- int n_bits;\r
- int (*packbits)(int,int,int *);\r
-};\r
-\r
-static struct caldac_struct caldac_mb88341={ 12, 8, pack_mb88341 };\r
-static struct caldac_struct caldac_dac8800={ 8, 8, pack_dac8800 };\r
-static struct caldac_struct caldac_dac8043={ 1, 12, pack_dac8043 };\r
-#ifdef PCIMIO\r
-static struct caldac_struct caldac_ad8522={ 2, 12, pack_ad8522 };\r
-#endif\r
-\r
-\r
-static void caldac_setup(comedi_device *dev,comedi_subdevice *s)\r
-{\r
- int i,j;\r
- int n_dacs;\r
- int n_chans=0;\r
- int n_bits;\r
- int diffbits=0;\r
- \r
- if(!boardtype.caldac[0])return;\r
- n_bits=boardtype.caldac[0]->n_bits;\r
- for(i=0;i<3;i++){\r
- if(!boardtype.caldac[i])break;\r
- if(boardtype.caldac[i]->n_bits!=n_bits)diffbits=1;\r
- n_chans+=boardtype.caldac[i]->n_chans;\r
- }\r
- n_dacs=i;\r
- s->n_chan=n_chans;\r
-\r
- if(diffbits){\r
- int chan;\r
-\r
- s->maxdata_list=kmalloc(sizeof(int)*n_chans,GFP_KERNEL);\r
- chan=0;\r
- for(i=0;i<n_dacs;i++){\r
- for(j=0;j<boardtype.caldac[i]->n_chans;j++){\r
- s->maxdata_list[chan]=\r
- (1<<boardtype.caldac[i]->n_bits)-1;\r
- chan++;\r
- }\r
- }\r
- }else{\r
- s->maxdata=(1<<boardtype.caldac[0]->n_bits)-1;\r
- }\r
-}\r
-\r
-static void ni_write_caldac(comedi_device *dev,int addr,int val)\r
-{\r
- int loadbit=0,bits=0,bit,bitstring=0;\r
- int i;\r
- \r
- for(i=0;i<3;i++){\r
- if(!boardtype.caldac[i])return;\r
- if(addr<boardtype.caldac[i]->n_chans){\r
- bits=boardtype.caldac[i]->packbits(addr,val,&bitstring);\r
- loadbit=SerDacLd(i);\r
- break;\r
- }\r
- addr-=boardtype.caldac[i]->n_chans;\r
- }\r
-\r
- for(bit=1<<(bits-1);bit;bit>>=1){\r
- ni_writeb(((bit&bitstring)?0x02:0),Serial_Command);\r
- ni_writeb(1|((bit&bitstring)?0x02:0),Serial_Command);\r
- }\r
- ni_writeb(loadbit,Serial_Command);\r
- ni_writeb(0,Serial_Command);\r
-}\r
-\r
-\r
-\r
-static int pack_mb88341(int addr,int val,int *bitstring)\r
-{\r
- /*\r
- Fujitsu MB 88341\r
- Note that address bits are reversed. Thanks to\r
- Ingo Keen for noticing this.\r
-\r
- Note also that the 88341 expects address values from\r
- 1-12, whereas we use channel numbers 0-11. The NI\r
- docs use 1-12, also, so be careful here.\r
- */\r
- addr++;\r
- *bitstring=((addr&0x1)<<11) |\r
- ((addr&0x2)<<9) |\r
- ((addr&0x4)<<7) |\r
- ((addr&0x8)<<5) |\r
- (val&0xff);\r
- return 12;\r
-}\r
-\r
-static int pack_dac8800(int addr,int val,int *bitstring)\r
-{\r
- *bitstring=((addr&0x7)<<8)|(val&0xff);\r
- return 11;\r
-}\r
-\r
-static int pack_dac8043(int addr,int val,int *bitstring)\r
-{\r
- *bitstring=val&0xfff;\r
- return 12;\r
-}\r
- \r
-#ifdef PCIMIO\r
-static int pack_ad8522(int addr,int val,int *bitstring)\r
-{\r
- *bitstring=(val&0xfff)|(addr ? 0xc000:0xa000);\r
- return 16;\r
-}\r
-#endif\r
-\r
-\r
-\r
-/*\r
- *\r
- * Programmable Function Inputs\r
- *\r
- */\r
-\r
-\r
-static void pfi_setup(comedi_device *dev)\r
-{\r
- /* currently, we don't output any signals, thus, all\r
- the PFI's are input */\r
- \r
- //TIM 4/17/01 win_out(0,IO_Bidirection_Pin_Register);\r
- ni_set_bits(dev, IO_Bidirection_Pin_Register, 0x03ff, 0);\r
-}\r
-\r
-\r
-\r
-/*\r
- *\r
- * General Purpose Counter/Timer section\r
- *\r
- */\r
-\r
-/*\r
-* Low level stuff...Each STC counter has two 24 bit load registers (A&B). Just make\r
-* it easier to access them.\r
-*/\r
-static void GPCT_Load_A(comedi_device *dev, int chan, long value)\r
-{\r
- win_out( (value>>16) & 0x00ff, G_Load_A_Register_High(chan));\r
- win_out( value & 0xffff, G_Load_A_Register_Low(chan));\r
-}\r
-\r
-static void GPCT_Load_B(comedi_device *dev, int chan, long value)\r
-{\r
- win_out( (value>>16) & 0x00ff, G_Load_B_Register_High(chan));\r
- win_out( value & 0xffff, G_Load_B_Register_Low(chan));\r
-}\r
-\r
-/* Load a value into the counter, using register A as the intermediate step.\r
-* You might use GPCT_Load_Using_A to load a 0x000000 into a counter\r
-* reset its value.\r
-*/\r
-static void GPCT_Load_Using_A(comedi_device *dev, int chan, long value)\r
-{\r
- devpriv->gpct_mode[chan] &= (~G_Load_Source_Select);\r
- win_out( devpriv->gpct_mode[chan],G_Mode_Register(chan));\r
- GPCT_Load_A(dev,chan,value);\r
- win_out( devpriv->gpct_command[chan]|G_Load,G_Command_Register(chan));\r
-}\r
-\r
-/* Don't use this by itself! The read is not atomic and quite unsafe.\r
-* If you think you need this function, use GPCT_G_Watch instead.\r
-*/\r
-int inline GPCT_G_Read(comedi_device *dev, int chan)\r
-{\r
- int tmp;\r
- tmp = win_in( G_Save_Register_High(chan)) << 16;\r
- tmp |= win_in(G_Save_Register_Low(chan));\r
- return tmp;\r
-}\r
-\r
-/*\r
-* Read the GPCTs current value. \r
-* This function is actually straight out of the STC RLPM.\r
-*/\r
-int GPCT_G_Watch(comedi_device *dev, int chan)\r
-{\r
- int save1,save2;\r
- \r
- devpriv->gpct_command[chan] &= ~G_Save_Trace;\r
- win_out( devpriv->gpct_command[chan],G_Command_Register(chan));\r
- \r
- devpriv->gpct_command[chan] |= G_Save_Trace;\r
- win_out( devpriv->gpct_command[chan], G_Command_Register(chan));\r
-\r
- save1 = GPCT_G_Read(dev,chan);\r
- save2 = GPCT_G_Read(dev,chan);\r
- \r
- /*if the value changed during the read, try again*/\r
- if (save1 != save2) {\r
- save1 = GPCT_G_Read(dev,chan);\r
- }\r
- return save1;\r
-}\r
-\r
-\r
-int GPCT_Disarm(comedi_device *dev, int chan)\r
-{\r
- win_out( devpriv->gpct_command[chan] | G_Disarm,G_Command_Register(chan));\r
- return 0;\r
-}\r
-\r
-\r
-int GPCT_Arm(comedi_device *dev, int chan)\r
-{\r
- win_out( devpriv->gpct_command[chan] | G_Arm,G_Command_Register(chan));\r
- /* If the counter is doing pulse width measurement, then make\r
- sure that the counter did not start counting right away. This would\r
- indicate that we started acquiring the pulse after it had already \r
- started and our measurement would be inaccurate */\r
- if(devpriv->gpct_cur_operation[chan] == GPCT_SINGLE_PW){\r
- int g_status; \r
-\r
- g_status=win_in(G_Status_Register);\r
- \r
- if(chan == 0){\r
- //TIM 5/2/01 possible error with very short pulses\r
- if((G0_Counting_St & g_status)|| !(G0_Armed_St&g_status)) {\r
- //error: we missed the beginning of the pulse\r
- return -EINVAL; //there is probably a more accurate error code...\r
- }\r
- }else{\r
- if((G1_Counting_St & g_status)|| !(G1_Armed_St&g_status)) {\r
- //error: we missed the beginning of the pulse\r
- return -EINVAL;\r
- }\r
- }\r
- }\r
- return 0;\r
-}\r
-\r
-int GPCT_Set_Source(comedi_device *dev,int chan ,int source)\r
-{\r
- //printk("GPCT_Set_Source...");\r
- devpriv->gpct_input_select[chan] &= ~G_Source_Select(0x1f);//reset gate to 0\r
- switch(source) {\r
- case GPCT_INT_CLOCK:\r
- devpriv->gpct_input_select[chan] |= G_Source_Select(0);//INT_TIMEBASE\r
- break;\r
- case GPCT_EXT_PIN:\r
- if(chan==0)\r
- devpriv->gpct_input_select[chan] |= G_Source_Select(9);//PFI8\r
- else\r
- devpriv->gpct_input_select[chan] |= G_Source_Select(4);//PFI3\r
- break;\r
- default:\r
- return -EINVAL;\r
- }\r
- win_out(devpriv->gpct_input_select[chan], G_Input_Select_Register(chan));\r
- //printk("exit GPCT_Set_Source\n");\r
- return 0;\r
-}\r
-\r
-int GPCT_Set_Gate(comedi_device *dev,int chan ,int gate)\r
-{\r
- //printk("GPCT_Set_Gate...");\r
- devpriv->gpct_input_select[chan] &= ~G_Gate_Select(0x1f);//reset gate to 0\r
- switch(gate) {\r
- case GPCT_NO_GATE:\r
- devpriv->gpct_input_select[chan] |= G_Gate_Select(31);//Low\r
- devpriv->gpct_mode[chan] |= G_Gate_Polarity;\r
- break;\r
- case GPCT_EXT_PIN:\r
- devpriv->gpct_mode[chan] &= ~G_Gate_Polarity;\r
- if(chan==0){\r
- devpriv->gpct_input_select[chan] |= G_Gate_Select(10);//PFI9\r
- }else{\r
- devpriv->gpct_input_select[chan] |= G_Gate_Select(5);//PFI4\r
- }\r
- break;\r
- default:\r
- return -EINVAL;\r
- }\r
- win_out(devpriv->gpct_input_select[chan], G_Input_Select_Register(chan));\r
- win_out(devpriv->gpct_mode[chan], G_Mode_Register(chan));\r
- //printk("exit GPCT_Set_Gate\n");\r
- return 0;\r
-}\r
-\r
-int GPCT_Set_Direction(comedi_device *dev,int chan,int direction)\r
-{\r
- //printk("GPCT_Set_Direction...");\r
- \r
- devpriv->gpct_command[chan] &= ~G_Up_Down(0x3);\r
- switch (direction) {\r
- case GPCT_UP:\r
- devpriv->gpct_command[chan] |= G_Up_Down(1);\r
- break;\r
- case GPCT_DOWN:\r
- devpriv->gpct_command[chan] |= G_Up_Down(0);\r
- break;\r
- case GPCT_HWUD:\r
- devpriv->gpct_command[chan] |= G_Up_Down(2);\r
- break;\r
- default:\r
- printk("Error direction=0x%08x..",direction);\r
- return -EINVAL;\r
- }\r
- win_out(devpriv->gpct_command[chan], G_Command_Register(chan));\r
- //TIM 4/23/01 win_out(devpriv->gpct_mode[chan], G_Mode_Register(chan));\r
- //printk("exit GPCT_Set_Direction\n");\r
- return 0;\r
-}\r
-\r
-void GPCT_Event_Counting(comedi_device *dev,int chan)\r
-{\r
-\r
- //NOTE: possible residual bits from multibit masks can corrupt\r
- //If you config for several measurements between Resets, watch out!\r
- \r
- //printk("GPCT_Event_Counting...");\r
- \r
- devpriv->gpct_cur_operation[chan] = GPCT_SIMPLE_EVENT;\r
- \r
- // Gating_Mode = 1\r
- devpriv->gpct_mode[chan] &= ~(G_Gating_Mode(0x3));\r
- devpriv->gpct_mode[chan] |= G_Gating_Mode(1);\r
- \r
- // Trigger_Mode_For_Edge_Gate = 1\r
- devpriv->gpct_mode[chan] &= ~(G_Trigger_Mode_For_Edge_Gate(0x3));\r
- devpriv->gpct_mode[chan] |= G_Trigger_Mode_For_Edge_Gate(2);\r
-\r
- win_out( devpriv->gpct_mode[chan],G_Mode_Register(chan));\r
- //printk("exit GPCT_Event_Counting\n");\r
-}\r
-\r
-void GPCT_Period_Meas(comedi_device *dev, int chan)\r
-{\r
- //printk("GPCT_Period_Meas...");\r
- \r
- devpriv->gpct_cur_operation[chan] = GPCT_SINGLE_PERIOD;\r
-\r
- \r
- //NOTE: possible residual bits from multibit masks can corrupt\r
- //If you config for several measurements between Resets, watch out! \r
- devpriv->gpct_mode[chan] &= ~G_OR_Gate;\r
- devpriv->gpct_mode[chan] &= ~G_Gate_Select_Load_Source;\r
- \r
- // Output_Mode = 3 \r
- devpriv->gpct_mode[chan] &= ~(G_Output_Mode(0x3));\r
- devpriv->gpct_mode[chan] |= G_Output_Mode(3);\r
- \r
- \r
- //Gating Mode=2\r
- devpriv->gpct_mode[chan] &= ~(G_Gating_Mode(0x3));\r
- devpriv->gpct_mode[chan] |= G_Gating_Mode(2);\r
- \r
- // Trigger_Mode_For_Edge_Gate=0\r
- devpriv->gpct_mode[chan] &= ~(G_Trigger_Mode_For_Edge_Gate(0x3));\r
- devpriv->gpct_mode[chan] |= G_Trigger_Mode_For_Edge_Gate(0);\r
-\r
- devpriv->gpct_mode[chan] |= G_Reload_Source_Switching;\r
- devpriv->gpct_mode[chan] &= ~G_Loading_On_Gate;\r
- devpriv->gpct_mode[chan] &= ~G_Loading_On_TC;\r
- devpriv->gpct_mode[chan] &= ~G_Gate_On_Both_Edges;\r
-\r
- // Stop_Mode = 2\r
- devpriv->gpct_mode[chan] &= ~(G_Stop_Mode(0x3));\r
- devpriv->gpct_mode[chan] |= G_Stop_Mode(0);\r
- \r
- // Counting_Once = 2 \r
- devpriv->gpct_mode[chan] &= ~(G_Counting_Once(0x3));\r
- devpriv->gpct_mode[chan] |= G_Counting_Once(2);\r
-\r
- // Up_Down = 1 \r
- devpriv->gpct_command[chan] &= ~(G_Up_Down(0x3));\r
- devpriv->gpct_command[chan] |= G_Up_Down(1);\r
-\r
- win_out( devpriv->gpct_mode[chan],G_Mode_Register(chan));\r
- win_out( devpriv->gpct_command[chan],G_Command_Register(chan));\r
- //printk("exit GPCT_Period_Meas\n");\r
-}\r
-\r
-void GPCT_Pulse_Width_Meas(comedi_device *dev, int chan)\r
-{\r
- //printk("GPCT_Pulse_Width_Meas...");\r
-\r
- devpriv->gpct_cur_operation[chan] = GPCT_SINGLE_PW;\r
-\r
- devpriv->gpct_mode[chan] &= ~G_OR_Gate;\r
- devpriv->gpct_mode[chan] &= ~G_Gate_Select_Load_Source;\r
-\r
- // Output_Mode = 3 \r
- devpriv->gpct_mode[chan] &= ~(G_Output_Mode(0x3));\r
- devpriv->gpct_mode[chan] |= G_Output_Mode(3);\r
- \r
- //Gating Mode=1\r
- devpriv->gpct_mode[chan] &= ~(G_Gating_Mode(0x3));\r
- devpriv->gpct_mode[chan] |= G_Gating_Mode(1);//TIM 4/24/01 was 2\r
- \r
- // Trigger_Mode_For_Edge_Gate=2\r
- devpriv->gpct_mode[chan] &= ~(G_Trigger_Mode_For_Edge_Gate(0x3));\r
- devpriv->gpct_mode[chan] |= G_Trigger_Mode_For_Edge_Gate(2);//TIM 4/24/01 was 0\r
-\r
-\r
- devpriv->gpct_mode[chan] |= G_Reload_Source_Switching;//TIM 4/24/01 was 1\r
- devpriv->gpct_mode[chan] &= ~G_Loading_On_Gate;//TIM 4/24/01 was 0\r
-\r
- devpriv->gpct_mode[chan] &= ~G_Loading_On_TC;\r
- devpriv->gpct_mode[chan] &= ~G_Gate_On_Both_Edges;\r
-\r
- // Stop_Mode = 0\r
- devpriv->gpct_mode[chan] &= ~(G_Stop_Mode(0x3));\r
- devpriv->gpct_mode[chan] |= G_Stop_Mode(0);\r
- \r
- // Counting_Once = 2 \r
- devpriv->gpct_mode[chan] &= ~(G_Counting_Once(0x3));\r
- devpriv->gpct_mode[chan] |= G_Counting_Once(2);\r
-\r
- // Up_Down = 1 \r
- devpriv->gpct_command[chan] &= ~(G_Up_Down(0x3));\r
- devpriv->gpct_command[chan] |= G_Up_Down(1);\r
-\r
- win_out( devpriv->gpct_mode[chan],G_Mode_Register(chan));\r
- win_out( devpriv->gpct_command[chan],G_Command_Register(chan));\r
-\r
- //printk("exit GPCT_Pulse_Width_Meas\n");\r
-}\r
-\r
-/* GPCT_Gen_Single_Pulse() creates pulse of length pulsewidth which starts after the Arm\r
-signal is sent. The pulse is delayed by the value already in the counter. This function could\r
-be modified to send a pulse in response to a trigger event at its gate.*/\r
-void GPCT_Gen_Single_Pulse(comedi_device *dev, int chan, unsigned int length)\r
-{\r
- //printk("GPCT_Gen_Cont...");\r
-\r
- devpriv->gpct_cur_operation[chan] = GPCT_SINGLE_PULSE_OUT;\r
-\r
- // Set length of the pulse\r
- GPCT_Load_B(dev,chan, length-1);\r
-\r
- //Load next time using B, This is reset by GPCT_Load_Using_A()\r
- devpriv->gpct_mode[chan] |= G_Load_Source_Select;\r
- \r
- devpriv->gpct_mode[chan] &= ~G_OR_Gate;\r
- devpriv->gpct_mode[chan] &= ~G_Gate_Select_Load_Source;\r
-\r
- // Output_Mode = 3 \r
- devpriv->gpct_mode[chan] &= ~(G_Output_Mode(0x3));\r
- devpriv->gpct_mode[chan] |= G_Output_Mode(2); //TIM 4/26/01 was 3\r
- \r
- //Gating Mode=0 for untriggered single pulse\r
- devpriv->gpct_mode[chan] &= ~(G_Gating_Mode(0x3));\r
- devpriv->gpct_mode[chan] |= G_Gating_Mode(0); //TIM 4/25/01 was 1\r
- \r
- // Trigger_Mode_For_Edge_Gate=0\r
- devpriv->gpct_mode[chan] &= ~(G_Trigger_Mode_For_Edge_Gate(0x3));\r
- devpriv->gpct_mode[chan] |= G_Trigger_Mode_For_Edge_Gate(2);\r
-\r
-\r
- devpriv->gpct_mode[chan] |= G_Reload_Source_Switching;\r
- devpriv->gpct_mode[chan] &= ~G_Loading_On_Gate;\r
- devpriv->gpct_mode[chan] |= G_Loading_On_TC; //TIM 4/25/01\r
- devpriv->gpct_mode[chan] &= ~G_Gate_On_Both_Edges;\r
-\r
- // Stop_Mode = 2\r
- devpriv->gpct_mode[chan] &= ~(G_Stop_Mode(0x3));\r
- devpriv->gpct_mode[chan] |= G_Stop_Mode(2); //TIM 4/25/01\r
- \r
- // Counting_Once = 2 \r
- devpriv->gpct_mode[chan] &= ~(G_Counting_Once(0x3));\r
- devpriv->gpct_mode[chan] |= G_Counting_Once(1); //TIM 4/25/01\r
-\r
- // Up_Down = 1 \r
- devpriv->gpct_command[chan] &= ~(G_Up_Down(0x3));\r
- devpriv->gpct_command[chan] |= G_Up_Down(0); //TIM 4/25/01 was 1\r
-\r
- win_out( devpriv->gpct_mode[chan],G_Mode_Register(chan));\r
- win_out( devpriv->gpct_command[chan],G_Command_Register(chan));\r
-\r
- //printk("exit GPCT_Gen_Cont\n");\r
-}\r
-\r
-void GPCT_Gen_Cont_Pulse(comedi_device *dev, int chan, unsigned int length)\r
-{\r
- //printk("GPCT_Gen_Cont...");\r
-\r
- devpriv->gpct_cur_operation[chan] = GPCT_CONT_PULSE_OUT;\r
-\r
- // Set length of the pulse\r
- GPCT_Load_B(dev,chan, length-1);\r
-\r
- //Load next time using B, This is reset by GPCT_Load_Using_A()\r
- devpriv->gpct_mode[chan] |= G_Load_Source_Select;\r
- \r
- devpriv->gpct_mode[chan] &= ~G_OR_Gate;\r
- devpriv->gpct_mode[chan] &= ~G_Gate_Select_Load_Source;\r
-\r
- // Output_Mode = 3 \r
- devpriv->gpct_mode[chan] &= ~(G_Output_Mode(0x3));\r
- devpriv->gpct_mode[chan] |= G_Output_Mode(2); //TIM 4/26/01 was 3\r
- \r
- //Gating Mode=0 for untriggered single pulse\r
- devpriv->gpct_mode[chan] &= ~(G_Gating_Mode(0x3));\r
- devpriv->gpct_mode[chan] |= G_Gating_Mode(0); //TIM 4/26/01 was 0\r
- \r
- // Trigger_Mode_For_Edge_Gate=0\r
- devpriv->gpct_mode[chan] &= ~(G_Trigger_Mode_For_Edge_Gate(0x3));\r
- devpriv->gpct_mode[chan] |= G_Trigger_Mode_For_Edge_Gate(2);\r
-\r
-\r
- devpriv->gpct_mode[chan] |= G_Reload_Source_Switching;\r
- devpriv->gpct_mode[chan] &= ~G_Loading_On_Gate;\r
- devpriv->gpct_mode[chan] |= G_Loading_On_TC; \r
- devpriv->gpct_mode[chan] &= ~G_Gate_On_Both_Edges;\r
-\r
- // Stop_Mode = 2\r
- devpriv->gpct_mode[chan] &= ~(G_Stop_Mode(0x3));\r
- devpriv->gpct_mode[chan] |= G_Stop_Mode(0); //TIM 4/26/01\r
- \r
- // Counting_Once = 2 \r
- devpriv->gpct_mode[chan] &= ~(G_Counting_Once(0x3));\r
- devpriv->gpct_mode[chan] |= G_Counting_Once(0); //TIM 4/26/01\r
-\r
- // Up_Down = 1 \r
- devpriv->gpct_command[chan] &= ~(G_Up_Down(0x3));\r
- devpriv->gpct_command[chan] |= G_Up_Down(0); \r
-\r
- //TIM 4/26/01\r
- //This seems pretty unsafe since I don't think it is cleared anywhere.\r
- //I don't think this is working\r
- //devpriv->gpct_command[chan] &= ~G_Bank_Switch_Enable;\r
- //devpriv->gpct_command[chan] &= ~G_Bank_Switch_Mode;\r
- \r
-\r
- win_out( devpriv->gpct_mode[chan],G_Mode_Register(chan));\r
- win_out( devpriv->gpct_command[chan],G_Command_Register(chan));\r
-\r
- //printk("exit GPCT_Gen_Cont\n");\r
-}\r
-\r
-void GPCT_Reset(comedi_device *dev, int chan)\r
-{\r
- unsigned long irqflags;\r
- int temp_ack_reg=0;\r
- \r
- //printk("GPCT_Reset...");\r
- devpriv->gpct_cur_operation[chan] = GPCT_RESET;\r
-\r
- switch (chan) {\r
- case 0:\r
- //note: I need to share the soft copies of the Enable Register with the ISRs.\r
- // so I'm using comedi_spin_lock_irqsave() to guard this section of code\r
- win_out(G0_Reset,Joint_Reset_Register);\r
- comedi_spin_lock_irqsave(&dev->spinlock, irqflags);\r
- ni_set_bits(dev,Interrupt_A_Enable_Register,G0_TC_Interrupt_Enable, 0);\r
- ni_set_bits(dev,Interrupt_A_Enable_Register,G0_Gate_Interrupt_Enable,0);\r
- comedi_spin_unlock_irqrestore(&dev->spinlock, irqflags);\r
- temp_ack_reg |= G0_Gate_Error_Confirm;\r
- temp_ack_reg |= G0_TC_Error_Confirm;\r
- temp_ack_reg |= G0_TC_Interrupt_Ack;\r
- temp_ack_reg |= G0_Gate_Interrupt_Ack;\r
- win_out(temp_ack_reg,Interrupt_A_Ack_Register);\r
- \r
- //problem...this interferes with the other ctr...\r
- devpriv->an_trig_etc_reg |= GPFO_0_Output_Enable;\r
- win_out(devpriv->an_trig_etc_reg, Analog_Trigger_Etc_Register);\r
- break;\r
- case 1:\r
- win_out(G1_Reset,Joint_Reset_Register);\r
- comedi_spin_lock_irqsave(&dev->spinlock, irqflags);\r
- ni_set_bits(dev,Interrupt_B_Enable_Register,G1_TC_Interrupt_Enable, 0);\r
- ni_set_bits(dev,Interrupt_B_Enable_Register,G0_Gate_Interrupt_Enable,0);\r
- comedi_spin_unlock_irqrestore(&dev->spinlock, irqflags);\r
- temp_ack_reg |= G1_Gate_Error_Confirm;\r
- temp_ack_reg |= G1_TC_Error_Confirm;\r
- temp_ack_reg |= G1_TC_Interrupt_Ack;\r
- temp_ack_reg |= G1_Gate_Interrupt_Ack;\r
- win_out(temp_ack_reg,Interrupt_B_Ack_Register);\r
- \r
- devpriv->an_trig_etc_reg |= GPFO_1_Output_Enable;\r
- win_out(devpriv->an_trig_etc_reg, Analog_Trigger_Etc_Register);\r
- break;\r
- };\r
- \r
- devpriv->gpct_mode[chan] = 0;\r
- devpriv->gpct_input_select[chan] = 0;\r
- devpriv->gpct_command[chan] = 0;\r
- \r
- devpriv->gpct_command[chan] |= G_Synchronized_Gate;\r
- \r
- win_out( devpriv->gpct_mode[chan],G_Mode_Register(chan));\r
- win_out( devpriv->gpct_input_select[chan],G_Input_Select_Register(chan));\r
- win_out( 0,G_Autoincrement_Register(chan)); \r
- \r
- //printk("exit GPCT_Reset\n");\r
-}\r
-\r
-static int ni_gpct_insn_config(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data)\r
-{\r
- int retval=0;\r
- //printk("data[0] is 0x%08x, data[1] is 0x%08x\n",data[0],data[1]);\r
- switch(data[0]){\r
- case GPCT_RESET:\r
- if(insn->n!=1)return -EINVAL;\r
- GPCT_Reset(dev,insn->chanspec);\r
- break;\r
- case GPCT_SET_SOURCE:\r
- if(insn->n!=2)return -EINVAL;\r
- retval=GPCT_Set_Source(dev,insn->chanspec,data[1]);\r
- break;\r
- case GPCT_SET_GATE:\r
- if(insn->n!=2)return -EINVAL;\r
- retval=GPCT_Set_Gate(dev,insn->chanspec,data[1]);\r
- break;\r
- case GPCT_SET_DIRECTION:\r
- if(insn->n!=2) return -EINVAL;\r
- retval=GPCT_Set_Direction(dev,insn->chanspec,data[1]);\r
- break;\r
- case GPCT_GET_INT_CLK_FRQ:\r
- if(insn->n!=2) return -EINVAL;\r
- //There are actually 2 internal clocks on the STC, we always\r
- //use the fast 20MHz one at this time. Tim Ousley 5/1/01\r
- //NOTE: This is not the final interface, ideally the user\r
- //will never need to know the int. clk. freq.\r
- data[1]=50;//50ns = 20MHz = internal timebase of STC\r
- break;\r
- case GPCT_SET_OPERATION:\r
- //TIM 5/1/01 if((insn->n<2)||(insn->n>3))return -EINVAL;\r
- switch(data[1]){\r
- case GPCT_SIMPLE_EVENT:\r
- GPCT_Event_Counting(dev,insn->chanspec);\r
- break;\r
- case GPCT_SINGLE_PERIOD:\r
- GPCT_Period_Meas(dev,insn->chanspec);\r
- break;\r
- case GPCT_SINGLE_PW:\r
- GPCT_Pulse_Width_Meas(dev,insn->chanspec);\r
- break;\r
- case GPCT_SINGLE_PULSE_OUT:\r
- GPCT_Gen_Single_Pulse(dev,insn->chanspec,data[2]);\r
- break;\r
- case GPCT_CONT_PULSE_OUT:\r
- GPCT_Gen_Cont_Pulse(dev,insn->chanspec,data[2]);\r
- break;\r
- default:\r
- printk("unsupported GPCT operation!\n");\r
- return -EINVAL;\r
- }\r
- break;\r
- case GPCT_ARM:\r
- if(insn->n!=1)return -EINVAL;\r
- retval=GPCT_Arm(dev,insn->chanspec);\r
- break;\r
- case GPCT_DISARM:\r
- if(insn->n!=1)return -EINVAL;\r
- retval=GPCT_Disarm(dev,insn->chanspec);\r
- break;\r
- default:\r
- return -EINVAL;\r
- }\r
-\r
- //catch any errors from return values\r
- if(retval==0){\r
- return insn->n;\r
- }else{\r
- if(data[0]!=GPCT_ARM){ \r
- printk("error: retval was %d\n",retval);\r
- printk("data[0] is 0x%08x, data[1] is 0x%08x\n",data[0],data[1]);\r
- }\r
-\r
- return retval;\r
- }\r
-}\r
-\r
-static int ni_gpct_insn_read(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data) {\r
-\r
- int chan=insn->chanspec;\r
- int cur_op = devpriv->gpct_cur_operation[chan];\r
- \r
- //printk("in ni_gpct_insn_read, n=%d, data[0]=%d\n",insn->chanspec,data[0]);\r
- if(insn->n!=1)return -EINVAL;\r
- \r
- data[0] = GPCT_G_Watch(dev,insn->chanspec);\r
- \r
- /* for certain modes (period and pulse width measurment), the value\r
- in the counter is not valid until the counter stops. If the value is \r
- invalid, return a 0 */\r
- if((cur_op == GPCT_SINGLE_PERIOD) || (cur_op == GPCT_SINGLE_PW)){\r
- /* is the counter still running? */\r
- if(win_in(G_Status_Register) & (chan?G1_Counting_St:G0_Counting_St))\r
- data[0]=0;\r
- }\r
- return 1;\r
-}\r
-\r
-static int ni_gpct_insn_write(comedi_device *dev,comedi_subdevice *s,\r
- comedi_insn *insn,lsampl_t *data) {\r
-\r
- //printk("in ni_gpct_insn_write");\r
- if(insn->n!=1)return -EINVAL;\r
- GPCT_Load_Using_A(dev,insn->chanspec,data[0]);\r
- return 1;\r
-}\r
-\r
-\r
-static int ni_8255_callback(int dir,int port,int data,void *arg)\r
-{\r
- comedi_device *dev=arg;\r
-\r
- if(dir){\r
- ni_writeb(data,Port_A+2*port);\r
- return 0;\r
- }else{\r
- return ni_readb(Port_A+2*port);\r
- }\r
-}\r
+
+/*
+ comedi/drivers/ni_mio_common.c
+ Hardware driver for DAQ-STC based boards
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2001 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/*
+ This file is meant to be included by another file, e.g.,
+ ni_atmio.c or ni_pcimio.c.
+
+ Interrupt support originally added by Truxton Fulton
+ <trux@truxton.com>
+
+ References (from ftp://ftp.natinst.com/support/manuals):
+
+ 340747b.pdf AT-MIO E series Register Level Programmer Manual
+ 341079b.pdf PCI E Series RLPM
+ 340934b.pdf DAQ-STC reference manual
+
+ Other possibly relevant info:
+
+ 320517c.pdf User manual (obsolete)
+ 320517f.pdf User manual (new)
+ 320889a.pdf delete
+ 320906c.pdf maximum signal ratings
+ 321066a.pdf about 16x
+ 321791a.pdf discontinuation of at-mio-16e-10 rev. c
+ 321808a.pdf about at-mio-16e-10 rev P
+ 321837a.pdf discontinuation of at-mio-16de-10 rev d
+ 321838a.pdf about at-mio-16de-10 rev N
+
+ ISSUES:
+
+ - the interrupt routine needs to be cleaned up
+ - many printk's need to be changed to rt_printk()
+*/
+
+//#define DEBUG_INTERRUPT
+//#define TRY_BLOCK
+#define DEBUG_STATUS_A
+//#define DEBUG_STATUS_B
+
+#include "8255.h"
+
+#ifndef MDPRINTK
+#define MDPRINTK(format,args...)
+#endif
+
+/* reference: ground, common, differential, other */
+static int ni_modebits1[4]={ 0x3000, 0x2000, 0x1000, 0 };
+static int ni_modebits2[4]={ 0x3f, 0x3f, 0x37, 0x37 };
+
+static int ni_gainlkup[][16]={
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+ { 1, 2, 4, 7, 9, 10, 12, 15, 0,0,0,0,0,0,0,0 },
+ { 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 0,0 },
+ { 0, 1, 4, 7, 8, 9, 12, 15, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 4, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static comedi_lrange range_ni_E_ai={ 16, {
+ RANGE( -10, 10 ),
+ RANGE( -5, 5 ),
+ RANGE( -2.5, 2.5 ),
+ RANGE( -1, 1 ),
+ RANGE( -0.5, 0.5 ),
+ RANGE( -0.25, 0.25 ),
+ RANGE( -0.1, 0.1 ),
+ RANGE( -0.05, 0.05 ),
+ RANGE( 0, 20 ),
+ RANGE( 0, 10 ),
+ RANGE( 0, 5 ),
+ RANGE( 0, 2 ),
+ RANGE( 0, 1 ),
+ RANGE( 0, 0.5 ),
+ RANGE( 0, 0.2 ),
+ RANGE( 0, 0.1 ),
+}};
+static comedi_lrange range_ni_E_ai_limited={ 8, {
+ RANGE( -10, 10 ),
+ RANGE( -5, 5 ),
+ RANGE( -1, 1 ),
+ RANGE( -0.1, 0.1 ),
+ RANGE( 0, 10 ),
+ RANGE( 0, 5 ),
+ RANGE( 0, 1 ),
+ RANGE( 0, 0.1 ),
+}};
+static comedi_lrange range_ni_E_ai_limited14={ 14, {
+ RANGE( -10, 10 ),
+ RANGE( -5, 5 ),
+ RANGE( -2, 2 ),
+ RANGE( -1, 1 ),
+ RANGE( -0.5, 0.5 ),
+ RANGE( -0.2, 0.2 ),
+ RANGE( -0.1, 0.1 ),
+ RANGE( 0, 10 ),
+ RANGE( 0, 5 ),
+ RANGE( 0, 2 ),
+ RANGE( 0, 1 ),
+ RANGE( 0, 0.5 ),
+ RANGE( 0, 0.2 ),
+ RANGE( 0, 0.1 ),
+}};
+static comedi_lrange range_ni_E_ai_bipolar4={ 4, {
+ RANGE( -10, 10 ),
+ RANGE( -5, 5 ),
+ RANGE( -0.5, 0.5 ),
+ RANGE( -0.05, 0.05 ),
+}};
+#if 0
+static comedi_lrange range_ni_E_ao = { 2, {
+ RANGE( -10, 10 ),
+ RANGE( 0, 10 ),
+}};
+#endif
+static comedi_lrange range_ni_E_ao_ext = { 4, {
+ RANGE( -10, 10 ),
+ RANGE( 0, 10 ),
+ RANGE_ext( -1, 1 ),
+ RANGE_ext( 0, 1 ),
+}};
+
+static comedi_lrange *ni_range_lkup[]={
+ &range_ni_E_ai,
+ &range_ni_E_ai_limited,
+ &range_ni_E_ai_limited14,
+ &range_ni_E_ai_bipolar4,
+};
+
+
+
+static int ni_dio_insn_config(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data);
+static int ni_dio_insn_bits(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data);
+
+static int ni_calib_insn_read(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data);
+static int ni_calib_insn_write(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data);
+
+static int ni_eeprom_insn_read(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data);
+
+static void caldac_setup(comedi_device *dev,comedi_subdevice *s);
+static int ni_read_eeprom(comedi_device *dev,int addr);
+
+#ifdef DEBUG_STATUS_A
+static void ni_mio_print_status_a(int status);
+#else
+#define ni_mio_print_status_a(a)
+#endif
+#ifdef DEBUG_STATUS_B
+static void ni_mio_print_status_b(int status);
+#else
+#define ni_mio_print_status_b(a)
+#endif
+
+static int ni_ai_reset(comedi_device *dev,comedi_subdevice *s);
+static void ni_handle_fifo_half_full(comedi_device *dev);
+static void ni_handle_fifo_dregs(comedi_device *dev);
+#ifdef TRY_BLOCK
+static void ni_handle_block(comedi_device *dev);
+#endif
+#ifdef PCIDMA
+static void ni_handle_block_dma(comedi_device *dev);
+#endif
+static int ni_ai_inttrig(comedi_device *dev,comedi_subdevice *s,
+ unsigned int trignum);
+
+static int ni_ao_fifo_half_empty(comedi_device *dev,comedi_subdevice *s);
+
+static int ni_8255_callback(int dir,int port,int data,void *arg);
+
+static int ni_ns_to_timer(int *nanosec,int round_mode);
+
+//static int gpct_setup(comedi_device *dev,comedi_subdevice *s);
+static int ni_gpct_insn_config(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data);
+static int ni_gpct_insn_read(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data);
+
+static void pfi_setup(comedi_device *dev);
+
+/*GPCT function def's*/
+int GPCT_G_Watch(comedi_device *dev, int chan);
+
+void GPCT_Reset(comedi_device *dev, int chan);
+void GPCT_Gen_Cont_Pulse(comedi_device *dev, int chan, unsigned int length);
+void GPCT_Gen_Single_Pulse(comedi_device *dev, int chan, unsigned int length);
+void GPCT_Period_Meas(comedi_device *dev, int chan);
+void GPCT_Pulse_Width_Meas(comedi_device *dev, int chan);
+void GPCT_Event_Counting(comedi_device *dev,int chan);
+int GPCT_Set_Direction(comedi_device *dev,int chan,int direction);
+int GPCT_Set_Gate(comedi_device *dev,int chan ,int gate);
+int GPCT_Set_Source(comedi_device *dev,int chan ,int source);
+
+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,
+ comedi_insn *insn,lsampl_t *data);
+static int ni_gpct_insn_config(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data);
+
+#undef DEBUG
+
+#define AIMODE_NONE 0
+#define AIMODE_HALF_FULL 1
+#define AIMODE_SCAN 2
+#define AIMODE_SAMPLE 3
+
+static void handle_a_interrupt(comedi_device *dev,unsigned short status);
+static void handle_b_interrupt(comedi_device *dev,unsigned short status);
+#ifdef PCIDMA
+/*status must be long because the CHSR is 32 bits and the high bits
+are important to us */
+static void mite_handle_interrupt(comedi_device *dev,unsigned long status);
+void ni_munge(comedi_device *dev,comedi_subdevice *s,sampl_t *start, sampl_t *stop);
+#endif
+
+/* ni_set_bits( ) allows different parts of the ni_mio_common driver to
+* share registers (such as Interrupt_A_Register) without interfering with
+* each other. Use comedi_spin_lock_irqsave() and comedi_spin_unlock_irqrestore()
+* if you use this to modify the interrupt enable registers...which are sometimes
+* changed in ISRs
+*
+* NOTE: the switch/case statements are optimized out for a constant argument
+* so this is actually quite fast--- If you must wrap another function around this
+* make it inline to avoid a large speed penalty.
+*
+* value should only be 1 or 0.
+*/
+void inline ni_set_bits(comedi_device *dev, int reg, int bits, int value) {
+
+ switch (reg){
+ case Interrupt_A_Enable_Register:
+ if(value)
+ devpriv->int_a_enable_reg |= bits;
+ else
+ devpriv->int_a_enable_reg &= ~bits;
+ win_out(devpriv->int_a_enable_reg,Interrupt_A_Enable_Register);
+ break;
+ case Interrupt_B_Enable_Register:
+ if(value)
+ devpriv->int_b_enable_reg |= bits;
+ else
+ devpriv->int_b_enable_reg &= ~bits;
+ win_out(devpriv->int_b_enable_reg,Interrupt_B_Enable_Register);
+ break;
+ case IO_Bidirection_Pin_Register:
+ if(value)
+ devpriv->io_bidirection_pin_reg |= bits;
+ else
+ devpriv->io_bidirection_pin_reg &= ~bits;
+ win_out(devpriv->io_bidirection_pin_reg,IO_Bidirection_Pin_Register);
+ break;
+ default:
+ printk("Warning ni_set_bits() called with invalid arguments\n");
+ printk("reg is %d\n",reg);
+ break;
+ }
+}
+
+
+static
+void ni_E_interrupt(int irq,void *d,struct pt_regs * regs)
+{
+ comedi_device *dev=d;
+ unsigned short a_status;
+ unsigned short b_status;
+ int wsave;
+#ifdef PCIDMA
+ /* m_status must be long because the CHSR is a 32 bit register and we are
+ interested in several high bits */
+ unsigned long m_status;
+#endif
+
+ MDPRINTK("ni_E_Interrupt\n");
+/*
+ If you want to use windowed registers in an interrupt, it is
+ important that you restore the window address register. If
+ you change certain modes, e.g., AI_Configuration_Start/End,
+ you need to set up software flags for non-interrupt routines.
+*/
+ wsave=win_save();
+
+ a_status=ni_readw(AI_Status_1);
+ b_status=ni_readw(AO_Status_1);
+#ifdef PCIDMA
+ m_status=readl(devpriv->mite->mite_io_addr+MITE_CHSR+CHAN_OFFSET(0));
+#endif
+#ifdef DEBUG_INTERRUPT
+ rt_printk("ni_mio_common: interrupt: a_status=%04x b_status=%04x\n",
+ a_status,b_status);
+ ni_mio_print_status_a(a_status);
+ ni_mio_print_status_b(b_status);
+#endif
+#ifdef PCIDMA
+ //rt_printk("mite status=0x%08lx\n",m_status);
+ if(m_status&CHSR_INT)mite_handle_interrupt(dev,m_status);
+#endif
+ if(a_status&Interrupt_A_St)handle_a_interrupt(dev,a_status);
+ if(b_status&Interrupt_B_St)handle_b_interrupt(dev,b_status);
+
+ win_restore(wsave);
+ MDPRINTK("exit ni_E_Interrupt\n");
+}
+
+#ifdef PCIDMA
+static void mite_handle_interrupt(comedi_device *dev,unsigned long m_status)
+{
+ comedi_subdevice *s=dev->subdevices+0;
+
+ comedi_event(dev,s,COMEDI_CB_BLOCK);
+
+ MDPRINTK("mite_handle_interrupt\n");
+ writel(CHOR_CLRLC, devpriv->mite->mite_io_addr+MITE_CHOR+CHAN_OFFSET(0));
+#if 0
+ //Don't munge the data, just update the user's status variables
+ s->async->buf_int_count=mite_bytes_transferred(devpriv->mite, 0);
+ s->async->buf_int_ptr= s->async->buf_int_count % s->async->prealloc_bufsz;
+#else
+ //Munge the ADC data to change its format from twos complement to unsigned int
+ //This is slow but makes it more compatible with other cards
+ {
+ unsigned int raw_ptr;
+ s->async->buf_int_count = mite_bytes_transferred(devpriv->mite, 0);
+ raw_ptr = s->async->buf_int_count % s->async->prealloc_bufsz;
+ if(s->async->buf_int_ptr > raw_ptr) {
+ ni_munge(dev,s,s->async->buf_int_ptr+s->async->prealloc_buf,
+ s->async->prealloc_buf+s->async->prealloc_bufsz);
+ s->async->buf_int_ptr = 0;
+ }
+ ni_munge(dev,s,s->async->buf_int_ptr+s->async->prealloc_buf,
+ raw_ptr+s->async->prealloc_buf);
+ s->async->buf_int_ptr = raw_ptr;
+ }
+#endif
+ MDPRINTK("CHSR is 0x%08lx, count is %d\n",m_status,s->async->buf_int_count);
+ if(m_status&CHSR_DONE){
+ writel(CHOR_CLRDONE, devpriv->mite->mite_io_addr+MITE_CHOR+CHAN_OFFSET(0));
+ //printk("buf_int_count is %d, buf_int_ptr is %d\n",
+ // s->async->buf_int_count,s->async->buf_int_ptr);
+ ni_handle_block_dma(dev);
+ }
+ MDPRINTK("exit mite_handle_interrupt\n");
+ return;
+}
+
+#endif //PCIDMA
+
+static void handle_a_interrupt(comedi_device *dev,unsigned short status)
+{
+ comedi_subdevice *s=dev->subdevices+0;
+ unsigned short ack=0;
+
+ s->async->events = 0;
+
+ /* uncommon interrupt events */
+ if(status&(AI_Overrun_St|AI_Overflow_St|AI_SC_TC_Error_St|AI_SC_TC_St|AI_START1_St)){
+ if(status==0xffff){
+ 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)comedi_done(dev,s);
+ return;
+ }
+ if(status&(AI_Overrun_St|AI_Overflow_St|AI_SC_TC_Error_St)){
+ rt_printk("ni_mio_common: ai error a_status=%04x\n",
+ status);
+ ni_mio_print_status_a(status);
+
+ //TIM 5/11/01
+ win_out(AI_Error_Interrupt_Ack, Interrupt_A_Ack_Register);
+
+ #ifndef PCIDMA
+ ni_handle_fifo_dregs(dev);
+ #endif
+
+ //TIM 4/17/01
+ //win_out(0x0000,Interrupt_A_Enable_Register);
+ //turn off all AI interrupts
+ 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);
+
+ ni_ai_reset(dev,dev->subdevices);//added by tim
+ comedi_done(dev,s);
+ return;
+ }
+ if(status&AI_SC_TC_St){
+#ifdef DEBUG_INTERRUPT
+ rt_printk("ni_mio_common: SC_TC interrupt\n");
+#endif
+#ifdef TRY_BLOCK
+ ni_handle_block(dev);
+#else
+ //for MITE DMA ignore the terminal count from the STC
+ //instead finish up when the MITE asserts DONE
+#ifndef PCIDMA
+
+ if(!devpriv->ai_continuous){
+ ni_handle_fifo_dregs(dev);
+ //win_out(0x0000,Interrupt_A_Enable_Register); TIM 4/17/01
+ 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);
+
+ comedi_done(dev,s);
+ }
+#endif //PCIDMA
+#endif //TRY_BLOCK
+ ack|=AI_SC_TC_Interrupt_Ack;
+ }
+ if(status&AI_START1_St){
+ ack|=AI_START1_Interrupt_Ack;
+ }
+ }
+#ifndef PCIDMA
+ if(status&AI_FIFO_Half_Full_St){
+ ni_handle_fifo_half_full(dev);
+ }
+#endif //PCIDMA
+ if(devpriv->aimode==AIMODE_SCAN && status&AI_STOP_St){
+ ni_handle_fifo_dregs(dev);
+
+ s->async->events |= COMEDI_CB_EOS;
+
+ /* we need to ack the START, also */
+ ack|=AI_STOP_Interrupt_Ack|AI_START_Interrupt_Ack;
+ }
+ if(devpriv->aimode==AIMODE_SAMPLE){
+ ni_handle_fifo_dregs(dev);
+
+ //s->async->events |= COMEDI_CB_SAMPLE;
+ }
+
+ if(ack) ni_writew(ack,Interrupt_A_Ack);
+
+ comedi_event(dev,s,s->async->events);
+}
+
+static void handle_b_interrupt(comedi_device *dev,unsigned short b_status)
+{
+ comedi_subdevice *s=dev->subdevices+1;
+ //unsigned short ack=0;
+
+ if(b_status==0xffff)return;
+ if(b_status&AO_Overrun_St){
+ rt_printk("ni-E: AO FIFO underrun status=0x%04x status2=0x%04x\n",b_status,ni_readw(AO_Status_2));
+ }
+
+ if(b_status&AO_BC_TC_St){
+ rt_printk("ni-E: AO BC_TC status=0x%04x status2=0x%04x\n",b_status,ni_readw(AO_Status_2));
+ }
+
+ if(b_status&AO_FIFO_Request_St)
+ ni_ao_fifo_half_empty(dev,s);
+
+ b_status=ni_readw(AO_Status_1);
+ 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);
+ win_out(0,Interrupt_B_Enable_Register);
+ }
+}
+
+#ifdef DEBUG_STATUS_A
+static char *status_a_strings[]={
+ "passthru0","fifo","G0_gate","G0_TC",
+ "stop","start","sc_tc","start1",
+ "start2","sc_tc_error","overflow","overrun",
+ "fifo_empty","fifo_half_full","fifo_full","interrupt_a"
+};
+
+static void ni_mio_print_status_a(int status)
+{
+ int i;
+
+ rt_printk("A status:");
+ for(i=15;i>=0;i--){
+ if(status&(1<<i)){
+ rt_printk(" %s",status_a_strings[i]);
+ }
+ }
+ rt_printk("\n");
+}
+#endif
+
+#ifdef DEBUG_STATUS_B
+static char *status_b_strings[]={
+ "passthru1","fifo","G1_gate","G1_TC",
+ "UI2_TC","UPDATE","UC_TC","BC_TC",
+ "start1","overrun","start","bc_tc_error",
+ "fifo_empty","fifo_half_full","fifo_full","interrupt_b"
+};
+
+static void ni_mio_print_status_b(int status)
+{
+ int i;
+
+ rt_printk("B status:");
+ for(i=15;i>=0;i--){
+ if(status&(1<<i)){
+ rt_printk(" %s",status_b_strings[i]);
+ }
+ }
+ rt_printk("\n");
+}
+#endif
+
+static void ni_ai_fifo_read(comedi_device *dev,comedi_subdevice *s,
+ sampl_t *data,int n)
+{
+ comedi_async *async = s->async;
+ int i,j;
+ sampl_t d;
+ unsigned int mask;
+
+ mask=(1<<boardtype.adbits)-1;
+ j=async->cur_chan;
+ for(i=0;i<n;i++){
+ d=ni_readw(ADC_FIFO_Data_Register);
+ d^=devpriv->ai_xorlist[j];
+ d&=mask;
+ data[i]=d;
+ j++;
+ if(j>=async->cur_chanlist_len){
+ j=0;
+ async->events |= COMEDI_CB_EOS;
+ }
+ }
+ async->cur_chan=j;
+}
+
+#ifdef PCIDMA
+void ni_munge(comedi_device *dev,comedi_subdevice *s,sampl_t *start, sampl_t *stop)
+{
+ comedi_async *async = s->async;
+ int j;
+ sampl_t *i;
+ unsigned int mask;
+
+ mask=(1<<boardtype.adbits)-1;
+ j=async->cur_chan;
+ for(i=start;i<stop;i++){
+ *i ^=devpriv->ai_xorlist[j];
+ *i &=mask;
+ j++;
+ j %= async->cur_chanlist_len;
+ }
+ async->cur_chan=j;
+}
+
+static void ni_handle_block_dma(comedi_device *dev)
+{
+ MDPRINTK("ni_handle_block_dma\n");
+ //mite_dump_regs(devpriv->mite);
+ mite_dma_disarm(devpriv->mite);
+ //TIM 4/17/01 win_out(0x0000,Interrupt_A_Enable_Register);
+ 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);
+
+ ni_ai_reset(dev,dev->subdevices);
+ comedi_done(dev,dev->subdevices);
+ MDPRINTK("exit ni_handle_block_dma\n");
+}
+#endif
+
+#ifdef TRY_BLOCK
+/* Blocked mode is used to get interrupts at convenient places
+ * to do DMA. It is also useful when you want to count greater
+ * than 16M scans.
+ */
+static void ni_handle_block(comedi_device *dev)
+{
+ int n;
+
+ if(devpriv->ai_continuous){
+ n = devpriv->blocksize;
+ }else{
+ if(devpriv->n_left==0){
+ ni_handle_fifo_dregs(dev);
+ printk("end\n");
+ //TIM 4/17/01 win_out(0x0000,Interrupt_A_Enable_Register);
+ 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);
+ ni_ai_reset(dev,dev->subdevices);
+ comedi_done(dev,dev->subdevices);
+ }else if(devpriv->n_left<=devpriv->blocksize){
+ printk("last block %d\n",devpriv->n_left);
+ n = devpriv->n_left;
+ devpriv->n_left = 0;
+ }else{
+ printk("block %d\n",devpriv->n_left);
+ n = devpriv->blocksize;
+ devpriv->n_left -= devpriv->blocksize;
+ }
+ }
+#if 0
+ {
+ int size=0x10000;
+
+ /* stage number of scans */
+ win_out((size-1)>>16,AI_SC_Load_A_Registers);
+ win_out((size-1)&0xffff,AI_SC_Load_A_Registers+1);
+
+ //mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Continuous;
+ mode1 |= 0xe;
+ win_out(mode1,AI_Mode_1_Register);
+
+ /* load SC (Scan Count) */
+ win_out(AI_SC_Load,AI_Command_1_Register);
+
+ }
+#endif
+}
+#endif
+
+static void ni_handle_fifo_half_full(comedi_device *dev)
+{
+ int n,m;
+ comedi_subdevice *s=dev->subdevices+0;
+ comedi_async *async=s->async;
+
+ /*
+ if we got a fifo_half_full interrupt, we can transfer fifo/2
+ samples without checking the empty flag. It doesn't matter if
+ we transfer the rest of the samples, the performance trade-off
+ is minimal (checking empty flag for a few samples vs. having
+ 1% more interrupts.) At really high speeds, it's better to
+ ignore them.
+
+ */
+
+ n=boardtype.ai_fifo_depth/2;
+
+ /* this makes the assumption that the buffer length is
+ greater than the half-fifo depth. */
+
+ if(async->buf_int_ptr+n*sizeof(sampl_t)>=async->data_len){
+ m=(async->data_len-async->buf_int_ptr)/sizeof(sampl_t);
+ ni_ai_fifo_read(dev,s,async->data+async->buf_int_ptr,m);
+ async->buf_int_count+=m*sizeof(sampl_t);
+ n-=m;
+ async->buf_int_ptr=0;
+
+ async->events |= COMEDI_CB_EOBUF;
+ }
+ ni_ai_fifo_read(dev,s,async->data+async->buf_int_ptr,n);
+ async->buf_int_count+=n*sizeof(sampl_t);
+ async->buf_int_ptr+=n*sizeof(sampl_t);
+
+ async->events |= COMEDI_CB_BLOCK;
+}
+
+/*
+ Empties the AI fifo
+*/
+static void ni_handle_fifo_dregs(comedi_device *dev)
+{
+ comedi_subdevice *s=dev->subdevices+0;
+ sampl_t *data,d;
+ int i,n;
+ int j;
+ unsigned int mask;
+
+ mask=(1<<boardtype.adbits)-1;
+ j=s->async->cur_chan;
+ data=s->async->data+s->async->buf_int_ptr;
+ while(1){
+ n=(s->async->data_len-s->async->buf_int_ptr)/sizeof(sampl_t);
+ for(i=0;i<n;i++){
+ if(ni_readw(AI_Status_1)&AI_FIFO_Empty_St){
+ s->async->cur_chan=j;
+ return;
+ }
+ d=ni_readw(ADC_FIFO_Data_Register);
+ d^=devpriv->ai_xorlist[j];
+ d&=mask;
+ *data=d;
+ j++;
+ if(j>=s->async->cur_chanlist_len){
+ j=0;
+ //s->events |= COMEDI_CB_EOS;
+ }
+ data++;
+ s->async->buf_int_ptr+=sizeof(sampl_t);
+ s->async->buf_int_count+=sizeof(sampl_t);
+ }
+ s->async->buf_int_ptr=0;
+ data=s->async->data;
+ s->async->events |= COMEDI_CB_EOBUF;
+ }
+}
+
+#ifdef PCIDMA
+int ni_ai_setup_block_dma(comedi_device *dev,int frob,int mode1)
+{
+ int n;
+ int len;
+ unsigned long ll_start;
+ comedi_cmd *cmd=&dev->subdevices->async->cmd;
+
+ MDPRINTK("ni_ai_setup_block_dma\n");
+
+ /*Build MITE linked list and configure the MITE
+ * ******WARNING******
+ * There is no error handling here,
+ * the memory buffer *Must* be mlock'ed by the user*/
+
+ len = sizeof(sampl_t)*cmd->stop_arg*cmd->scan_end_arg;
+
+ /*use kvmem if no user buf specified */
+ ll_start = mite_ll_from_kvmem(devpriv->mite,dev->subdevices->async,
+ len);
+
+ mite_setregs(devpriv->mite, ll_start,0,COMEDI_INPUT);
+
+ /*tell the STC to use DMA0 for AI.
+ * Select the MITE DMA channel to use, 0x01=A*/
+ ni_writeb(0x01,AI_AO_Select);
+
+ /* stage number of scans */
+ n = cmd->stop_arg;
+ win_out((n-1)>>16,AI_SC_Load_A_Registers);
+ win_out((n-1)&0xffff,AI_SC_Load_A_Registers+1);
+ win_out((n-1)>>16,AI_SC_Load_B_Registers);
+ win_out((n-1)&0xffff,AI_SC_Load_B_Registers+1);
+
+ /* load SC (Scan Count) */
+ win_out(AI_SC_Load,AI_Command_1_Register);
+
+ mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Continuous;
+ win_out(mode1,AI_Mode_1_Register);
+
+ /*start the MITE*/
+ mite_dma_arm(devpriv->mite);
+
+ MDPRINTK("exit ni_ai_setup_block_dma\n");
+
+ return mode1;
+}
+#endif
+
+#ifdef TRY_BLOCK
+int ni_ai_setup_block(comedi_device *dev,int frob,int mode1)
+{
+ int n;
+ int last=0;
+
+printk("n_left = %d\n",devpriv->n_left);
+ if(devpriv->ai_continuous){
+ n=devpriv->blocksize;
+ last=0;
+ }else{
+ n=devpriv->n_left;
+ if(n>devpriv->blocksize){
+ n=devpriv->blocksize;
+ last=0;
+ }else{
+ last=1;
+ }
+ devpriv->n_left -= n;
+ }
+
+ if(frob){
+ /* stage number of scans */
+ win_out((n-1)>>16,AI_SC_Load_A_Registers);
+ win_out((n-1)&0xffff,AI_SC_Load_A_Registers+1);
+ win_out((n-1)>>16,AI_SC_Load_B_Registers);
+ win_out((n-1)&0xffff,AI_SC_Load_B_Registers+1);
+
+ /* load SC (Scan Count) */
+ win_out(AI_SC_Load,AI_Command_1_Register);
+#if 0
+ if(!last){
+ mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Continuous;
+ }else{
+ mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Trigger_Once;
+ }
+#endif
+ mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Continuous;
+ win_out(mode1,AI_Mode_1_Register);
+
+ }
+
+ return mode1;
+}
+#endif
+
+#ifdef PCIDMA
+int ni_ai_setup_MITE_dma(comedi_device *dev,comedi_cmd *cmd,int mode1)
+{
+ int n,len;
+ unsigned long ll_start;
+ comedi_async *async_mite;
+
+ len = sizeof(sampl_t)*cmd->stop_arg*cmd->scan_end_arg;
+ async_mite=dev->subdevices[cmd->subdev].async;
+ ll_start = mite_ll_from_kvmem(devpriv->mite, async_mite,len);
+ mite_setregs(devpriv->mite, ll_start,0,COMEDI_INPUT);
+
+ /*tell the STC to use DMA0 for AI.
+ Select the MITE DMA channel to use, 0x01=A*/
+ ni_writeb(0x01,AI_AO_Select);
+
+ /* stage number of scans */
+ n = cmd->stop_arg;
+ win_out((n-1)>>16,AI_SC_Load_A_Registers);
+ win_out((n-1)&0xffff,AI_SC_Load_A_Registers+1);
+ win_out((n-1)>>16,AI_SC_Load_B_Registers);
+ win_out((n-1)&0xffff,AI_SC_Load_B_Registers+1);
+
+ /* load SC (Scan Count) */
+ win_out(AI_SC_Load,AI_Command_1_Register);
+
+ mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Continuous;
+ win_out(mode1,AI_Mode_1_Register);
+
+ /*start the MITE*/
+ mite_dma_arm(devpriv->mite);
+ return mode1;
+}
+#endif
+
+/*
+ used for both cancel ioctl and board initialization
+
+ this is pretty harsh for a cancel, but it works...
+ */
+static int ni_ai_reset(comedi_device *dev,comedi_subdevice *s)
+{
+#ifdef PCIDMA
+ mite_dma_disarm(devpriv->mite);
+#endif
+ //TIM 4/17/01 win_out(0x0000,Interrupt_A_Enable_Register);
+ 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);
+
+ win_out(AI_Reset,Joint_Reset_Register);
+
+ win_out(1,ADC_FIFO_Clear);
+
+ /* ai configuration */
+
+ win_out(AI_Configuration_Start,Joint_Reset_Register);
+
+ win_out(0x0000,AI_Command_1_Register); /* reset pulses */
+ win_out(0x000d,AI_Mode_1_Register);
+ win_out(0x0000,AI_Mode_2_Register);
+#if 0
+ win_out((1<<6)|0x0000,AI_Mode_3_Register); /* generate FIFO interrupts on half full */
+#else
+ win_out((0<<6)|0x0000,AI_Mode_3_Register); /* generate FIFO interrupts on non-empty */
+#endif
+ /* TIM 5/11/01
+ 0xA4A0 causes overrun errors at high speeds. 0xA420 fixes it,
+ but I haven't tested to see if it breaks something else. I don't think it would*/
+ #ifdef PCIDMA
+ win_out(0xA420,AI_Personal_Register);
+ #else
+ win_out(0xa4a0,AI_Personal_Register); /* ? */
+ #endif
+ win_out(0x032e,AI_Output_Control_Register);
+ win_out(0x0060,AI_Trigger_Select_Register); /* trigger source */
+
+ /* this should be done in _ai_modeX() */
+ win_out(0x29e0,AI_START_STOP_Select_Register);
+
+ /* the following registers should not be changed, because there
+ * are no backup registers in devpriv. If you want to change
+ * any of these, add a backup register and other appropriate code:
+ * Clock_and_FOUT_Register
+ * AI_Mode_1_Register
+ * AI_Mode_3_Register
+ * AI_Personal_Register
+ * AI_Output_Control_Register
+ * AI_Trigger_Select_Register
+ */
+ win_out(0x3f80,Interrupt_A_Ack_Register); /* clear interrupts */
+
+ win_out(AI_Configuration_End,Joint_Reset_Register);
+
+ return 0;
+}
+
+static int ni_ai_poll(comedi_device *dev,comedi_subdevice *s)
+{
+ unsigned long flags;
+
+ comedi_spin_lock_irqsave(&dev->spinlock,flags);
+ ni_handle_fifo_dregs(dev);
+ comedi_spin_unlock_irqrestore(&dev->spinlock,flags);
+
+ comedi_event(dev,s,s->async->events);
+
+ return s->async->buf_int_count-s->async->buf_user_count;
+}
+
+static void ni_load_channelgain_list(comedi_device *dev,unsigned int n_chan,unsigned int *list,int dither);
+
+#define NI_TIMEOUT 1000
+
+
+static int ni_ai_insn_read(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+{
+ int i,n;
+ int wsave;
+ unsigned int mask,sign;
+
+ wsave=win_save();
+
+ win_out(1,ADC_FIFO_Clear);
+
+ /* interrupt on errors */
+ //TIM 4/17/01 win_out(0x0020,Interrupt_A_Enable_Register);
+ ni_set_bits(dev, Interrupt_A_Enable_Register, AI_Error_Interrupt_Enable,1);
+
+
+ //ni_load_channelgain_list(dev,1,&insn->chanspec,(insn->flags&TRIG_DITHER)==TRIG_DITHER);
+ ni_load_channelgain_list(dev,1,&insn->chanspec,0);
+
+#if 0
+#define NI_TIMEOUT 1000
+#endif
+ mask=(1<<boardtype.adbits)-1;
+ sign=devpriv->ai_xorlist[0];
+ for(n=0;n<insn->n;n++){
+ win_out(1,AI_Command_1_Register);
+ for(i=0;i<NI_TIMEOUT;i++){
+ if(!(ni_readw(AI_Status_1)&AI_FIFO_Empty_St))
+ break;
+ }
+ if(i==NI_TIMEOUT){
+ rt_printk("ni_E: timeout 2\n");
+ win_restore(wsave);
+ return -ETIME;
+ }
+ data[n]=(ni_readw(ADC_FIFO_Data_Register)&mask)^sign;
+ }
+ win_restore(wsave);
+ return insn->n;
+}
+
+
+static void ni_load_channelgain_list(comedi_device *dev,unsigned int n_chan,unsigned int *list,int dither)
+{
+ unsigned int chan,range,aref;
+ unsigned int i;
+ unsigned int hi,lo;
+ unsigned short sign;
+
+ if(n_chan==1){
+ if(devpriv->changain_state && devpriv->changain_spec==list[0]){
+ // ready to go.
+ return;
+ }
+ devpriv->changain_state=1;
+ devpriv->changain_spec=list[0];
+ }else{
+ devpriv->changain_state=0;
+ }
+
+ win_out(1,Configuration_Memory_Clear);
+
+ sign=1<<(boardtype.adbits-1);
+ for(i=0;i<n_chan;i++){
+ chan=CR_CHAN(list[i]);
+ range=CR_RANGE(list[i]);
+ aref=CR_AREF(list[i]);
+
+ /* fix the external/internal range differences */
+ range=ni_gainlkup[boardtype.gainlkup][range];
+ devpriv->ai_xorlist[i]=(range<8)?sign:0;
+
+ hi=ni_modebits1[aref]|(chan&ni_modebits2[aref]);
+ ni_writew(hi,Configuration_Memory_High);
+
+ lo=((i==n_chan-1)?0x8000:0) | ((range&0x8)<<5) | (range&0x7) | (dither<<9);
+ ni_writew(lo,Configuration_Memory_Low);
+ }
+
+ /* prime the channel/gain list */
+
+ win_out(1,AI_Command_1_Register);
+ for(i=0;i<1000;i++){
+ if(!(ni_readw(AI_Status_1)&AI_FIFO_Empty_St)){
+ win_out(1,ADC_FIFO_Clear);
+ return;
+ }
+ //udelay(25);
+ }
+ rt_printk("ni_E: timeout 1\n");
+}
+
+#define TIMER_BASE 50 /* 20 Mhz base */
+
+static int ni_ns_to_timer(int *nanosec,int round_mode)
+{
+ int divider,base;
+
+ base=TIMER_BASE;
+
+ switch(round_mode){
+ case TRIG_ROUND_NEAREST:
+ default:
+ divider=(*nanosec+base/2)/base;
+ break;
+ case TRIG_ROUND_DOWN:
+ divider=(*nanosec)/base;
+ break;
+ case TRIG_ROUND_UP:
+ divider=(*nanosec+base-1)/base;
+ break;
+ }
+
+ *nanosec=base*divider;
+ return divider-1;
+}
+
+static int ni_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd)
+{
+ int err=0;
+ int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp=cmd->start_src;
+ cmd->start_src &= TRIG_NOW|TRIG_INT;
+ if(!cmd->start_src || tmp!=cmd->start_src)err++;
+
+ tmp=cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER|TRIG_EXT;
+ if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;
+
+ tmp=cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER|TRIG_EXT;
+ if(!cmd->convert_src || tmp!=cmd->convert_src)err++;
+
+ tmp=cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++;
+
+ tmp=cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT|TRIG_NONE;
+ if(!cmd->stop_src || tmp!=cmd->stop_src)err++;
+
+ if(err)return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ /* note that mutual compatiblity is not an issue here */
+ if(cmd->start_src!=TRIG_NOW &&
+ cmd->start_src!=TRIG_INT)err++;
+ if(cmd->scan_begin_src!=TRIG_TIMER &&
+ cmd->scan_begin_src!=TRIG_EXT)err++;
+ if(cmd->convert_src!=TRIG_TIMER &&
+ cmd->convert_src!=TRIG_EXT)err++;
+ if(cmd->stop_src!=TRIG_COUNT &&
+ cmd->stop_src!=TRIG_NONE)err++;
+
+ if(err)return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if(cmd->start_arg!=0){
+ /* true for both TRIG_NOW and TRIG_INT */
+ cmd->start_arg=0;
+ err++;
+ }
+ if(cmd->scan_begin_src==TRIG_TIMER){
+ if(cmd->scan_begin_arg<boardtype.ai_speed){
+ cmd->scan_begin_arg=boardtype.ai_speed;
+ err++;
+ }
+ if(cmd->scan_begin_arg>TIMER_BASE*0xffffff){
+ cmd->scan_begin_arg=TIMER_BASE*0xffffff;
+ err++;
+ }
+ }else{
+ /* external trigger */
+ /* should be level/edge, hi/lo specification here */
+ /* should specify multiple external triggers */
+#if 0
+/* XXX This is disabled, since we want to use bits 30 and 31 to
+ * refer to edge/level and hi/lo triggering. */
+ if(cmd->scan_begin_arg>9){
+ cmd->scan_begin_arg=9;
+ err++;
+ }
+#endif
+ }
+ if(cmd->convert_src==TRIG_TIMER){
+ if(cmd->convert_arg<boardtype.ai_speed){
+ cmd->convert_arg=boardtype.ai_speed;
+ err++;
+ }
+ if(cmd->convert_arg>TIMER_BASE*0xffff){
+ cmd->convert_arg=TIMER_BASE*0xffff;
+ err++;
+ }
+ }else{
+ /* external trigger */
+ /* see above */
+#if 0
+/* XXX This is disabled, since we want to use bits 30 and 31 to
+ * refer to edge/level and hi/lo triggering. */
+ if(cmd->convert_arg>9){
+ cmd->convert_arg=9;
+ err++;
+ }
+#endif
+ }
+
+ if(cmd->scan_end_arg!=cmd->chanlist_len){
+ cmd->scan_end_arg=cmd->chanlist_len;
+ err++;
+ }
+ if(cmd->stop_src==TRIG_COUNT){
+ if(cmd->stop_arg>0x00ffffff){
+ cmd->stop_arg=0x00ffffff;
+ err++;
+ }
+ }else{
+ /* TRIG_NONE */
+ if(cmd->stop_arg!=0){
+ cmd->stop_arg=0;
+ err++;
+ }
+ }
+
+ if(err)return 3;
+
+ /* step 4: fix up any arguments */
+
+ if(cmd->scan_begin_src==TRIG_TIMER){
+ tmp=cmd->scan_begin_arg;
+ ni_ns_to_timer(&cmd->scan_begin_arg,cmd->flags&TRIG_ROUND_MASK);
+ if(tmp!=cmd->scan_begin_arg)err++;
+ }
+ if(cmd->convert_src==TRIG_TIMER){
+ tmp=cmd->convert_arg;
+ ni_ns_to_timer(&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;
+ err++;
+ }
+ }
+
+ if(err)return 4;
+
+ return 0;
+}
+
+static int ni_ai_cmd(comedi_device *dev,comedi_subdevice *s)
+{
+ int wsave;
+ comedi_cmd *cmd=&s->async->cmd;
+ int timer;
+ int mode1=0; /* mode1 is needed for both stop and convert */
+ int mode2=0;
+
+ MDPRINTK("ni_ai_cmd\n");
+ wsave = win_save();
+
+ win_out(1,ADC_FIFO_Clear);
+
+ ni_load_channelgain_list(dev,cmd->chanlist_len,cmd->chanlist,
+ (cmd->flags&TRIG_DITHER)==TRIG_DITHER);
+
+ /* start configuration */
+ win_out(AI_Configuration_Start,Joint_Reset_Register);
+
+#ifndef TRY_BLOCK
+ #ifdef PCIDMA
+ ni_ai_setup_MITE_dma(dev,cmd,mode1);
+ #else
+ switch(cmd->stop_src){
+ case TRIG_COUNT:
+ /* stage number of scans */
+ win_out((cmd->stop_arg-1)>>16,AI_SC_Load_A_Registers);
+ win_out((cmd->stop_arg-1)&0xffff,AI_SC_Load_A_Registers+1);
+
+ mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Trigger_Once;
+ win_out(mode1,AI_Mode_1_Register);
+
+ /* load SC (Scan Count) */
+ win_out(AI_SC_Load,AI_Command_1_Register);
+
+ devpriv->ai_continuous = 0;
+
+ break;
+ case TRIG_NONE:
+ /* stage number of scans */
+ win_out(0,AI_SC_Load_A_Registers);
+ win_out(0,AI_SC_Load_A_Registers+1);
+
+ mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Continuous;
+ win_out(mode1,AI_Mode_1_Register);
+
+ /* load SC (Scan Count) */
+ win_out(AI_SC_Load,AI_Command_1_Register);
+
+ devpriv->ai_continuous = 1;
+
+ break;
+ }
+ #endif
+#else
+ devpriv->blocksize = 0x4000;
+ switch(cmd->stop_src){
+ case TRIG_COUNT:
+ devpriv->ai_continuous = 0;
+ devpriv->n_left = cmd->stop_arg;
+ break;
+ case TRIG_NONE:
+ devpriv->ai_continuous = 1;
+ devpriv->n_left = 0;
+ break;
+ }
+
+ mode1 = ni_ai_setup_block(dev,1,mode1);
+#endif
+
+
+ switch(cmd->scan_begin_src){
+ case TRIG_TIMER:
+ /*
+ AI_SI_Special_Trigger_Delay=0
+ AI_Pre_Trigger=0
+ AI_START_STOP_Select_Register:
+ AI_START_Polarity=0 (?) rising edge
+ AI_START_Edge=1 edge triggered
+ AI_START_Sync=1 (?)
+ AI_START_Select=0 SI_TC
+ AI_STOP_Polarity=0 rising edge
+ AI_STOP_Edge=0 level
+ AI_STOP_Sync=1
+ AI_STOP_Select=19 external pin (configuration mem)
+ */
+ win_out(AI_START_Edge|AI_START_Sync|
+ AI_STOP_Select(19)|AI_STOP_Sync,
+ AI_START_STOP_Select_Register);
+
+ timer=ni_ns_to_timer(&cmd->scan_begin_arg,TRIG_ROUND_NEAREST);
+ win_out((timer>>16),AI_SI_Load_A_Registers);
+ win_out((timer&0xffff),AI_SI_Load_A_Registers+1);
+
+ /* AI_SI_Initial_Load_Source=A */
+ mode2 |= AI_SI_Initial_Load_Source&0;
+//mode2 |= AI_SC_Reload_Mode;
+ win_out(mode2,AI_Mode_2_Register);
+
+ /* load SI */
+ win_out(AI_SI_Load,AI_Command_1_Register);
+
+ /* stage freq. counter into SI B */
+ win_out((timer>>16),AI_SI_Load_B_Registers);
+ win_out((timer&0xffff),AI_SI_Load_B_Registers+1);
+
+ break;
+ case TRIG_EXT:
+ {
+ unsigned int reg = 0;
+
+/* XXX duh, these should be moved, but I need to think about it
+ * a bit first. --ds */
+#define COMEDI_TRIG_LEVEL 0
+#define COMEDI_TRIG_EDGE (1<<31)
+#define COMEDI_TRIG_FALLING 0
+#define COMEDI_TRIG_RISING (1<<30)
+
+ if(cmd->scan_begin_arg&COMEDI_TRIG_EDGE)
+ reg |= AI_START_Edge;
+ /* AI_START_Polarity==1 is falling edge */
+ if(!(cmd->scan_begin_arg&COMEDI_TRIG_RISING))
+ reg |= AI_START_Polarity;
+ reg |= AI_START_Sync;
+ reg |= AI_START_Select(1+(cmd->scan_begin_arg&0xf));
+ reg |= AI_STOP_Select(19)|AI_STOP_Sync;
+
+ win_out(reg,AI_START_STOP_Select_Register);
+ break;
+ }
+ }
+
+ switch(cmd->convert_src){
+ case TRIG_TIMER:
+ timer=ni_ns_to_timer(&cmd->convert_arg,TRIG_ROUND_NEAREST);
+ win_out(1,AI_SI2_Load_A_Register); /* 0,0 does not work. */
+ win_out(timer,AI_SI2_Load_B_Register);
+
+ /* AI_SI2_Reload_Mode = alternate */
+ /* AI_SI2_Initial_Load_Source = A */
+ win_out((AI_SI2_Initial_Load_Source&0)|
+ (AI_SI2_Reload_Mode),
+ AI_Mode_2_Register);
+
+ /* AI_SI2_Load */
+ win_out(AI_SI2_Load,AI_Command_1_Register);
+
+ //mode2 |= AI_SI_Reload_Mode(0);
+ /* XXX the AI_SI stuff should go to the scan_begin_src area */
+ mode2 |= AI_SI_Reload_Mode(1);
+ //mode2 |= 0&AI_SI_Initial_Load_Source;
+ mode2 |= AI_SI_Initial_Load_Source;
+ mode2 |= AI_SI2_Reload_Mode; // alternate
+ mode2 |= AI_SI2_Initial_Load_Source; // B
+
+ win_out(mode2,AI_Mode_2_Register);
+
+ break;
+ case TRIG_EXT:
+ mode1 |= AI_CONVERT_Source_Select(1+cmd->convert_arg) |
+ AI_CONVERT_Source_Polarity;
+ win_out(mode1,AI_Mode_1_Register);
+
+ win_out(mode2 | AI_SI2_Reload_Mode,AI_Mode_2_Register);
+
+ mode2 |= AI_SI_Reload_Mode(0);
+ mode2 |= 0&AI_SI_Initial_Load_Source;
+ mode2 |= AI_SI2_Reload_Mode; // alternate
+ mode2 |= AI_SI2_Initial_Load_Source; // B
+
+ win_out(mode2,AI_Mode_2_Register);
+
+ break;
+ }
+
+ if(dev->irq){
+ int bits;
+
+ /* interrupt on FIFO, errors, SC_TC */
+ bits= AI_Error_Interrupt_Enable|
+ AI_SC_TC_Interrupt_Enable;
+
+#ifndef PCIDMA
+ bits|=AI_FIFO_Interrupt_Enable;
+#endif
+
+ if(s->async->cb_mask&COMEDI_CB_EOS){
+ /* wake on end-of-scan */
+ devpriv->aimode=AIMODE_SCAN;
+ }else{
+ devpriv->aimode=AIMODE_HALF_FULL;
+ }
+
+ switch(devpriv->aimode){
+ case AIMODE_HALF_FULL:
+ /*generate FIFO interrupts on half-full */
+ win_out(AI_FIFO_Mode_HF|0x0000,AI_Mode_3_Register);
+ break;
+ case AIMODE_SAMPLE:
+ /*generate FIFO interrupts on non-empty */
+ win_out(AI_FIFO_Mode_NE|0x0000,AI_Mode_3_Register);
+ break;
+ case AIMODE_SCAN:
+ /*generate FIFO interrupts on half-full */
+ win_out(AI_FIFO_Mode_HF|0x0000,AI_Mode_3_Register);
+ bits|=AI_STOP_Interrupt_Enable;
+ break;
+ default:
+ break;
+ }
+
+ win_out(0x3f80,Interrupt_A_Ack_Register); /* clear interrupts */
+
+ //TIM 4/17/01 win_out(bits,Interrupt_A_Enable_Register) ;
+ ni_set_bits(dev, Interrupt_A_Enable_Register, bits, 1);
+
+ MDPRINTK("Interrupt_A_Enable_Register = 0x%04x\n",bits);
+ }else{
+ /* interrupt on nothing */
+ win_out(0x0000,Interrupt_A_Enable_Register) ;
+
+ /* XXX start polling if necessary */
+ MDPRINTK("interrupting on nothing\n");
+ }
+
+ /* end configuration */
+ win_out(AI_Configuration_End,Joint_Reset_Register);
+
+ switch(cmd->scan_begin_src){
+ case TRIG_TIMER:
+ /* AI_SI2_Arm, AI_SI_Arm, AI_DIV_Arm, AI_SC_Arm */
+ win_out(0x1540,AI_Command_1_Register);
+ break;
+ case TRIG_EXT:
+ /* AI_SI2_Arm, AI_DIV_Arm, AI_SC_Arm */
+ win_out(0x1540,AI_Command_1_Register);
+ break;
+ }
+
+ if(cmd->start_src==TRIG_NOW){
+ /* TRIG_NOW */
+ /* AI_START1_Pulse */
+ win_out(AI_START1_Pulse,AI_Command_2_Register);
+ s->async->inttrig=NULL;
+ }else{
+ /* TRIG_INT */
+ s->async->inttrig=ni_ai_inttrig;
+ }
+
+ win_restore(wsave);
+
+ //mite_dump_regs(devpriv->mite);
+ MDPRINTK("exit ni_ai_cmd\n");
+
+ return 0;
+}
+
+static int ni_ai_inttrig(comedi_device *dev,comedi_subdevice *s,
+ unsigned int trignum)
+{
+ int wsave;
+
+ if(trignum!=0)return -EINVAL;
+
+ wsave = win_save();
+
+ win_out(AI_START1_Pulse,AI_Command_2_Register);
+ s->async->inttrig=NULL;
+
+ win_restore(wsave);
+
+ return 1;
+}
+
+static void ni_ao_fifo_load(comedi_device *dev,comedi_subdevice *s,
+ sampl_t *data,int n)
+{
+ int i;
+
+ for(i=0;i<n;i++){
+ ni_writew(data[i],DAC_FIFO_Data);
+ }
+}
+
+
+/*
+ * There's a small problem if the FIFO gets really low and we
+ * don't have the data to fill it. Basically, if after we fill
+ * the FIFO with all the data available, the FIFO is _still_
+ * less than half full, we never clear the interrupt. If the
+ * IRQ is in edge mode, we never get another interrupt, because
+ * this one wasn't cleared. If in level mode, we get flooded
+ * with interrupts that we can't fulfill, because nothing ever
+ * gets put into the buffer.
+ *
+ * This kind of situation is recoverable, but it is easier to
+ * just pretend we had a FIFO underrun, since there is a good
+ * chance it will happen anyway. This is _not_ the case for
+ * RT code, as RT code might purposely be running close to the
+ * metal. Needs to be fixed eventually.
+ */
+static int ni_ao_fifo_half_empty(comedi_device *dev,comedi_subdevice *s)
+{
+ int n,m;
+
+ n=(s->async->buf_int_count-s->async->buf_user_count)/sizeof(sampl_t);
+ if(n==0)return 0;
+ if(n>boardtype.ao_fifo_depth/2)
+ n=boardtype.ao_fifo_depth/2;
+
+ if(s->async->buf_int_ptr+n*sizeof(sampl_t)>s->async->data_len){
+ m=(s->async->data_len-s->async->buf_int_ptr)/sizeof(sampl_t);
+ ni_ao_fifo_load(dev,s,s->async->data+s->async->buf_int_ptr,m);
+ s->async->buf_int_count+=m*sizeof(sampl_t);
+ s->async->buf_int_ptr=0;
+ n-=m;
+ }
+ ni_ao_fifo_load(dev,s,s->async->data+s->async->buf_int_ptr,n);
+ s->async->buf_int_count+=n*sizeof(sampl_t);
+ s->async->buf_int_ptr+=n*sizeof(sampl_t);
+
+ comedi_bufcheck(dev,s);
+
+ return 1;
+}
+
+static int ni_ao_prep_fifo(comedi_device *dev,comedi_subdevice *s)
+{
+ int n;
+
+ /* reset fifo */
+ win_out(0,DAC_FIFO_Clear);
+
+ /* load some data */
+ n=(s->async->buf_int_count-s->async->buf_user_count)/sizeof(sampl_t);
+ if(n==0)return 0;
+ if(n>boardtype.ao_fifo_depth)
+ n=boardtype.ao_fifo_depth;
+
+ ni_ao_fifo_load(dev,s,s->async->data+s->async->buf_int_ptr,n);
+ s->async->buf_int_count+=n*sizeof(sampl_t);
+ s->async->buf_int_ptr+=n*sizeof(sampl_t);
+
+ return n;
+}
+
+
+static int ni_ao_insn_read(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ data[0] = devpriv->ao[CR_CHAN(insn->chanspec)];
+
+ return 1;
+}
+
+static int ni_ao_insn_write(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ unsigned int conf;
+ unsigned int chan;
+ unsigned int range;
+ unsigned int dat = data[0];
+
+ chan=CR_CHAN(insn->chanspec);
+
+ conf=chan<<8;
+
+ /* XXX check range with current range in flaglist[chan] */
+ /* should update calibration if range changes (ick) */
+
+ range = CR_RANGE(insn->chanspec);
+ if(boardtype.ao_unipolar){
+ conf |= (range&1)^1;
+ }
+ conf |= (range&2)<<1;
+
+#if 0
+ /* XXX oops. forgot flags in insn! */
+ /* not all boards can deglitch, but this shouldn't hurt */
+ if(insn->flags & TRIG_DEGLITCH)
+ conf |= 2;
+#endif
+
+ /* analog reference */
+ /* AREF_OTHER connects AO ground to AI ground, i think */
+ conf |= (CR_AREF(insn->chanspec)==AREF_OTHER)? 8 : 0;
+
+ ni_writew(conf,AO_Configuration);
+
+ devpriv->ao[chan] = dat;
+
+ if(((range&1)==0) || !boardtype.ao_unipolar)
+ dat^=(1<<(boardtype.aobits-1));
+
+ ni_writew(dat,(chan)? DAC1_Direct_Data : DAC0_Direct_Data);
+
+ return 1;
+}
+
+static int ni_ao_cmd(comedi_device *dev,comedi_subdevice *s)
+{
+ comedi_cmd *cmd = &s->async->cmd;
+ unsigned int conf;
+ unsigned int chan;
+ unsigned int range;
+ int trigvar;
+ int i;
+
+ trigvar = ni_ns_to_timer(&cmd->scan_begin_arg,TRIG_ROUND_NEAREST);
+
+ win_out(AO_Disarm,AO_Command_1_Register);
+
+ for(i=0;i<cmd->chanlist_len;i++){
+ chan=CR_CHAN(cmd->chanlist[i]);
+
+ conf=chan<<8;
+
+ /* XXX check range with current range in flaglist[chan] */
+ /* should update calibration if range changes (ick) */
+
+ range = CR_RANGE(cmd->chanlist[i]);
+ conf |= (range&1);
+ conf |= (range&2)<<1;
+
+ /* not all boards can deglitch, but this shouldn't hurt */
+ if(cmd->flags & TRIG_DEGLITCH) /* XXX ? */
+ conf |= 2;
+
+ /* analog reference */
+ /* AREF_OTHER connects AO ground to AI ground, i think */
+ conf |= (CR_AREF(cmd->chanlist[i])==AREF_OTHER)? 8 : 0;
+
+ ni_writew(conf,AO_Configuration);
+ }
+
+ /* user is supposed to write() to buffer before triggering */
+ if(ni_ao_prep_fifo(dev,s)==0)
+ return -EIO;
+
+ win_out(AO_Configuration_Start,Joint_Reset_Register);
+
+ devpriv->ao_mode1|=AO_Trigger_Once;
+ win_out(devpriv->ao_mode1,AO_Mode_1_Register);
+ devpriv->ao_trigger_select&=~(AO_START1_Polarity|AO_START1_Select(-1));
+ devpriv->ao_trigger_select|=AO_START1_Edge|AO_START1_Sync;
+ win_out(devpriv->ao_trigger_select,AO_Trigger_Select_Register);
+ devpriv->ao_mode3&=~AO_Trigger_Length;
+ win_out(devpriv->ao_mode3,AO_Mode_3_Register);
+
+ if(cmd->stop_src==TRIG_NOW){
+ devpriv->ao_mode1|=AO_Continuous;
+ }else{
+ devpriv->ao_mode1&=~AO_Continuous;
+ }
+ win_out(devpriv->ao_mode1,AO_Mode_1_Register);
+ devpriv->ao_mode2&=~AO_BC_Initial_Load_Source;
+ win_out(devpriv->ao_mode2,AO_Mode_2_Register);
+ if(cmd->stop_src==TRIG_NOW){
+ win_out(0xff,AO_BC_Load_A_Register_High);
+ win_out(0xffff,AO_BC_Load_A_Register_Low);
+ }else{
+ win_out(0,AO_BC_Load_A_Register_High);
+ win_out(0,AO_BC_Load_A_Register_Low);
+ }
+ win_out(AO_BC_Load,AO_Command_1_Register);
+ devpriv->ao_mode2&=~AO_UC_Initial_Load_Source;
+ win_out(devpriv->ao_mode2,AO_Mode_2_Register);
+ if(cmd->stop_src==TRIG_NOW){
+ win_out(0xff,AO_UC_Load_A_Register_High);
+ win_out(0xffff,AO_UC_Load_A_Register_Low);
+ win_out(AO_UC_Load,AO_Command_1_Register);
+ win_out(0xff,AO_UC_Load_A_Register_High);
+ win_out(0xffff,AO_UC_Load_A_Register_Low);
+ }else{
+ win_out(0,AO_UC_Load_A_Register_High);
+ win_out(0,AO_UC_Load_A_Register_Low);
+ win_out(AO_UC_Load,AO_Command_1_Register);
+ win_out((cmd->stop_arg-1)>>16,AO_UC_Load_A_Register_High);
+ win_out((cmd->stop_arg-1)&0xffff,AO_UC_Load_A_Register_Low);
+ }
+
+ devpriv->ao_cmd2&=~AO_BC_Gate_Enable;
+ ni_writew(devpriv->ao_cmd2,AO_Command_2);
+ devpriv->ao_mode1&=~(AO_UI_Source_Select(0x1f)|AO_UI_Source_Polarity);
+ win_out(devpriv->ao_mode1,AO_Mode_1_Register);
+ devpriv->ao_mode2&=~(AO_UI_Reload_Mode(3)|AO_UI_Initial_Load_Source);
+ win_out(devpriv->ao_mode2,AO_Mode_2_Register);
+ win_out(0,AO_UI_Load_A_Register_High);
+ win_out(1,AO_UI_Load_A_Register_Low);
+ win_out(AO_UI_Load,AO_Command_1_Register);
+ win_out((trigvar>>16),AO_UI_Load_A_Register_High);
+ win_out((trigvar&0xffff),AO_UI_Load_A_Register_Low);
+
+ if(cmd->scan_end_arg>1){
+ devpriv->ao_mode1|=AO_Multiple_Channels;
+ win_out(AO_Number_Of_Channels(cmd->scan_end_arg-1)|
+ AO_UPDATE_Output_Select(1),
+ AO_Output_Control_Register);
+ }else{
+ devpriv->ao_mode1&=~AO_Multiple_Channels;
+ win_out(AO_Number_Of_Channels(CR_CHAN(cmd->chanlist[0]))|
+ AO_UPDATE_Output_Select(1),
+ AO_Output_Control_Register);
+ }
+ win_out(devpriv->ao_mode1,AO_Mode_1_Register);
+
+ win_out(AO_DAC0_Update_Mode|AO_DAC1_Update_Mode,AO_Command_1_Register);
+
+ devpriv->ao_mode3|=AO_Stop_On_Overrun_Error;
+ win_out(devpriv->ao_mode3,AO_Mode_3_Register);
+
+devpriv->ao_mode2|=AO_FIFO_Mode(1);
+ devpriv->ao_mode2&=~AO_FIFO_Retransmit_Enable;
+ win_out(devpriv->ao_mode2,AO_Mode_2_Register);
+
+ win_out(AO_Configuration_End,Joint_Reset_Register);
+
+ win_out(devpriv->ao_mode3|AO_Not_An_UPDATE,AO_Mode_3_Register);
+ win_out(devpriv->ao_mode3,AO_Mode_3_Register);
+
+ /* wait for DACs to be loaded */
+ udelay(100);
+
+ win_out(devpriv->ao_cmd1|AO_UI_Arm|AO_UC_Arm|AO_BC_Arm|AO_DAC1_Update_Mode|AO_DAC0_Update_Mode,
+ AO_Command_1_Register);
+
+ //TIM 4/17/01 win_out(AO_FIFO_Interrupt_Enable|AO_Error_Interrupt_Enable,Interrupt_B_Enable_Register);
+ ni_set_bits(dev, Interrupt_B_Enable_Register,
+ AO_FIFO_Interrupt_Enable|AO_Error_Interrupt_Enable, 1);
+
+ ni_writew(devpriv->ao_cmd2|AO_START1_Pulse,AO_Command_2);
+
+ return 0;
+}
+
+static int ni_ao_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd)
+{
+ int err=0;
+ int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp=cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if(!cmd->start_src || tmp!=cmd->start_src)err++;
+
+ tmp=cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER;
+ if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;
+
+ tmp=cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if(!cmd->convert_src || tmp!=cmd->convert_src)err++;
+
+ tmp=cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++;
+
+ tmp=cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT|TRIG_NONE;
+ if(!cmd->stop_src || tmp!=cmd->stop_src)err++;
+
+ if(err)return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if(cmd->stop_src!=TRIG_COUNT &&
+ cmd->stop_src!=TRIG_NONE)err++;
+
+ if(err)return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if(cmd->start_arg!=0){
+ cmd->start_arg=0;
+ err++;
+ }
+#if 0
+ /* XXX need ao_speed */
+ if(cmd->scan_begin_arg<boardtype.ao_speed){
+ cmd->scan_begin_arg=boardtype.ao_speed;
+ err++;
+ }
+#endif
+ if(cmd->scan_begin_arg>TIMER_BASE*0xffffff){ /* XXX check */
+ cmd->scan_begin_arg=TIMER_BASE*0xffffff;
+ err++;
+ }
+ if(cmd->convert_arg!=0){
+ cmd->convert_arg=0;
+ err++;
+ }
+ if(cmd->scan_end_arg!=cmd->chanlist_len){
+ cmd->scan_end_arg=cmd->chanlist_len;
+ err++;
+ }
+ if(cmd->stop_src==TRIG_COUNT){ /* XXX check */
+ if(cmd->stop_arg>0x00ffffff){
+ cmd->stop_arg=0x00ffffff;
+ err++;
+ }
+ }else{
+ /* TRIG_NONE */
+ if(cmd->stop_arg!=0){
+ cmd->stop_arg=0;
+ err++;
+ }
+ }
+
+ if(err)return 3;
+
+ /* step 4: fix up any arguments */
+
+ tmp = cmd->scan_begin_arg;
+ ni_ns_to_timer(&cmd->scan_begin_arg,cmd->flags&TRIG_ROUND_MASK);
+ if(tmp!=cmd->scan_begin_arg)err++;
+
+ if(err)return 4;
+
+ return 0;
+}
+
+
+static int ni_dio_insn_config(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+#ifdef DEBUG_DIO
+ printk("ni_dio_insn_config() chan=%d io=%d\n",
+ CR_CHAN(insn->chanspec),data[0]);
+#endif
+ if(insn->n!=1)return -EINVAL;
+ switch(data[0]){
+ case COMEDI_OUTPUT:
+ s->io_bits |= 1<<CR_CHAN(insn->chanspec);
+ break;
+ case COMEDI_INPUT:
+ s->io_bits &= ~(1<<CR_CHAN(insn->chanspec));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ devpriv->dio_control &= ~DIO_Pins_Dir_Mask;
+ devpriv->dio_control |= DIO_Pins_Dir(s->io_bits);
+ win_out(devpriv->dio_control,DIO_Control_Register);
+
+ return 1;
+}
+
+static int ni_dio_insn_bits(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+#ifdef DEBUG_DIO
+ printk("ni_dio_insn_bits() mask=0x%x bits=0x%x\n",data[0],data[1]);
+#endif
+ if(insn->n!=2)return -EINVAL;
+ if(data[0]){
+ s->state &= ~data[0];
+ s->state |= (data[0]&data[1]);
+ devpriv->dio_output &= ~DIO_Parallel_Data_Mask;
+ devpriv->dio_output |= DIO_Parallel_Data_Out(s->state);
+ win_out(devpriv->dio_output,DIO_Output_Register);
+ }
+ data[1] = ni_readw(DIO_Parallel_Input);
+
+ return 2;
+}
+
+static void mio_common_detach(comedi_device *dev)
+{
+ if(dev->subdevices && boardtype.has_8255)
+ subdev_8255_cleanup(dev,dev->subdevices+3);
+}
+
+static int ni_E_init(comedi_device *dev,comedi_devconfig *it)
+{
+ comedi_subdevice *s;
+
+ dev->n_subdevices=7;
+
+ if(alloc_subdevices(dev)<0)
+ return -ENOMEM;
+
+ /* analog input subdevice */
+
+ s=dev->subdevices+0;
+ dev->read_subdev=s;
+ s->type=COMEDI_SUBD_AI;
+ s->subdev_flags=SDF_READABLE|SDF_RT|SDF_GROUND|SDF_COMMON|SDF_DIFF|SDF_OTHER;
+ s->subdev_flags|=SDF_DITHER;
+ s->n_chan=boardtype.n_adchan;
+ 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->do_cmdtest=ni_ai_cmdtest;
+ s->do_cmd=ni_ai_cmd;
+ s->cancel=ni_ai_reset;
+ s->poll=ni_ai_poll;
+
+ /* analog output subdevice */
+
+ s=dev->subdevices+1;
+ if(boardtype.n_aochan){
+ dev->write_subdev=s;
+ s->type=COMEDI_SUBD_AO;
+ s->subdev_flags=SDF_WRITEABLE|SDF_RT|SDF_DEGLITCH|SDF_GROUND|SDF_OTHER;
+ s->n_chan=boardtype.n_aochan;
+ s->maxdata=(1<<boardtype.aobits)-1;
+ if(boardtype.ao_unipolar){
+ s->range_table=&range_ni_E_ao_ext; /* XXX wrong for some boards */
+ }else{
+ s->range_table=&range_bipolar10;
+ }
+ s->insn_read=ni_ao_insn_read;
+ s->insn_write=ni_ao_insn_write;
+ s->do_cmd=ni_ao_cmd;
+ s->do_cmdtest=ni_ao_cmdtest;
+ s->len_chanlist = 2;
+ }else{
+ s->type=COMEDI_SUBD_UNUSED;
+ }
+
+ /* digital i/o subdevice */
+
+ s=dev->subdevices+2;
+ s->type=COMEDI_SUBD_DIO;
+ s->subdev_flags=SDF_WRITEABLE|SDF_READABLE|SDF_RT;
+ s->n_chan=8;
+ s->maxdata=1;
+ s->range_table=&range_digital;
+ s->io_bits=0; /* all bits input */
+ s->insn_bits=ni_dio_insn_bits;
+ s->insn_config=ni_dio_insn_config;
+
+ /* dio setup */
+ devpriv->dio_control = DIO_Pins_Dir(s->io_bits);
+ win_out(devpriv->dio_control,DIO_Control_Register);
+
+ /* 8255 device */
+ s=dev->subdevices+3;
+ if(boardtype.has_8255){
+ subdev_8255_init(dev,s,ni_8255_callback,dev);
+ }else{
+ s->type=COMEDI_SUBD_UNUSED;
+ }
+ /* XXX */
+
+ /* general purpose counter/timer device */
+ s=dev->subdevices+4;
+ s->type=COMEDI_SUBD_COUNTER;
+ s->subdev_flags=SDF_READABLE|SDF_WRITEABLE;
+ s->insn_read=ni_gpct_insn_read;
+ s->insn_config=ni_gpct_insn_config;
+ s->n_chan=1; /* XXX */
+ s->maxdata=1;
+
+ s=dev->subdevices+4;
+ s->type=COMEDI_SUBD_COUNTER;
+ s->subdev_flags=SDF_READABLE|SDF_WRITEABLE;
+ s->insn_read= ni_gpct_insn_read;
+ s->insn_write= ni_gpct_insn_write;
+ s->insn_config=ni_gpct_insn_config;
+ s->n_chan=2;
+ s->maxdata=1;
+ devpriv->an_trig_etc_reg = 0;
+ GPCT_Reset(dev,0);
+ GPCT_Reset(dev,1);
+
+ /* calibration subdevice -- ai and ao */
+ s=dev->subdevices+5;
+ s->type=COMEDI_SUBD_CALIB;
+ s->subdev_flags=SDF_WRITEABLE|SDF_INTERNAL;
+ caldac_setup(dev,s);
+ s->insn_read=ni_calib_insn_read;
+ s->insn_write=ni_calib_insn_write;
+
+ /* EEPROM */
+ s=dev->subdevices+6;
+ s->type=COMEDI_SUBD_MEMORY;
+ s->subdev_flags=SDF_READABLE|SDF_INTERNAL;
+ s->n_chan=512;
+ s->maxdata=0xff;
+ s->insn_read=ni_eeprom_insn_read;
+
+ /* ai configuration */
+ ni_ai_reset(dev,dev->subdevices+0);
+ win_out(0x1ba0,Clock_and_FOUT_Register);
+
+
+ /* analog output configuration */
+
+ devpriv->ao0p=0x0000;
+ ni_writew(devpriv->ao0p,AO_Configuration);
+ devpriv->ao1p=0x0100;
+ ni_writew(devpriv->ao1p,AO_Configuration);
+ win_out(AO_Configuration_Start,Joint_Reset_Register);
+ win_out(AO_Disarm,AO_Command_1_Register);
+ win_out(0,Interrupt_B_Enable_Register);
+ win_out(0x0010,AO_Personal_Register);
+ ni_writew(0x3f98,Interrupt_B_Ack);
+ win_out(0x1430,AO_Personal_Register);
+ win_out(0,AO_Output_Control_Register);
+ win_out(0,AO_Start_Select_Register);
+ devpriv->ao_cmd1=0;
+ win_out(devpriv->ao_cmd1,AO_Command_1_Register);
+ devpriv->ao_cmd2=0;
+ devpriv->ao_mode1=0;
+ devpriv->ao_mode2=0;
+ devpriv->ao_mode3=0;
+ devpriv->ao_trigger_select=0;
+
+ if(dev->irq){
+ win_out((IRQ_POLARITY<<0) | /* polarity : active high */
+ (0<<1) | /* no interrupt on 3 pins */
+ (1<<11) | /* enable int A */
+ (1<<15) | /* enable int B */
+ (interrupt_pin(dev->irq)<<8) | /* interrupt output pin A */
+ (interrupt_pin(dev->irq)<<12) , /* interrupt output pin B */
+ Interrupt_Control_Register
+ );
+ }
+
+ pfi_setup(dev);
+
+ printk("\n");
+
+ return 0;
+}
+
+
+
+
+/*
+ presents the EEPROM as a subdevice
+*/
+
+static int ni_eeprom_insn_read(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ data[0]=ni_read_eeprom(dev,CR_CHAN(insn->chanspec));
+
+ return 1;
+}
+
+/*
+ reads bytes out of eeprom
+*/
+
+static int ni_read_eeprom(comedi_device *dev,int addr)
+{
+ int bit;
+ int bitstring;
+
+ bitstring=0x0300|((addr&0x100)<<3)|(addr&0xff);
+ ni_writeb_p(0x04,Serial_Command);
+ for(bit=0x8000;bit;bit>>=1){
+ ni_writeb_p(0x04|((bit&bitstring)?0x02:0),Serial_Command);
+ ni_writeb_p(0x05|((bit&bitstring)?0x02:0),Serial_Command);
+ }
+ bitstring=0;
+ for(bit=0x80;bit;bit>>=1){
+ ni_writeb_p(0x04,Serial_Command);
+ ni_writeb_p(0x05,Serial_Command);
+ bitstring|=((ni_readb_p(XXX_Status)&0x01)?bit:0);
+ }
+ ni_writeb_p(0x00,Serial_Command);
+
+ return bitstring;
+}
+
+static void ni_write_caldac(comedi_device *dev,int addr,int val);
+/*
+ calibration subdevice
+*/
+static int ni_calib_insn_write(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ ni_write_caldac(dev,CR_CHAN(insn->chanspec),data[0]);
+ devpriv->caldacs[CR_CHAN(insn->chanspec)] = data[0];
+
+ return 1;
+}
+
+static int ni_calib_insn_read(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ data[0] = devpriv->caldacs[CR_CHAN(insn->chanspec)];
+
+ return 1;
+}
+
+static int pack_mb88341(int addr,int val,int *bitstring);
+static int pack_dac8800(int addr,int val,int *bitstring);
+static int pack_dac8043(int addr,int val,int *bitstring);
+#ifdef PCIMIO
+static int pack_ad8522(int addr,int val,int *bitstring);
+#endif
+
+struct caldac_struct{
+ int n_chans;
+ int n_bits;
+ int (*packbits)(int,int,int *);
+};
+
+static struct caldac_struct caldac_mb88341={ 12, 8, pack_mb88341 };
+static struct caldac_struct caldac_dac8800={ 8, 8, pack_dac8800 };
+static struct caldac_struct caldac_dac8043={ 1, 12, pack_dac8043 };
+#ifdef PCIMIO
+static struct caldac_struct caldac_ad8522={ 2, 12, pack_ad8522 };
+#endif
+
+
+static void caldac_setup(comedi_device *dev,comedi_subdevice *s)
+{
+ int i,j;
+ int n_dacs;
+ int n_chans=0;
+ int n_bits;
+ int diffbits=0;
+
+ if(!boardtype.caldac[0])return;
+ n_bits=boardtype.caldac[0]->n_bits;
+ for(i=0;i<3;i++){
+ if(!boardtype.caldac[i])break;
+ if(boardtype.caldac[i]->n_bits!=n_bits)diffbits=1;
+ n_chans+=boardtype.caldac[i]->n_chans;
+ }
+ n_dacs=i;
+ s->n_chan=n_chans;
+
+ if(diffbits){
+ int chan;
+
+ s->maxdata_list=kmalloc(sizeof(int)*n_chans,GFP_KERNEL);
+ chan=0;
+ for(i=0;i<n_dacs;i++){
+ for(j=0;j<boardtype.caldac[i]->n_chans;j++){
+ s->maxdata_list[chan]=
+ (1<<boardtype.caldac[i]->n_bits)-1;
+ chan++;
+ }
+ }
+ }else{
+ s->maxdata=(1<<boardtype.caldac[0]->n_bits)-1;
+ }
+}
+
+static void ni_write_caldac(comedi_device *dev,int addr,int val)
+{
+ int loadbit=0,bits=0,bit,bitstring=0;
+ int i;
+
+ for(i=0;i<3;i++){
+ if(!boardtype.caldac[i])return;
+ if(addr<boardtype.caldac[i]->n_chans){
+ bits=boardtype.caldac[i]->packbits(addr,val,&bitstring);
+ loadbit=SerDacLd(i);
+ break;
+ }
+ addr-=boardtype.caldac[i]->n_chans;
+ }
+
+ for(bit=1<<(bits-1);bit;bit>>=1){
+ ni_writeb(((bit&bitstring)?0x02:0),Serial_Command);
+ ni_writeb(1|((bit&bitstring)?0x02:0),Serial_Command);
+ }
+ ni_writeb(loadbit,Serial_Command);
+ ni_writeb(0,Serial_Command);
+}
+
+
+
+static int pack_mb88341(int addr,int val,int *bitstring)
+{
+ /*
+ Fujitsu MB 88341
+ Note that address bits are reversed. Thanks to
+ Ingo Keen for noticing this.
+
+ Note also that the 88341 expects address values from
+ 1-12, whereas we use channel numbers 0-11. The NI
+ docs use 1-12, also, so be careful here.
+ */
+ addr++;
+ *bitstring=((addr&0x1)<<11) |
+ ((addr&0x2)<<9) |
+ ((addr&0x4)<<7) |
+ ((addr&0x8)<<5) |
+ (val&0xff);
+ return 12;
+}
+
+static int pack_dac8800(int addr,int val,int *bitstring)
+{
+ *bitstring=((addr&0x7)<<8)|(val&0xff);
+ return 11;
+}
+
+static int pack_dac8043(int addr,int val,int *bitstring)
+{
+ *bitstring=val&0xfff;
+ return 12;
+}
+
+#ifdef PCIMIO
+static int pack_ad8522(int addr,int val,int *bitstring)
+{
+ *bitstring=(val&0xfff)|(addr ? 0xc000:0xa000);
+ return 16;
+}
+#endif
+
+
+
+/*
+ *
+ * Programmable Function Inputs
+ *
+ */
+
+
+static void pfi_setup(comedi_device *dev)
+{
+ /* currently, we don't output any signals, thus, all
+ the PFI's are input */
+
+ //TIM 4/17/01 win_out(0,IO_Bidirection_Pin_Register);
+ ni_set_bits(dev, IO_Bidirection_Pin_Register, 0x03ff, 0);
+}
+
+
+
+/*
+ *
+ * General Purpose Counter/Timer section
+ *
+ */
+
+/*
+* Low level stuff...Each STC counter has two 24 bit load registers (A&B). Just make
+* it easier to access them.
+*/
+static void GPCT_Load_A(comedi_device *dev, int chan, long value)
+{
+ win_out( (value>>16) & 0x00ff, G_Load_A_Register_High(chan));
+ win_out( value & 0xffff, G_Load_A_Register_Low(chan));
+}
+
+static void GPCT_Load_B(comedi_device *dev, int chan, long value)
+{
+ win_out( (value>>16) & 0x00ff, G_Load_B_Register_High(chan));
+ win_out( value & 0xffff, G_Load_B_Register_Low(chan));
+}
+
+/* Load a value into the counter, using register A as the intermediate step.
+* You might use GPCT_Load_Using_A to load a 0x000000 into a counter
+* reset its value.
+*/
+static void GPCT_Load_Using_A(comedi_device *dev, int chan, long value)
+{
+ devpriv->gpct_mode[chan] &= (~G_Load_Source_Select);
+ win_out( devpriv->gpct_mode[chan],G_Mode_Register(chan));
+ GPCT_Load_A(dev,chan,value);
+ win_out( devpriv->gpct_command[chan]|G_Load,G_Command_Register(chan));
+}
+
+/* Don't use this by itself! The read is not atomic and quite unsafe.
+* If you think you need this function, use GPCT_G_Watch instead.
+*/
+int inline GPCT_G_Read(comedi_device *dev, int chan)
+{
+ int tmp;
+ tmp = win_in( G_Save_Register_High(chan)) << 16;
+ tmp |= win_in(G_Save_Register_Low(chan));
+ return tmp;
+}
+
+/*
+* Read the GPCTs current value.
+* This function is actually straight out of the STC RLPM.
+*/
+int GPCT_G_Watch(comedi_device *dev, int chan)
+{
+ int save1,save2;
+
+ devpriv->gpct_command[chan] &= ~G_Save_Trace;
+ win_out( devpriv->gpct_command[chan],G_Command_Register(chan));
+
+ devpriv->gpct_command[chan] |= G_Save_Trace;
+ win_out( devpriv->gpct_command[chan], G_Command_Register(chan));
+
+ save1 = GPCT_G_Read(dev,chan);
+ save2 = GPCT_G_Read(dev,chan);
+
+ /*if the value changed during the read, try again*/
+ if (save1 != save2) {
+ save1 = GPCT_G_Read(dev,chan);
+ }
+ return save1;
+}
+
+
+int GPCT_Disarm(comedi_device *dev, int chan)
+{
+ win_out( devpriv->gpct_command[chan] | G_Disarm,G_Command_Register(chan));
+ return 0;
+}
+
+
+int GPCT_Arm(comedi_device *dev, int chan)
+{
+ win_out( devpriv->gpct_command[chan] | G_Arm,G_Command_Register(chan));
+ /* If the counter is doing pulse width measurement, then make
+ sure that the counter did not start counting right away. This would
+ indicate that we started acquiring the pulse after it had already
+ started and our measurement would be inaccurate */
+ if(devpriv->gpct_cur_operation[chan] == GPCT_SINGLE_PW){
+ int g_status;
+
+ g_status=win_in(G_Status_Register);
+
+ if(chan == 0){
+ //TIM 5/2/01 possible error with very short pulses
+ if((G0_Counting_St & g_status)|| !(G0_Armed_St&g_status)) {
+ //error: we missed the beginning of the pulse
+ return -EINVAL; //there is probably a more accurate error code...
+ }
+ }else{
+ if((G1_Counting_St & g_status)|| !(G1_Armed_St&g_status)) {
+ //error: we missed the beginning of the pulse
+ return -EINVAL;
+ }
+ }
+ }
+ return 0;
+}
+
+int GPCT_Set_Source(comedi_device *dev,int chan ,int source)
+{
+ //printk("GPCT_Set_Source...");
+ devpriv->gpct_input_select[chan] &= ~G_Source_Select(0x1f);//reset gate to 0
+ switch(source) {
+ case GPCT_INT_CLOCK:
+ devpriv->gpct_input_select[chan] |= G_Source_Select(0);//INT_TIMEBASE
+ break;
+ case GPCT_EXT_PIN:
+ if(chan==0)
+ devpriv->gpct_input_select[chan] |= G_Source_Select(9);//PFI8
+ else
+ devpriv->gpct_input_select[chan] |= G_Source_Select(4);//PFI3
+ break;
+ default:
+ return -EINVAL;
+ }
+ win_out(devpriv->gpct_input_select[chan], G_Input_Select_Register(chan));
+ //printk("exit GPCT_Set_Source\n");
+ return 0;
+}
+
+int GPCT_Set_Gate(comedi_device *dev,int chan ,int gate)
+{
+ //printk("GPCT_Set_Gate...");
+ devpriv->gpct_input_select[chan] &= ~G_Gate_Select(0x1f);//reset gate to 0
+ switch(gate) {
+ case GPCT_NO_GATE:
+ devpriv->gpct_input_select[chan] |= G_Gate_Select(31);//Low
+ devpriv->gpct_mode[chan] |= G_Gate_Polarity;
+ break;
+ case GPCT_EXT_PIN:
+ devpriv->gpct_mode[chan] &= ~G_Gate_Polarity;
+ if(chan==0){
+ devpriv->gpct_input_select[chan] |= G_Gate_Select(10);//PFI9
+ }else{
+ devpriv->gpct_input_select[chan] |= G_Gate_Select(5);//PFI4
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ win_out(devpriv->gpct_input_select[chan], G_Input_Select_Register(chan));
+ win_out(devpriv->gpct_mode[chan], G_Mode_Register(chan));
+ //printk("exit GPCT_Set_Gate\n");
+ return 0;
+}
+
+int GPCT_Set_Direction(comedi_device *dev,int chan,int direction)
+{
+ //printk("GPCT_Set_Direction...");
+
+ devpriv->gpct_command[chan] &= ~G_Up_Down(0x3);
+ switch (direction) {
+ case GPCT_UP:
+ devpriv->gpct_command[chan] |= G_Up_Down(1);
+ break;
+ case GPCT_DOWN:
+ devpriv->gpct_command[chan] |= G_Up_Down(0);
+ break;
+ case GPCT_HWUD:
+ devpriv->gpct_command[chan] |= G_Up_Down(2);
+ break;
+ default:
+ printk("Error direction=0x%08x..",direction);
+ return -EINVAL;
+ }
+ win_out(devpriv->gpct_command[chan], G_Command_Register(chan));
+ //TIM 4/23/01 win_out(devpriv->gpct_mode[chan], G_Mode_Register(chan));
+ //printk("exit GPCT_Set_Direction\n");
+ return 0;
+}
+
+void GPCT_Event_Counting(comedi_device *dev,int chan)
+{
+
+ //NOTE: possible residual bits from multibit masks can corrupt
+ //If you config for several measurements between Resets, watch out!
+
+ //printk("GPCT_Event_Counting...");
+
+ devpriv->gpct_cur_operation[chan] = GPCT_SIMPLE_EVENT;
+
+ // Gating_Mode = 1
+ devpriv->gpct_mode[chan] &= ~(G_Gating_Mode(0x3));
+ devpriv->gpct_mode[chan] |= G_Gating_Mode(1);
+
+ // Trigger_Mode_For_Edge_Gate = 1
+ devpriv->gpct_mode[chan] &= ~(G_Trigger_Mode_For_Edge_Gate(0x3));
+ devpriv->gpct_mode[chan] |= G_Trigger_Mode_For_Edge_Gate(2);
+
+ win_out( devpriv->gpct_mode[chan],G_Mode_Register(chan));
+ //printk("exit GPCT_Event_Counting\n");
+}
+
+void GPCT_Period_Meas(comedi_device *dev, int chan)
+{
+ //printk("GPCT_Period_Meas...");
+
+ devpriv->gpct_cur_operation[chan] = GPCT_SINGLE_PERIOD;
+
+
+ //NOTE: possible residual bits from multibit masks can corrupt
+ //If you config for several measurements between Resets, watch out!
+ devpriv->gpct_mode[chan] &= ~G_OR_Gate;
+ devpriv->gpct_mode[chan] &= ~G_Gate_Select_Load_Source;
+
+ // Output_Mode = 3
+ devpriv->gpct_mode[chan] &= ~(G_Output_Mode(0x3));
+ devpriv->gpct_mode[chan] |= G_Output_Mode(3);
+
+
+ //Gating Mode=2
+ devpriv->gpct_mode[chan] &= ~(G_Gating_Mode(0x3));
+ devpriv->gpct_mode[chan] |= G_Gating_Mode(2);
+
+ // Trigger_Mode_For_Edge_Gate=0
+ devpriv->gpct_mode[chan] &= ~(G_Trigger_Mode_For_Edge_Gate(0x3));
+ devpriv->gpct_mode[chan] |= G_Trigger_Mode_For_Edge_Gate(0);
+
+ devpriv->gpct_mode[chan] |= G_Reload_Source_Switching;
+ devpriv->gpct_mode[chan] &= ~G_Loading_On_Gate;
+ devpriv->gpct_mode[chan] &= ~G_Loading_On_TC;
+ devpriv->gpct_mode[chan] &= ~G_Gate_On_Both_Edges;
+
+ // Stop_Mode = 2
+ devpriv->gpct_mode[chan] &= ~(G_Stop_Mode(0x3));
+ devpriv->gpct_mode[chan] |= G_Stop_Mode(0);
+
+ // Counting_Once = 2
+ devpriv->gpct_mode[chan] &= ~(G_Counting_Once(0x3));
+ devpriv->gpct_mode[chan] |= G_Counting_Once(2);
+
+ // Up_Down = 1
+ devpriv->gpct_command[chan] &= ~(G_Up_Down(0x3));
+ devpriv->gpct_command[chan] |= G_Up_Down(1);
+
+ win_out( devpriv->gpct_mode[chan],G_Mode_Register(chan));
+ win_out( devpriv->gpct_command[chan],G_Command_Register(chan));
+ //printk("exit GPCT_Period_Meas\n");
+}
+
+void GPCT_Pulse_Width_Meas(comedi_device *dev, int chan)
+{
+ //printk("GPCT_Pulse_Width_Meas...");
+
+ devpriv->gpct_cur_operation[chan] = GPCT_SINGLE_PW;
+
+ devpriv->gpct_mode[chan] &= ~G_OR_Gate;
+ devpriv->gpct_mode[chan] &= ~G_Gate_Select_Load_Source;
+
+ // Output_Mode = 3
+ devpriv->gpct_mode[chan] &= ~(G_Output_Mode(0x3));
+ devpriv->gpct_mode[chan] |= G_Output_Mode(3);
+
+ //Gating Mode=1
+ devpriv->gpct_mode[chan] &= ~(G_Gating_Mode(0x3));
+ devpriv->gpct_mode[chan] |= G_Gating_Mode(1);//TIM 4/24/01 was 2
+
+ // Trigger_Mode_For_Edge_Gate=2
+ devpriv->gpct_mode[chan] &= ~(G_Trigger_Mode_For_Edge_Gate(0x3));
+ devpriv->gpct_mode[chan] |= G_Trigger_Mode_For_Edge_Gate(2);//TIM 4/24/01 was 0
+
+
+ devpriv->gpct_mode[chan] |= G_Reload_Source_Switching;//TIM 4/24/01 was 1
+ devpriv->gpct_mode[chan] &= ~G_Loading_On_Gate;//TIM 4/24/01 was 0
+
+ devpriv->gpct_mode[chan] &= ~G_Loading_On_TC;
+ devpriv->gpct_mode[chan] &= ~G_Gate_On_Both_Edges;
+
+ // Stop_Mode = 0
+ devpriv->gpct_mode[chan] &= ~(G_Stop_Mode(0x3));
+ devpriv->gpct_mode[chan] |= G_Stop_Mode(0);
+
+ // Counting_Once = 2
+ devpriv->gpct_mode[chan] &= ~(G_Counting_Once(0x3));
+ devpriv->gpct_mode[chan] |= G_Counting_Once(2);
+
+ // Up_Down = 1
+ devpriv->gpct_command[chan] &= ~(G_Up_Down(0x3));
+ devpriv->gpct_command[chan] |= G_Up_Down(1);
+
+ win_out( devpriv->gpct_mode[chan],G_Mode_Register(chan));
+ win_out( devpriv->gpct_command[chan],G_Command_Register(chan));
+
+ //printk("exit GPCT_Pulse_Width_Meas\n");
+}
+
+/* GPCT_Gen_Single_Pulse() creates pulse of length pulsewidth which starts after the Arm
+signal is sent. The pulse is delayed by the value already in the counter. This function could
+be modified to send a pulse in response to a trigger event at its gate.*/
+void GPCT_Gen_Single_Pulse(comedi_device *dev, int chan, unsigned int length)
+{
+ //printk("GPCT_Gen_Cont...");
+
+ devpriv->gpct_cur_operation[chan] = GPCT_SINGLE_PULSE_OUT;
+
+ // Set length of the pulse
+ GPCT_Load_B(dev,chan, length-1);
+
+ //Load next time using B, This is reset by GPCT_Load_Using_A()
+ devpriv->gpct_mode[chan] |= G_Load_Source_Select;
+
+ devpriv->gpct_mode[chan] &= ~G_OR_Gate;
+ devpriv->gpct_mode[chan] &= ~G_Gate_Select_Load_Source;
+
+ // Output_Mode = 3
+ devpriv->gpct_mode[chan] &= ~(G_Output_Mode(0x3));
+ devpriv->gpct_mode[chan] |= G_Output_Mode(2); //TIM 4/26/01 was 3
+
+ //Gating Mode=0 for untriggered single pulse
+ devpriv->gpct_mode[chan] &= ~(G_Gating_Mode(0x3));
+ devpriv->gpct_mode[chan] |= G_Gating_Mode(0); //TIM 4/25/01 was 1
+
+ // Trigger_Mode_For_Edge_Gate=0
+ devpriv->gpct_mode[chan] &= ~(G_Trigger_Mode_For_Edge_Gate(0x3));
+ devpriv->gpct_mode[chan] |= G_Trigger_Mode_For_Edge_Gate(2);
+
+
+ devpriv->gpct_mode[chan] |= G_Reload_Source_Switching;
+ devpriv->gpct_mode[chan] &= ~G_Loading_On_Gate;
+ devpriv->gpct_mode[chan] |= G_Loading_On_TC; //TIM 4/25/01
+ devpriv->gpct_mode[chan] &= ~G_Gate_On_Both_Edges;
+
+ // Stop_Mode = 2
+ devpriv->gpct_mode[chan] &= ~(G_Stop_Mode(0x3));
+ devpriv->gpct_mode[chan] |= G_Stop_Mode(2); //TIM 4/25/01
+
+ // Counting_Once = 2
+ devpriv->gpct_mode[chan] &= ~(G_Counting_Once(0x3));
+ devpriv->gpct_mode[chan] |= G_Counting_Once(1); //TIM 4/25/01
+
+ // Up_Down = 1
+ devpriv->gpct_command[chan] &= ~(G_Up_Down(0x3));
+ devpriv->gpct_command[chan] |= G_Up_Down(0); //TIM 4/25/01 was 1
+
+ win_out( devpriv->gpct_mode[chan],G_Mode_Register(chan));
+ win_out( devpriv->gpct_command[chan],G_Command_Register(chan));
+
+ //printk("exit GPCT_Gen_Cont\n");
+}
+
+void GPCT_Gen_Cont_Pulse(comedi_device *dev, int chan, unsigned int length)
+{
+ //printk("GPCT_Gen_Cont...");
+
+ devpriv->gpct_cur_operation[chan] = GPCT_CONT_PULSE_OUT;
+
+ // Set length of the pulse
+ GPCT_Load_B(dev,chan, length-1);
+
+ //Load next time using B, This is reset by GPCT_Load_Using_A()
+ devpriv->gpct_mode[chan] |= G_Load_Source_Select;
+
+ devpriv->gpct_mode[chan] &= ~G_OR_Gate;
+ devpriv->gpct_mode[chan] &= ~G_Gate_Select_Load_Source;
+
+ // Output_Mode = 3
+ devpriv->gpct_mode[chan] &= ~(G_Output_Mode(0x3));
+ devpriv->gpct_mode[chan] |= G_Output_Mode(2); //TIM 4/26/01 was 3
+
+ //Gating Mode=0 for untriggered single pulse
+ devpriv->gpct_mode[chan] &= ~(G_Gating_Mode(0x3));
+ devpriv->gpct_mode[chan] |= G_Gating_Mode(0); //TIM 4/26/01 was 0
+
+ // Trigger_Mode_For_Edge_Gate=0
+ devpriv->gpct_mode[chan] &= ~(G_Trigger_Mode_For_Edge_Gate(0x3));
+ devpriv->gpct_mode[chan] |= G_Trigger_Mode_For_Edge_Gate(2);
+
+
+ devpriv->gpct_mode[chan] |= G_Reload_Source_Switching;
+ devpriv->gpct_mode[chan] &= ~G_Loading_On_Gate;
+ devpriv->gpct_mode[chan] |= G_Loading_On_TC;
+ devpriv->gpct_mode[chan] &= ~G_Gate_On_Both_Edges;
+
+ // Stop_Mode = 2
+ devpriv->gpct_mode[chan] &= ~(G_Stop_Mode(0x3));
+ devpriv->gpct_mode[chan] |= G_Stop_Mode(0); //TIM 4/26/01
+
+ // Counting_Once = 2
+ devpriv->gpct_mode[chan] &= ~(G_Counting_Once(0x3));
+ devpriv->gpct_mode[chan] |= G_Counting_Once(0); //TIM 4/26/01
+
+ // Up_Down = 1
+ devpriv->gpct_command[chan] &= ~(G_Up_Down(0x3));
+ devpriv->gpct_command[chan] |= G_Up_Down(0);
+
+ //TIM 4/26/01
+ //This seems pretty unsafe since I don't think it is cleared anywhere.
+ //I don't think this is working
+ //devpriv->gpct_command[chan] &= ~G_Bank_Switch_Enable;
+ //devpriv->gpct_command[chan] &= ~G_Bank_Switch_Mode;
+
+
+ win_out( devpriv->gpct_mode[chan],G_Mode_Register(chan));
+ win_out( devpriv->gpct_command[chan],G_Command_Register(chan));
+
+ //printk("exit GPCT_Gen_Cont\n");
+}
+
+void GPCT_Reset(comedi_device *dev, int chan)
+{
+ unsigned long irqflags;
+ int temp_ack_reg=0;
+
+ //printk("GPCT_Reset...");
+ devpriv->gpct_cur_operation[chan] = GPCT_RESET;
+
+ switch (chan) {
+ case 0:
+ //note: I need to share the soft copies of the Enable Register with the ISRs.
+ // so I'm using comedi_spin_lock_irqsave() to guard this section of code
+ win_out(G0_Reset,Joint_Reset_Register);
+ comedi_spin_lock_irqsave(&dev->spinlock, irqflags);
+ ni_set_bits(dev,Interrupt_A_Enable_Register,G0_TC_Interrupt_Enable, 0);
+ ni_set_bits(dev,Interrupt_A_Enable_Register,G0_Gate_Interrupt_Enable,0);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irqflags);
+ temp_ack_reg |= G0_Gate_Error_Confirm;
+ temp_ack_reg |= G0_TC_Error_Confirm;
+ temp_ack_reg |= G0_TC_Interrupt_Ack;
+ temp_ack_reg |= G0_Gate_Interrupt_Ack;
+ win_out(temp_ack_reg,Interrupt_A_Ack_Register);
+
+ //problem...this interferes with the other ctr...
+ devpriv->an_trig_etc_reg |= GPFO_0_Output_Enable;
+ win_out(devpriv->an_trig_etc_reg, Analog_Trigger_Etc_Register);
+ break;
+ case 1:
+ win_out(G1_Reset,Joint_Reset_Register);
+ comedi_spin_lock_irqsave(&dev->spinlock, irqflags);
+ ni_set_bits(dev,Interrupt_B_Enable_Register,G1_TC_Interrupt_Enable, 0);
+ ni_set_bits(dev,Interrupt_B_Enable_Register,G0_Gate_Interrupt_Enable,0);
+ comedi_spin_unlock_irqrestore(&dev->spinlock, irqflags);
+ temp_ack_reg |= G1_Gate_Error_Confirm;
+ temp_ack_reg |= G1_TC_Error_Confirm;
+ temp_ack_reg |= G1_TC_Interrupt_Ack;
+ temp_ack_reg |= G1_Gate_Interrupt_Ack;
+ win_out(temp_ack_reg,Interrupt_B_Ack_Register);
+
+ devpriv->an_trig_etc_reg |= GPFO_1_Output_Enable;
+ win_out(devpriv->an_trig_etc_reg, Analog_Trigger_Etc_Register);
+ break;
+ };
+
+ devpriv->gpct_mode[chan] = 0;
+ devpriv->gpct_input_select[chan] = 0;
+ devpriv->gpct_command[chan] = 0;
+
+ devpriv->gpct_command[chan] |= G_Synchronized_Gate;
+
+ win_out( devpriv->gpct_mode[chan],G_Mode_Register(chan));
+ win_out( devpriv->gpct_input_select[chan],G_Input_Select_Register(chan));
+ win_out( 0,G_Autoincrement_Register(chan));
+
+ //printk("exit GPCT_Reset\n");
+}
+
+static int ni_gpct_insn_config(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ int retval=0;
+ //printk("data[0] is 0x%08x, data[1] is 0x%08x\n",data[0],data[1]);
+ switch(data[0]){
+ case GPCT_RESET:
+ if(insn->n!=1)return -EINVAL;
+ GPCT_Reset(dev,insn->chanspec);
+ break;
+ case GPCT_SET_SOURCE:
+ if(insn->n!=2)return -EINVAL;
+ retval=GPCT_Set_Source(dev,insn->chanspec,data[1]);
+ break;
+ case GPCT_SET_GATE:
+ if(insn->n!=2)return -EINVAL;
+ retval=GPCT_Set_Gate(dev,insn->chanspec,data[1]);
+ break;
+ case GPCT_SET_DIRECTION:
+ if(insn->n!=2) return -EINVAL;
+ retval=GPCT_Set_Direction(dev,insn->chanspec,data[1]);
+ break;
+ case GPCT_GET_INT_CLK_FRQ:
+ if(insn->n!=2) return -EINVAL;
+ //There are actually 2 internal clocks on the STC, we always
+ //use the fast 20MHz one at this time. Tim Ousley 5/1/01
+ //NOTE: This is not the final interface, ideally the user
+ //will never need to know the int. clk. freq.
+ data[1]=50;//50ns = 20MHz = internal timebase of STC
+ break;
+ case GPCT_SET_OPERATION:
+ //TIM 5/1/01 if((insn->n<2)||(insn->n>3))return -EINVAL;
+ switch(data[1]){
+ case GPCT_SIMPLE_EVENT:
+ GPCT_Event_Counting(dev,insn->chanspec);
+ break;
+ case GPCT_SINGLE_PERIOD:
+ GPCT_Period_Meas(dev,insn->chanspec);
+ break;
+ case GPCT_SINGLE_PW:
+ GPCT_Pulse_Width_Meas(dev,insn->chanspec);
+ break;
+ case GPCT_SINGLE_PULSE_OUT:
+ GPCT_Gen_Single_Pulse(dev,insn->chanspec,data[2]);
+ break;
+ case GPCT_CONT_PULSE_OUT:
+ GPCT_Gen_Cont_Pulse(dev,insn->chanspec,data[2]);
+ break;
+ default:
+ printk("unsupported GPCT operation!\n");
+ return -EINVAL;
+ }
+ break;
+ case GPCT_ARM:
+ if(insn->n!=1)return -EINVAL;
+ retval=GPCT_Arm(dev,insn->chanspec);
+ break;
+ case GPCT_DISARM:
+ if(insn->n!=1)return -EINVAL;
+ retval=GPCT_Disarm(dev,insn->chanspec);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ //catch any errors from return values
+ if(retval==0){
+ return insn->n;
+ }else{
+ if(data[0]!=GPCT_ARM){
+ printk("error: retval was %d\n",retval);
+ printk("data[0] is 0x%08x, data[1] is 0x%08x\n",data[0],data[1]);
+ }
+
+ return retval;
+ }
+}
+
+static int ni_gpct_insn_read(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data) {
+
+ int chan=insn->chanspec;
+ int cur_op = devpriv->gpct_cur_operation[chan];
+
+ //printk("in ni_gpct_insn_read, n=%d, data[0]=%d\n",insn->chanspec,data[0]);
+ if(insn->n!=1)return -EINVAL;
+
+ data[0] = GPCT_G_Watch(dev,insn->chanspec);
+
+ /* for certain modes (period and pulse width measurment), the value
+ in the counter is not valid until the counter stops. If the value is
+ invalid, return a 0 */
+ if((cur_op == GPCT_SINGLE_PERIOD) || (cur_op == GPCT_SINGLE_PW)){
+ /* is the counter still running? */
+ if(win_in(G_Status_Register) & (chan?G1_Counting_St:G0_Counting_St))
+ data[0]=0;
+ }
+ return 1;
+}
+
+static int ni_gpct_insn_write(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data) {
+
+ //printk("in ni_gpct_insn_write");
+ if(insn->n!=1)return -EINVAL;
+ GPCT_Load_Using_A(dev,insn->chanspec,data[0]);
+ return 1;
+}
+
+
+static int ni_8255_callback(int dir,int port,int data,void *arg)
+{
+ comedi_device *dev=arg;
+
+ if(dir){
+ ni_writeb(data,Port_A+2*port);
+ return 0;
+ }else{
+ return ni_readb(Port_A+2*port);
+ }
+}