diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c')
-rw-r--r-- | drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c | 2697 |
1 files changed, 2697 insertions, 0 deletions
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c new file mode 100644 index 00000000000..68eb8dfd513 --- /dev/null +++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c @@ -0,0 +1,2697 @@ +/** +@verbatim + +Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module. + + ADDI-DATA GmbH + Dieselstrasse 3 + D-77833 Ottersweier + Tel: +19(0)7223/9493-0 + Fax: +49(0)7223/9493-92 + http://www.addi-data-com + info@addi-data.com + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +You shoud also find the complete GPL in the COPYING file accompanying this source code. + +@endverbatim +*/ +/* + +-----------------------------------------------------------------------+ + | (C) ADDI-DATA GmbH Dieselstrasse 3 D-77833 Ottersweier | + +-----------------------------------------------------------------------+ + | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com | + | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com | + +-----------------------------------------------------------------------+ + | Project : APCI-3120 | Compiler : GCC | + | Module name : hwdrv_apci3120.c| Version : 2.96 | + +-------------------------------+---------------------------------------+ + | Project manager: Eric Stolz | Date : 02/12/2002 | + +-----------------------------------------------------------------------+ + | Description :APCI3120 Module. Hardware abstraction Layer for APCI3120| + +-----------------------------------------------------------------------+ + | UPDATE'S | + +-----------------------------------------------------------------------+ + | Date | Author | Description of updates | + +----------+-----------+------------------------------------------------+ + | | | | + | | | | + +----------+-----------+------------------------------------------------+ +*/ + +#include "hwdrv_apci3120.h" +static UINT ui_Temp = 0; + +// FUNCTION DEFINITIONS + +/* ++----------------------------------------------------------------------------+ +| ANALOG INPUT SUBDEVICE | ++----------------------------------------------------------------------------+ +*/ + +/* ++----------------------------------------------------------------------------+ +| Function name :int i_APCI3120_InsnConfigAnalogInput(struct comedi_device *dev,| +| struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) | +| | ++----------------------------------------------------------------------------+ +| Task : Calls card specific function | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| struct comedi_subdevice *s | +| struct comedi_insn *insn | +| unsigned int *data | ++----------------------------------------------------------------------------+ +| Return Value : | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_InsnConfigAnalogInput(struct comedi_device * dev, struct comedi_subdevice * s, + struct comedi_insn * insn, unsigned int * data) +{ + UINT i; + + if ((data[0] != APCI3120_EOC_MODE) && (data[0] != APCI3120_EOS_MODE)) + return -1; + + // Check for Conversion time to be added ?? + devpriv->ui_EocEosConversionTime = data[2]; + + if (data[0] == APCI3120_EOS_MODE) { + + //Test the number of the channel + for (i = 0; i < data[3]; i++) { + + if (CR_CHAN(data[4 + i]) >= this_board->i_NbrAiChannel) { + printk("bad channel list\n"); + return -2; + } + } + + devpriv->b_InterruptMode = APCI3120_EOS_MODE; + + if (data[1]) { + devpriv->b_EocEosInterrupt = APCI3120_ENABLE; + } else + devpriv->b_EocEosInterrupt = APCI3120_DISABLE; + // Copy channel list and Range List to devpriv + + devpriv->ui_AiNbrofChannels = data[3]; + for (i = 0; i < devpriv->ui_AiNbrofChannels; i++) { + devpriv->ui_AiChannelList[i] = data[4 + i]; + } + + } else // EOC + { + devpriv->b_InterruptMode = APCI3120_EOC_MODE; + if (data[1]) { + devpriv->b_EocEosInterrupt = APCI3120_ENABLE; + } else { + devpriv->b_EocEosInterrupt = APCI3120_DISABLE; + } + } + + return insn->n; +} + +/* ++----------------------------------------------------------------------------+ +| Function name :int i_APCI3120_InsnReadAnalogInput(struct comedi_device *dev, | +| struct comedi_subdevice *s,struct comedi_insn *insn, unsigned int *data) | +| | ++----------------------------------------------------------------------------+ +| Task : card specific function | +| Reads analog input in synchronous mode | +| EOC and EOS is selected as per configured | +| if no conversion time is set uses default conversion | +| time 10 microsec. | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| struct comedi_subdevice *s | +| struct comedi_insn *insn | +| unsigned int *data | ++----------------------------------------------------------------------------+ +| Return Value : | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_InsnReadAnalogInput(struct comedi_device * dev, struct comedi_subdevice * s, + struct comedi_insn * insn, unsigned int * data) +{ + USHORT us_ConvertTiming, us_TmpValue, i; + BYTE b_Tmp; + + // fix convertion time to 10 us + if (!devpriv->ui_EocEosConversionTime) { + printk("No timer0 Value using 10 us\n"); + us_ConvertTiming = 10; + } else + us_ConvertTiming = (USHORT) (devpriv->ui_EocEosConversionTime / 1000); // nano to useconds + + // this_board->i_hwdrv_InsnReadAnalogInput(dev,us_ConvertTiming,insn->n,&insn->chanspec,data,insn->unused[0]); + + // Clear software registers + devpriv->b_TimerSelectMode = 0; + devpriv->b_ModeSelectRegister = 0; + devpriv->us_OutputRegister = 0; +// devpriv->b_DigitalOutputRegister=0; + + if (insn->unused[0] == 222) // second insn read + { + + for (i = 0; i < insn->n; i++) { + data[i] = devpriv->ui_AiReadData[i]; + } + + } else { + devpriv->tsk_Current = current; // Save the current process task structure + //Testing if board have the new Quartz and calculate the time value + //to set in the timer + + us_TmpValue = + (USHORT) inw(devpriv->iobase + APCI3120_RD_STATUS); + + //EL250804: Testing if board APCI3120 have the new Quartz or if it is an APCI3001 + if ((us_TmpValue & 0x00B0) == 0x00B0 + || !strcmp(this_board->pc_DriverName, "apci3001")) { + us_ConvertTiming = (us_ConvertTiming * 2) - 2; + } else { + us_ConvertTiming = + ((us_ConvertTiming * 12926) / 10000) - 1; + } + + us_TmpValue = (USHORT) devpriv->b_InterruptMode; + + switch (us_TmpValue) { + + case APCI3120_EOC_MODE: + + // Testing the interrupt flag and set the EOC bit + // Clears the FIFO + inw(devpriv->iobase + APCI3120_RESET_FIFO); + + // Initialize the sequence array + + //if (!i_APCI3120_SetupChannelList(dev,s,1,chanlist,0)) return -EINVAL; + + if (!i_APCI3120_SetupChannelList(dev, s, 1, + &insn->chanspec, 0)) + return -EINVAL; + + //Initialize Timer 0 mode 4 + devpriv->b_TimerSelectMode = + (devpriv-> + b_TimerSelectMode & 0xFC) | + APCI3120_TIMER_0_MODE_4; + outb(devpriv->b_TimerSelectMode, + devpriv->iobase + APCI3120_TIMER_CRT1); + + // Reset the scan bit and Disables the EOS, DMA, EOC interrupt + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister & APCI3120_DISABLE_SCAN; + + if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) { + + //Disables the EOS,DMA and enables the EOC interrupt + devpriv->b_ModeSelectRegister = + (devpriv-> + b_ModeSelectRegister & + APCI3120_DISABLE_EOS_INT) | + APCI3120_ENABLE_EOC_INT; + inw(devpriv->iobase); + + } else { + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister & + APCI3120_DISABLE_ALL_INTERRUPT_WITHOUT_TIMER; + } + + outb(devpriv->b_ModeSelectRegister, + devpriv->iobase + APCI3120_WRITE_MODE_SELECT); + + // Sets gate 0 + devpriv->us_OutputRegister = + (devpriv-> + us_OutputRegister & APCI3120_CLEAR_PA_PR) | + APCI3120_ENABLE_TIMER0; + outw(devpriv->us_OutputRegister, + devpriv->iobase + APCI3120_WR_ADDRESS); + + // Select Timer 0 + b_Tmp = ((devpriv-> + b_DigitalOutputRegister) & 0xF0) | + APCI3120_SELECT_TIMER_0_WORD; + outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0); + + //Set the convertion time + outw(us_ConvertTiming, + devpriv->iobase + APCI3120_TIMER_VALUE); + + us_TmpValue = + (USHORT) inw(dev->iobase + APCI3120_RD_STATUS); + + if (devpriv->b_EocEosInterrupt == APCI3120_DISABLE) { + + do { + // Waiting for the end of conversion + us_TmpValue = + inw(devpriv->iobase + + APCI3120_RD_STATUS); + } while ((us_TmpValue & APCI3120_EOC) == + APCI3120_EOC); + + //Read the result in FIFO and put it in insn data pointer + us_TmpValue = inw(devpriv->iobase + 0); + *data = us_TmpValue; + + inw(devpriv->iobase + APCI3120_RESET_FIFO); + } + + break; + + case APCI3120_EOS_MODE: + + inw(devpriv->iobase); + // Clears the FIFO + inw(devpriv->iobase + APCI3120_RESET_FIFO); + // clear PA PR and disable timer 0 + + devpriv->us_OutputRegister = + (devpriv-> + us_OutputRegister & APCI3120_CLEAR_PA_PR) | + APCI3120_DISABLE_TIMER0; + + outw(devpriv->us_OutputRegister, + devpriv->iobase + APCI3120_WR_ADDRESS); + + if (!i_APCI3120_SetupChannelList(dev, s, + devpriv->ui_AiNbrofChannels, + devpriv->ui_AiChannelList, 0)) + return -EINVAL; + + //Initialize Timer 0 mode 2 + devpriv->b_TimerSelectMode = + (devpriv-> + b_TimerSelectMode & 0xFC) | + APCI3120_TIMER_0_MODE_2; + outb(devpriv->b_TimerSelectMode, + devpriv->iobase + APCI3120_TIMER_CRT1); + + //Select Timer 0 + b_Tmp = ((devpriv-> + b_DigitalOutputRegister) & 0xF0) | + APCI3120_SELECT_TIMER_0_WORD; + outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0); + + //Set the convertion time + outw(us_ConvertTiming, + devpriv->iobase + APCI3120_TIMER_VALUE); + + //Set the scan bit + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister | APCI3120_ENABLE_SCAN; + outb(devpriv->b_ModeSelectRegister, + devpriv->iobase + APCI3120_WRITE_MODE_SELECT); + + //If Interrupt function is loaded + if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) { + //Disables the EOC,DMA and enables the EOS interrupt + devpriv->b_ModeSelectRegister = + (devpriv-> + b_ModeSelectRegister & + APCI3120_DISABLE_EOC_INT) | + APCI3120_ENABLE_EOS_INT; + inw(devpriv->iobase); + + } else + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister & + APCI3120_DISABLE_ALL_INTERRUPT_WITHOUT_TIMER; + + outb(devpriv->b_ModeSelectRegister, + devpriv->iobase + APCI3120_WRITE_MODE_SELECT); + + inw(devpriv->iobase + APCI3120_RD_STATUS); + + //Sets gate 0 + + devpriv->us_OutputRegister = + devpriv-> + us_OutputRegister | APCI3120_ENABLE_TIMER0; + outw(devpriv->us_OutputRegister, + devpriv->iobase + APCI3120_WR_ADDRESS); + + //Start conversion + outw(0, devpriv->iobase + APCI3120_START_CONVERSION); + + //Waiting of end of convertion if interrupt is not installed + if (devpriv->b_EocEosInterrupt == APCI3120_DISABLE) { + //Waiting the end of convertion + do { + us_TmpValue = + inw(devpriv->iobase + + APCI3120_RD_STATUS); + } + while ((us_TmpValue & APCI3120_EOS) != + APCI3120_EOS); + + for (i = 0; i < devpriv->ui_AiNbrofChannels; + i++) { + //Read the result in FIFO and write them in shared memory + us_TmpValue = inw(devpriv->iobase); + data[i] = (UINT) us_TmpValue; + } + + devpriv->b_InterruptMode = APCI3120_EOC_MODE; // Restore defaults. + } + break; + + default: + printk("inputs wrong\n"); + + } + devpriv->ui_EocEosConversionTime = 0; // re initializing the variable; + } + + return insn->n; + +} + +/* ++----------------------------------------------------------------------------+ +| Function name :int i_APCI3120_StopCyclicAcquisition(struct comedi_device *dev,| +| struct comedi_subdevice *s)| +| | ++----------------------------------------------------------------------------+ +| Task : Stops Cyclic acquisition | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| struct comedi_subdevice *s | +| | ++----------------------------------------------------------------------------+ +| Return Value :0 | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_StopCyclicAcquisition(struct comedi_device * dev, struct comedi_subdevice * s) +{ + // Disable A2P Fifo write and AMWEN signal + outw(0, devpriv->i_IobaseAddon + 4); + + //Disable Bus Master ADD ON + outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0); + outw(0, devpriv->i_IobaseAddon + 2); + outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0); + outw(0, devpriv->i_IobaseAddon + 2); + + //Disable BUS Master PCI + outl(0, devpriv->i_IobaseAmcc + AMCC_OP_REG_MCSR); + + //outl(inl(devpriv->i_IobaseAmcc+AMCC_OP_REG_INTCSR)&(~AINT_WRITE_COMPL), devpriv->i_IobaseAmcc+AMCC_OP_REG_INTCSR); // stop amcc irqs + //outl(inl(devpriv->i_IobaseAmcc+AMCC_OP_REG_MCSR)&(~EN_A2P_TRANSFERS), devpriv->i_IobaseAmcc+AMCC_OP_REG_MCSR); // stop DMA + + //Disable ext trigger + i_APCI3120_ExttrigDisable(dev); + + devpriv->us_OutputRegister = 0; + //stop counters + outw(devpriv-> + us_OutputRegister & APCI3120_DISABLE_TIMER0 & + APCI3120_DISABLE_TIMER1, dev->iobase + APCI3120_WR_ADDRESS); + + outw(APCI3120_DISABLE_ALL_TIMER, dev->iobase + APCI3120_WR_ADDRESS); + + //DISABLE_ALL_INTERRUPT + outb(APCI3120_DISABLE_ALL_INTERRUPT, + dev->iobase + APCI3120_WRITE_MODE_SELECT); + //Flush FIFO + inb(dev->iobase + APCI3120_RESET_FIFO); + inw(dev->iobase + APCI3120_RD_STATUS); + devpriv->ui_AiActualScan = 0; + devpriv->ui_AiActualScanPosition = 0; + s->async->cur_chan = 0; + devpriv->ui_AiBufferPtr = 0; + devpriv->b_AiContinuous = 0; + devpriv->ui_DmaActualBuffer = 0; + + devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE; + devpriv->b_InterruptMode = APCI3120_EOC_MODE; + devpriv->b_EocEosInterrupt = APCI3120_DISABLE; + i_APCI3120_Reset(dev); + return 0; +} + +/* ++----------------------------------------------------------------------------+ +| Function name :int i_APCI3120_CommandTestAnalogInput(struct comedi_device *dev| +| ,struct comedi_subdevice *s,struct comedi_cmd *cmd) | +| | ++----------------------------------------------------------------------------+ +| Task : Test validity for a command for cyclic anlog input | +| acquisition | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| struct comedi_subdevice *s | +| struct comedi_cmd *cmd | ++----------------------------------------------------------------------------+ +| Return Value :0 | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_CommandTestAnalogInput(struct comedi_device * dev, struct comedi_subdevice * s, + struct comedi_cmd * cmd) +{ + int err = 0; + int tmp; // divisor1,divisor2; + + // step 1: make sure trigger sources are trivially valid + + tmp = cmd->start_src; + cmd->start_src &= TRIG_NOW | TRIG_EXT; + if (!cmd->start_src || tmp != cmd->start_src) + err++; + + tmp = cmd->scan_begin_src; + cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW; + if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) + err++; + + tmp = cmd->convert_src; + cmd->convert_src &= TRIG_TIMER; + 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->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT) { + err++; + } + + if (cmd->scan_begin_src != TRIG_TIMER && + cmd->scan_begin_src != TRIG_FOLLOW) + err++; + + if (cmd->convert_src != TRIG_TIMER) + err++; + + if (cmd->scan_end_src != TRIG_COUNT) { + cmd->scan_end_src = TRIG_COUNT; + err++; + } + + if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT) + err++; + + if (err) + return 2; + + // step 3: make sure arguments are trivially compatible + + if (cmd->start_arg != 0) { + cmd->start_arg = 0; + err++; + } + + if (cmd->scan_begin_src == TRIG_TIMER) // Test Delay timing + { + if (cmd->scan_begin_arg < this_board->ui_MinDelaytimeNs) { + cmd->scan_begin_arg = this_board->ui_MinDelaytimeNs; + err++; + } + } + + if (cmd->convert_src == TRIG_TIMER) // Test Acquisition timing + { + if (cmd->scan_begin_src == TRIG_TIMER) { + if ((cmd->convert_arg) + && (cmd->convert_arg < + this_board->ui_MinAcquisitiontimeNs)) { + cmd->convert_arg = + this_board->ui_MinAcquisitiontimeNs; + err++; + } + } else { + if (cmd->convert_arg < + this_board->ui_MinAcquisitiontimeNs) { + cmd->convert_arg = + this_board->ui_MinAcquisitiontimeNs; + err++; + + } + } + } + + if (!cmd->chanlist_len) { + cmd->chanlist_len = 1; + err++; + } + if (cmd->chanlist_len > this_board->i_AiChannelList) { + cmd->chanlist_len = this_board->i_AiChannelList; + err++; + } + if (cmd->stop_src == TRIG_COUNT) { + if (!cmd->stop_arg) { + cmd->stop_arg = 1; + 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->convert_src == TRIG_TIMER) { + + 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; +} + +/* ++----------------------------------------------------------------------------+ +| Function name : int i_APCI3120_CommandAnalogInput(struct comedi_device *dev, | +| struct comedi_subdevice *s) | +| | ++----------------------------------------------------------------------------+ +| Task : Does asynchronous acquisition | +| Determines the mode 1 or 2. | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| struct comedi_subdevice *s | +| | ++----------------------------------------------------------------------------+ +| Return Value : | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_CommandAnalogInput(struct comedi_device * dev, struct comedi_subdevice * s) +{ + struct comedi_cmd *cmd = &s->async->cmd; + + //loading private structure with cmd structure inputs + devpriv->ui_AiFlags = cmd->flags; + devpriv->ui_AiNbrofChannels = cmd->chanlist_len; + devpriv->ui_AiScanLength = cmd->scan_end_arg; + devpriv->pui_AiChannelList = cmd->chanlist; + + //UPDATE-0.7.57->0.7.68devpriv->AiData=s->async->data; + devpriv->AiData = s->async->prealloc_buf; + //UPDATE-0.7.57->0.7.68devpriv->ui_AiDataLength=s->async->data_len; + devpriv->ui_AiDataLength = s->async->prealloc_bufsz; + + if (cmd->stop_src == TRIG_COUNT) { + devpriv->ui_AiNbrofScans = cmd->stop_arg; + } else { + devpriv->ui_AiNbrofScans = 0; + } + + devpriv->ui_AiTimer0 = 0; // variables changed to timer0,timer1 + devpriv->ui_AiTimer1 = 0; + if ((devpriv->ui_AiNbrofScans == 0) || (devpriv->ui_AiNbrofScans == -1)) + devpriv->b_AiContinuous = 1; // user want neverending analog acquisition + // stopped using cancel + + if (cmd->start_src == TRIG_EXT) + devpriv->b_ExttrigEnable = APCI3120_ENABLE; + else + devpriv->b_ExttrigEnable = APCI3120_DISABLE; + + if (cmd->scan_begin_src == TRIG_FOLLOW) { + // mode 1 or 3 + if (cmd->convert_src == TRIG_TIMER) { + // mode 1 + + devpriv->ui_AiTimer0 = cmd->convert_arg; // timer constant in nano seconds + //return this_board->i_hwdrv_CommandAnalogInput(1,dev,s); + return i_APCI3120_CyclicAnalogInput(1, dev, s); + } + + } + if ((cmd->scan_begin_src == TRIG_TIMER) + && (cmd->convert_src == TRIG_TIMER)) { + // mode 2 + devpriv->ui_AiTimer1 = cmd->scan_begin_arg; + devpriv->ui_AiTimer0 = cmd->convert_arg; // variable changed timer2 to timer0 + //return this_board->i_hwdrv_CommandAnalogInput(2,dev,s); + return i_APCI3120_CyclicAnalogInput(2, dev, s); + } + return -1; +} + +/* ++----------------------------------------------------------------------------+ +| Function name : int i_APCI3120_CyclicAnalogInput(int mode, | +| struct comedi_device * dev,struct comedi_subdevice * s) | ++----------------------------------------------------------------------------+ +| Task : This is used for analog input cyclic acquisition | +| Performs the command operations. | +| If DMA is configured does DMA initialization | +| otherwise does the acquisition with EOS interrupt. | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : | +| | +| | ++----------------------------------------------------------------------------+ +| Return Value : | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_CyclicAnalogInput(int mode, struct comedi_device * dev, + struct comedi_subdevice * s) +{ + BYTE b_Tmp; + UINT ui_Tmp, ui_DelayTiming = 0, ui_TimerValue1 = 0, dmalen0 = + 0, dmalen1 = 0, ui_TimerValue2 = + 0, ui_TimerValue0, ui_ConvertTiming; + USHORT us_TmpValue; + + //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver + //devpriv->b_AiCyclicAcquisition=APCI3120_ENABLE; + //END JK 07.05.04: Comparison between WIN32 and Linux driver + + /*******************/ + /* Resets the FIFO */ + /*******************/ + inb(dev->iobase + APCI3120_RESET_FIFO); + + //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver + //inw(dev->iobase+APCI3120_RD_STATUS); + //END JK 07.05.04: Comparison between WIN32 and Linux driver + + /***************************/ + /* Acquisition initialized */ + /***************************/ + //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver + devpriv->b_AiCyclicAcquisition = APCI3120_ENABLE; + //END JK 07.05.04: Comparison between WIN32 and Linux driver + + // clear software registers + devpriv->b_TimerSelectMode = 0; + devpriv->us_OutputRegister = 0; + devpriv->b_ModeSelectRegister = 0; + //devpriv->b_DigitalOutputRegister=0; + + //COMMENT JK 07.05.04: Followings calls are in i_APCI3120_StartAnalogInputAcquisition + + /****************************/ + /* Clear Timer Write TC INT */ + /****************************/ + outl(APCI3120_CLEAR_WRITE_TC_INT, + devpriv->i_IobaseAmcc + APCI3120_AMCC_OP_REG_INTCSR); + + /************************************/ + /* Clears the timer status register */ + /************************************/ + //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver + //inw(dev->iobase+APCI3120_TIMER_STATUS_REGISTER); + inb(dev->iobase + APCI3120_TIMER_STATUS_REGISTER); + //END JK 07.05.04: Comparison between WIN32 and Linux driver + + /**************************/ + /* Disables All Timer */ + /* Sets PR and PA to 0 */ + /**************************/ + devpriv->us_OutputRegister = devpriv->us_OutputRegister & + APCI3120_DISABLE_TIMER0 & + APCI3120_DISABLE_TIMER1 & APCI3120_CLEAR_PA_PR; + + outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS); + + /*******************/ + /* Resets the FIFO */ + /*******************/ + //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver + inb(devpriv->iobase + APCI3120_RESET_FIFO); + //END JK 07.05.04: Comparison between WIN32 and Linux driver + + devpriv->ui_AiActualScan = 0; + devpriv->ui_AiActualScanPosition = 0; + s->async->cur_chan = 0; + devpriv->ui_AiBufferPtr = 0; + devpriv->ui_DmaActualBuffer = 0; + + // value for timer2 minus -2 has to be done .....dunno y?? + ui_TimerValue2 = devpriv->ui_AiNbrofScans - 2; + ui_ConvertTiming = devpriv->ui_AiTimer0; + + if (mode == 2) + ui_DelayTiming = devpriv->ui_AiTimer1; + + /**********************************/ + /* Initializes the sequence array */ + /**********************************/ + if (!i_APCI3120_SetupChannelList(dev, s, devpriv->ui_AiNbrofChannels, + devpriv->pui_AiChannelList, 0)) + return -EINVAL; + + us_TmpValue = (USHORT) inw(dev->iobase + APCI3120_RD_STATUS); +/*** EL241003 : add this section in comment because floats must not be used + if((us_TmpValue & 0x00B0)==0x00B0) + { + f_ConvertValue=(((float)ui_ConvertTiming * 0.002) - 2); + ui_TimerValue0=(UINT)f_ConvertValue; + if (mode==2) + { + f_DelayValue = (((float)ui_DelayTiming * 0.00002) - 2); + ui_TimerValue1 = (UINT) f_DelayValue; + } + } + else + { + f_ConvertValue=(((float)ui_ConvertTiming * 0.0012926) - 1); + ui_TimerValue0=(UINT)f_ConvertValue; + if (mode == 2) + { + f_DelayValue = (((float)ui_DelayTiming * 0.000012926) - 1); + ui_TimerValue1 = (UINT) f_DelayValue; + } + } +***********************************************************************************************/ +/*** EL241003 Begin : add this section to replace floats calculation by integer calculations **/ + //EL250804: Testing if board APCI3120 have the new Quartz or if it is an APCI3001 + if ((us_TmpValue & 0x00B0) == 0x00B0 + || !strcmp(this_board->pc_DriverName, "apci3001")) { + ui_TimerValue0 = ui_ConvertTiming * 2 - 2000; + ui_TimerValue0 = ui_TimerValue0 / 1000; + + if (mode == 2) { + ui_DelayTiming = ui_DelayTiming / 1000; + ui_TimerValue1 = ui_DelayTiming * 2 - 200; + ui_TimerValue1 = ui_TimerValue1 / 100; + } + } else { + ui_ConvertTiming = ui_ConvertTiming / 1000; + ui_TimerValue0 = ui_ConvertTiming * 12926 - 10000; + ui_TimerValue0 = ui_TimerValue0 / 10000; + + if (mode == 2) { + ui_DelayTiming = ui_DelayTiming / 1000; + ui_TimerValue1 = ui_DelayTiming * 12926 - 1; + ui_TimerValue1 = ui_TimerValue1 / 1000000; + } + } +/*** EL241003 End ******************************************************************************/ + + if (devpriv->b_ExttrigEnable == APCI3120_ENABLE) { + i_APCI3120_ExttrigEnable(dev); // activate EXT trigger + } + switch (mode) { + case 1: + // init timer0 in mode 2 + devpriv->b_TimerSelectMode = + (devpriv-> + b_TimerSelectMode & 0xFC) | APCI3120_TIMER_0_MODE_2; + outb(devpriv->b_TimerSelectMode, + dev->iobase + APCI3120_TIMER_CRT1); + + //Select Timer 0 + b_Tmp = ((devpriv-> + b_DigitalOutputRegister) & 0xF0) | + APCI3120_SELECT_TIMER_0_WORD; + outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0); + //Set the convertion time + outw(((USHORT) ui_TimerValue0), + dev->iobase + APCI3120_TIMER_VALUE); + break; + + case 2: + // init timer1 in mode 2 + devpriv->b_TimerSelectMode = + (devpriv-> + b_TimerSelectMode & 0xF3) | APCI3120_TIMER_1_MODE_2; + outb(devpriv->b_TimerSelectMode, + dev->iobase + APCI3120_TIMER_CRT1); + + //Select Timer 1 + b_Tmp = ((devpriv-> + b_DigitalOutputRegister) & 0xF0) | + APCI3120_SELECT_TIMER_1_WORD; + outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0); + //Set the convertion time + outw(((USHORT) ui_TimerValue1), + dev->iobase + APCI3120_TIMER_VALUE); + + // init timer0 in mode 2 + devpriv->b_TimerSelectMode = + (devpriv-> + b_TimerSelectMode & 0xFC) | APCI3120_TIMER_0_MODE_2; + outb(devpriv->b_TimerSelectMode, + dev->iobase + APCI3120_TIMER_CRT1); + + //Select Timer 0 + b_Tmp = ((devpriv-> + b_DigitalOutputRegister) & 0xF0) | + APCI3120_SELECT_TIMER_0_WORD; + outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0); + + //Set the convertion time + outw(((USHORT) ui_TimerValue0), + dev->iobase + APCI3120_TIMER_VALUE); + break; + + } + // ##########common for all modes################# + + /***********************/ + /* Clears the SCAN bit */ + /***********************/ + //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver + //devpriv->b_ModeSelectRegister=devpriv->b_ModeSelectRegister | APCI3120_DISABLE_SCAN; + devpriv->b_ModeSelectRegister = devpriv->b_ModeSelectRegister & + APCI3120_DISABLE_SCAN; + //END JK 07.05.04: Comparison between WIN32 and Linux driver + outb(devpriv->b_ModeSelectRegister, + dev->iobase + APCI3120_WRITE_MODE_SELECT); + + // If DMA is disabled + if (devpriv->us_UseDma == APCI3120_DISABLE) { + // disable EOC and enable EOS + devpriv->b_InterruptMode = APCI3120_EOS_MODE; + devpriv->b_EocEosInterrupt = APCI3120_ENABLE; + + devpriv->b_ModeSelectRegister = + (devpriv-> + b_ModeSelectRegister & APCI3120_DISABLE_EOC_INT) | + APCI3120_ENABLE_EOS_INT; + outb(devpriv->b_ModeSelectRegister, + dev->iobase + APCI3120_WRITE_MODE_SELECT); + + if (!devpriv->b_AiContinuous) { + // configure Timer2 For counting EOS + //Reset gate 2 of Timer 2 to disable it (Set Bit D14 to 0) + devpriv->us_OutputRegister = + devpriv-> + us_OutputRegister & APCI3120_DISABLE_TIMER2; + outw(devpriv->us_OutputRegister, + dev->iobase + APCI3120_WR_ADDRESS); + + // DISABLE TIMER INTERRUPT + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister & + APCI3120_DISABLE_TIMER_INT & 0xEF; + outb(devpriv->b_ModeSelectRegister, + dev->iobase + APCI3120_WRITE_MODE_SELECT); + + //(1) Init timer 2 in mode 0 and write timer value + devpriv->b_TimerSelectMode = + (devpriv-> + b_TimerSelectMode & 0x0F) | + APCI3120_TIMER_2_MODE_0; + outb(devpriv->b_TimerSelectMode, + dev->iobase + APCI3120_TIMER_CRT1); + + //Writing LOW WORD + b_Tmp = ((devpriv-> + b_DigitalOutputRegister) & 0xF0) | + APCI3120_SELECT_TIMER_2_LOW_WORD; + outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0); + outw(LOWORD(ui_TimerValue2), + dev->iobase + APCI3120_TIMER_VALUE); + + //Writing HIGH WORD + b_Tmp = ((devpriv-> + b_DigitalOutputRegister) & 0xF0) | + APCI3120_SELECT_TIMER_2_HIGH_WORD; + outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0); + outw(HIWORD(ui_TimerValue2), + dev->iobase + APCI3120_TIMER_VALUE); + + //(2) Reset FC_TIMER BIT Clearing timer status register + inb(dev->iobase + APCI3120_TIMER_STATUS_REGISTER); + // enable timer counter and disable watch dog + devpriv->b_ModeSelectRegister = + (devpriv-> + b_ModeSelectRegister | + APCI3120_ENABLE_TIMER_COUNTER) & + APCI3120_DISABLE_WATCHDOG; + // select EOS clock input for timer 2 + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister | + APCI3120_TIMER2_SELECT_EOS; + // Enable timer2 interrupt + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister | + APCI3120_ENABLE_TIMER_INT; + outb(devpriv->b_ModeSelectRegister, + dev->iobase + APCI3120_WRITE_MODE_SELECT); + devpriv->b_Timer2Mode = APCI3120_COUNTER; + devpriv->b_Timer2Interrupt = APCI3120_ENABLE; + } + } else { + // If DMA Enabled + //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver + //inw(dev->iobase+0);// reset EOC bit + //END JK 07.05.04: Comparison between WIN32 and Linux driver + devpriv->b_InterruptMode = APCI3120_DMA_MODE; + + /************************************/ + /* Disables the EOC, EOS interrupt */ + /************************************/ + devpriv->b_ModeSelectRegister = devpriv->b_ModeSelectRegister & + APCI3120_DISABLE_EOC_INT & APCI3120_DISABLE_EOS_INT; + + outb(devpriv->b_ModeSelectRegister, + dev->iobase + APCI3120_WRITE_MODE_SELECT); + + dmalen0 = devpriv->ui_DmaBufferSize[0]; + dmalen1 = devpriv->ui_DmaBufferSize[1]; + + if (!devpriv->b_AiContinuous) { + + if (dmalen0 > (devpriv->ui_AiNbrofScans * devpriv->ui_AiScanLength * 2)) { // must we fill full first buffer? + dmalen0 = + devpriv->ui_AiNbrofScans * + devpriv->ui_AiScanLength * 2; + } else if (dmalen1 > (devpriv->ui_AiNbrofScans * devpriv->ui_AiScanLength * 2 - dmalen0)) // and must we fill full second buffer when first is once filled? + dmalen1 = + devpriv->ui_AiNbrofScans * + devpriv->ui_AiScanLength * 2 - dmalen0; + } + + if (devpriv->ui_AiFlags & TRIG_WAKE_EOS) { + // don't we want wake up every scan? + if (dmalen0 > (devpriv->ui_AiScanLength * 2)) { + dmalen0 = devpriv->ui_AiScanLength * 2; + if (devpriv->ui_AiScanLength & 1) + dmalen0 += 2; + } + if (dmalen1 > (devpriv->ui_AiScanLength * 2)) { + dmalen1 = devpriv->ui_AiScanLength * 2; + if (devpriv->ui_AiScanLength & 1) + dmalen1 -= 2; + if (dmalen1 < 4) + dmalen1 = 4; + } + } else { // isn't output buff smaller that our DMA buff? + if (dmalen0 > (devpriv->ui_AiDataLength)) { + dmalen0 = devpriv->ui_AiDataLength; + } + if (dmalen1 > (devpriv->ui_AiDataLength)) { + dmalen1 = devpriv->ui_AiDataLength; + } + } + devpriv->ui_DmaBufferUsesize[0] = dmalen0; + devpriv->ui_DmaBufferUsesize[1] = dmalen1; + + //Initialize DMA + + // Set Transfer count enable bit and A2P_fifo reset bit in AGCSTS register + //1 + ui_Tmp = AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO; + outl(ui_Tmp, devpriv->i_IobaseAmcc + AMCC_OP_REG_AGCSTS); + + // changed since 16 bit interface for add on + /*********************/ + /* ENABLE BUS MASTER */ + /*********************/ + outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0); + outw(APCI3120_ENABLE_TRANSFER_ADD_ON_LOW, + devpriv->i_IobaseAddon + 2); + + outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0); + outw(APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH, + devpriv->i_IobaseAddon + 2); + + // TO VERIFIED + //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver + outw(0x1000, devpriv->i_IobaseAddon + 2); + //END JK 07.05.04: Comparison between WIN32 and Linux driver + + //2 No change + // A2P FIFO MANAGEMENT + // A2P fifo reset & transfer control enable + /***********************/ + /* A2P FIFO MANAGEMENT */ + /***********************/ + outl(APCI3120_A2P_FIFO_MANAGEMENT, devpriv->i_IobaseAmcc + + APCI3120_AMCC_OP_MCSR); + + //3 + //beginning address of dma buf + //The 32 bit address of dma buffer is converted into two 16 bit addresses + // Can done by using _attach and put into into an array + // array used may be for differnet pages + + // DMA Start Adress Low + outw(APCI3120_ADD_ON_MWAR_LOW, devpriv->i_IobaseAddon + 0); + outw((devpriv->ul_DmaBufferHw[0] & 0xFFFF), + devpriv->i_IobaseAddon + 2); + + /*************************/ + /* DMA Start Adress High */ + /*************************/ + outw(APCI3120_ADD_ON_MWAR_HIGH, devpriv->i_IobaseAddon + 0); + outw((devpriv->ul_DmaBufferHw[0] / 65536), + devpriv->i_IobaseAddon + 2); + + //4 + // amount of bytes to be transfered set transfer count + // used ADDON MWTC register + //commented testing outl(devpriv->ui_DmaBufferUsesize[0], devpriv->i_IobaseAddon+AMCC_OP_REG_AMWTC); + + /**************************/ + /* Nbr of acquisition LOW */ + /**************************/ + outw(APCI3120_ADD_ON_MWTC_LOW, devpriv->i_IobaseAddon + 0); + outw((devpriv->ui_DmaBufferUsesize[0] & 0xFFFF), + devpriv->i_IobaseAddon + 2); + + /***************************/ + /* Nbr of acquisition HIGH */ + /***************************/ + outw(APCI3120_ADD_ON_MWTC_HIGH, devpriv->i_IobaseAddon + 0); + outw((devpriv->ui_DmaBufferUsesize[0] / 65536), + devpriv->i_IobaseAddon + 2); + + //5 + // To configure A2P FIFO + // testing outl( FIFO_ADVANCE_ON_BYTE_2,devpriv->i_IobaseAmcc+AMCC_OP_REG_INTCSR); + + /******************/ + /* A2P FIFO RESET */ + /******************/ + // TO VERIFY + //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver + outl(0x04000000UL, devpriv->i_IobaseAmcc + AMCC_OP_REG_MCSR); + //END JK 07.05.04: Comparison between WIN32 and Linux driver + + //6 + //ENABLE A2P FIFO WRITE AND ENABLE AMWEN + // AMWEN_ENABLE | A2P_FIFO_WRITE_ENABLE (0x01|0x02)=0x03 + //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver + //outw(3,devpriv->i_IobaseAddon + 4); + //END JK 07.05.04: Comparison between WIN32 and Linux driver + + //7 + //initialise end of dma interrupt AINT_WRITE_COMPL = ENABLE_WRITE_TC_INT(ADDI) + /***************************************************/ + /* A2P FIFO CONFIGURATE, END OF DMA INTERRUPT INIT */ + /***************************************************/ + outl((APCI3120_FIFO_ADVANCE_ON_BYTE_2 | + APCI3120_ENABLE_WRITE_TC_INT), + devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR); + + //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver + /******************************************/ + /* ENABLE A2P FIFO WRITE AND ENABLE AMWEN */ + /******************************************/ + outw(3, devpriv->i_IobaseAddon + 4); + //END JK 07.05.04: Comparison between WIN32 and Linux driver + + /******************/ + /* A2P FIFO RESET */ + /******************/ + //BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver + outl(0x04000000UL, + devpriv->i_IobaseAmcc + APCI3120_AMCC_OP_MCSR); + //END JK 07.05.04: Comparison between WIN32 and Linux driver + } + + if ((devpriv->us_UseDma == APCI3120_DISABLE) + && !devpriv->b_AiContinuous) { + // set gate 2 to start conversion + devpriv->us_OutputRegister = + devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER2; + outw(devpriv->us_OutputRegister, + dev->iobase + APCI3120_WR_ADDRESS); + } + + switch (mode) { + case 1: + // set gate 0 to start conversion + devpriv->us_OutputRegister = + devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER0; + outw(devpriv->us_OutputRegister, + dev->iobase + APCI3120_WR_ADDRESS); + break; + case 2: + // set gate 0 and gate 1 + devpriv->us_OutputRegister = + devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER1; + devpriv->us_OutputRegister = + devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER0; + outw(devpriv->us_OutputRegister, + dev->iobase + APCI3120_WR_ADDRESS); + break; + + } + + return 0; + +} + +/* ++----------------------------------------------------------------------------+ +| INTERNAL FUNCTIONS | ++----------------------------------------------------------------------------+ +*/ + +/* ++----------------------------------------------------------------------------+ +| Function name : int i_APCI3120_Reset(struct comedi_device *dev) | +| | +| | ++----------------------------------------------------------------------------+ +| Task : Hardware reset function | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| | +| | ++----------------------------------------------------------------------------+ +| Return Value : | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_Reset(struct comedi_device * dev) +{ + unsigned int i; + unsigned short us_TmpValue; + + devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE; + devpriv->b_EocEosInterrupt = APCI3120_DISABLE; + devpriv->b_InterruptMode = APCI3120_EOC_MODE; + devpriv->ui_EocEosConversionTime = 0; // set eoc eos conv time to 0 + devpriv->b_OutputMemoryStatus = 0; + + // variables used in timer subdevice + devpriv->b_Timer2Mode = 0; + devpriv->b_Timer2Interrupt = 0; + devpriv->b_ExttrigEnable = 0; // Disable ext trigger + + /* Disable all interrupts, watchdog for the anolog output */ + devpriv->b_ModeSelectRegister = 0; + outb(devpriv->b_ModeSelectRegister, + dev->iobase + APCI3120_WRITE_MODE_SELECT); + + // Disables all counters, ext trigger and clears PA, PR + devpriv->us_OutputRegister = 0; + outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS); + + //Code to set the all anolog o/p channel to 0v + //8191 is decimal value for zero(0 v)volt in bipolar mode(default) + outw(8191 | APCI3120_ANALOG_OP_CHANNEL_1, dev->iobase + APCI3120_ANALOG_OUTPUT_1); //channel 1 + outw(8191 | APCI3120_ANALOG_OP_CHANNEL_2, dev->iobase + APCI3120_ANALOG_OUTPUT_1); //channel 2 + outw(8191 | APCI3120_ANALOG_OP_CHANNEL_3, dev->iobase + APCI3120_ANALOG_OUTPUT_1); //channel 3 + outw(8191 | APCI3120_ANALOG_OP_CHANNEL_4, dev->iobase + APCI3120_ANALOG_OUTPUT_1); //channel 4 + + outw(8191 | APCI3120_ANALOG_OP_CHANNEL_5, dev->iobase + APCI3120_ANALOG_OUTPUT_2); //channel 5 + outw(8191 | APCI3120_ANALOG_OP_CHANNEL_6, dev->iobase + APCI3120_ANALOG_OUTPUT_2); //channel 6 + outw(8191 | APCI3120_ANALOG_OP_CHANNEL_7, dev->iobase + APCI3120_ANALOG_OUTPUT_2); //channel 7 + outw(8191 | APCI3120_ANALOG_OP_CHANNEL_8, dev->iobase + APCI3120_ANALOG_OUTPUT_2); //channel 8 + + // Reset digital output to L0W + +//ES05 outb(0x0,dev->iobase+APCI3120_DIGITAL_OUTPUT); + udelay(10); + + inw(dev->iobase + 0); //make a dummy read + inb(dev->iobase + APCI3120_RESET_FIFO); // flush FIFO + inw(dev->iobase + APCI3120_RD_STATUS); // flush A/D status register + + //code to reset the RAM sequence + for (i = 0; i < 16; i++) { + us_TmpValue = i << 8; //select the location + outw(us_TmpValue, dev->iobase + APCI3120_SEQ_RAM_ADDRESS); + } + return 0; +} + +/* ++----------------------------------------------------------------------------+ +| Function name : int i_APCI3120_SetupChannelList(struct comedi_device * dev, | +| struct comedi_subdevice * s, int n_chan,unsigned int *chanlist| +| ,char check) | +| | ++----------------------------------------------------------------------------+ +| Task :This function will first check channel list is ok or not| +|and then initialize the sequence RAM with the polarity, Gain,Channel number | +|If the last argument of function "check"is 1 then it only checks the channel| +|list is ok or not. | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device * dev | +| struct comedi_subdevice * s | +| int n_chan | + unsigned int *chanlist + char check ++----------------------------------------------------------------------------+ +| Return Value : | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_SetupChannelList(struct comedi_device * dev, struct comedi_subdevice * s, + int n_chan, unsigned int *chanlist, char check) +{ + unsigned int i; //, differencial=0, bipolar=0; + unsigned int gain; + unsigned short us_TmpValue; + + /* correct channel and range number check itself comedi/range.c */ + if (n_chan < 1) { + if (!check) + comedi_error(dev, "range/channel list is empty!"); + return 0; + } + // All is ok, so we can setup channel/range list + if (check) + return 1; + + //Code to set the PA and PR...Here it set PA to 0.. + devpriv->us_OutputRegister = + devpriv->us_OutputRegister & APCI3120_CLEAR_PA_PR; + devpriv->us_OutputRegister = ((n_chan - 1) & 0xf) << 8; + outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS); + + for (i = 0; i < n_chan; i++) { + // store range list to card + us_TmpValue = CR_CHAN(chanlist[i]); // get channel number; + + if (CR_RANGE(chanlist[i]) < APCI3120_BIPOLAR_RANGES) { + us_TmpValue &= ((~APCI3120_UNIPOLAR) & 0xff); // set bipolar + } else { + us_TmpValue |= APCI3120_UNIPOLAR; // enable unipolar...... + } + + gain = CR_RANGE(chanlist[i]); // get gain number + us_TmpValue |= ((gain & 0x03) << 4); //<<4 for G0 and G1 bit in RAM + us_TmpValue |= i << 8; //To select the RAM LOCATION.... + outw(us_TmpValue, dev->iobase + APCI3120_SEQ_RAM_ADDRESS); + + printk("\n Gain = %i", + (((unsigned char)CR_RANGE(chanlist[i]) & 0x03) << 2)); + printk("\n Channel = %i", CR_CHAN(chanlist[i])); + printk("\n Polarity = %i", us_TmpValue & APCI3120_UNIPOLAR); + } + return 1; // we can serve this with scan logic +} + +/* ++----------------------------------------------------------------------------+ +| Function name : int i_APCI3120_ExttrigEnable(struct comedi_device * dev) | +| | +| | ++----------------------------------------------------------------------------+ +| Task : Enable the external trigger | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device * dev | +| | +| | ++----------------------------------------------------------------------------+ +| Return Value : 0 | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_ExttrigEnable(struct comedi_device * dev) +{ + + devpriv->us_OutputRegister |= APCI3120_ENABLE_EXT_TRIGGER; + outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS); + return 0; +} + +/* ++----------------------------------------------------------------------------+ +| Function name : int i_APCI3120_ExttrigDisable(struct comedi_device * dev) | +| | ++----------------------------------------------------------------------------+ +| Task : Disables the external trigger | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device * dev | +| | +| | ++----------------------------------------------------------------------------+ +| Return Value : 0 | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_ExttrigDisable(struct comedi_device * dev) +{ + devpriv->us_OutputRegister &= ~APCI3120_ENABLE_EXT_TRIGGER; + outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS); + return 0; +} + +/* ++----------------------------------------------------------------------------+ +| INTERRUPT FUNCTIONS | ++----------------------------------------------------------------------------+ +*/ + +/* ++----------------------------------------------------------------------------+ +| Function name : void v_APCI3120_Interrupt(int irq, void *d) | +| | +| | ++----------------------------------------------------------------------------+ +| Task :Interrupt handler for APCI3120 | +| When interrupt occurs this gets called. | +| First it finds which interrupt has been generated and | +| handles corresponding interrupt | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : int irq | +| void *d | +| | ++----------------------------------------------------------------------------+ +| Return Value : void | +| | ++----------------------------------------------------------------------------+ +*/ + +void v_APCI3120_Interrupt(int irq, void *d) +{ + struct comedi_device *dev = d; + USHORT int_daq; + + unsigned int int_amcc, ui_Check, i; + USHORT us_TmpValue; + BYTE b_DummyRead; + + struct comedi_subdevice *s = dev->subdevices + 0; + ui_Check = 1; + + int_daq = inw(dev->iobase + APCI3120_RD_STATUS) & 0xf000; // get IRQ reasons + int_amcc = inl(devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR); // get AMCC INT register + + if ((!int_daq) && (!(int_amcc & ANY_S593X_INT))) { + comedi_error(dev, "IRQ from unknow source"); + return; + } + + outl(int_amcc | 0x00ff0000, devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR); // shutdown IRQ reasons in AMCC + + int_daq = (int_daq >> 12) & 0xF; + + if (devpriv->b_ExttrigEnable == APCI3120_ENABLE) { + //Disable ext trigger + i_APCI3120_ExttrigDisable(dev); + devpriv->b_ExttrigEnable = APCI3120_DISABLE; + } + //clear the timer 2 interrupt + inb(devpriv->i_IobaseAmcc + APCI3120_TIMER_STATUS_REGISTER); + + if (int_amcc & MASTER_ABORT_INT) + comedi_error(dev, "AMCC IRQ - MASTER DMA ABORT!"); + if (int_amcc & TARGET_ABORT_INT) + comedi_error(dev, "AMCC IRQ - TARGET DMA ABORT!"); + + // Ckeck if EOC interrupt + if (((int_daq & 0x8) == 0) + && (devpriv->b_InterruptMode == APCI3120_EOC_MODE)) { + if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) { + + // Read the AI Value + + devpriv->ui_AiReadData[0] = + (UINT) inw(devpriv->iobase + 0); + devpriv->b_EocEosInterrupt = APCI3120_DISABLE; + send_sig(SIGIO, devpriv->tsk_Current, 0); // send signal to the sample + } else { + //Disable EOC Interrupt + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister & APCI3120_DISABLE_EOC_INT; + outb(devpriv->b_ModeSelectRegister, + devpriv->iobase + APCI3120_WRITE_MODE_SELECT); + + } + } + + // Check If EOS interrupt + if ((int_daq & 0x2) && (devpriv->b_InterruptMode == APCI3120_EOS_MODE)) { + + if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) // enable this in without DMA ??? + { + + if (devpriv->b_AiCyclicAcquisition == APCI3120_ENABLE) { + ui_Check = 0; + i_APCI3120_InterruptHandleEos(dev); + devpriv->ui_AiActualScan++; + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister | + APCI3120_ENABLE_EOS_INT; + outb(devpriv->b_ModeSelectRegister, + dev->iobase + + APCI3120_WRITE_MODE_SELECT); + } else { + ui_Check = 0; + for (i = 0; i < devpriv->ui_AiNbrofChannels; + i++) { + us_TmpValue = inw(devpriv->iobase + 0); + devpriv->ui_AiReadData[i] = + (UINT) us_TmpValue; + } + devpriv->b_EocEosInterrupt = APCI3120_DISABLE; + devpriv->b_InterruptMode = APCI3120_EOC_MODE; + + send_sig(SIGIO, devpriv->tsk_Current, 0); // send signal to the sample + + } + + } else { + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister & APCI3120_DISABLE_EOS_INT; + outb(devpriv->b_ModeSelectRegister, + dev->iobase + APCI3120_WRITE_MODE_SELECT); + devpriv->b_EocEosInterrupt = APCI3120_DISABLE; //Default settings + devpriv->b_InterruptMode = APCI3120_EOC_MODE; + } + + } + //Timer2 interrupt + if (int_daq & 0x1) { + + switch (devpriv->b_Timer2Mode) { + case APCI3120_COUNTER: + + devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE; + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister & APCI3120_DISABLE_EOS_INT; + outb(devpriv->b_ModeSelectRegister, + dev->iobase + APCI3120_WRITE_MODE_SELECT); + + // stop timer 2 + devpriv->us_OutputRegister = + devpriv-> + us_OutputRegister & APCI3120_DISABLE_ALL_TIMER; + outw(devpriv->us_OutputRegister, + dev->iobase + APCI3120_WR_ADDRESS); + + //stop timer 0 and timer 1 + i_APCI3120_StopCyclicAcquisition(dev, s); + devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE; + + //UPDATE-0.7.57->0.7.68comedi_done(dev,s); + s->async->events |= COMEDI_CB_EOA; + comedi_event(dev, s); + + break; + + case APCI3120_TIMER: + + //Send a signal to from kernel to user space + send_sig(SIGIO, devpriv->tsk_Current, 0); + break; + + case APCI3120_WATCHDOG: + + //Send a signal to from kernel to user space + send_sig(SIGIO, devpriv->tsk_Current, 0); + break; + + default: + + // disable Timer Interrupt + + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister & + APCI3120_DISABLE_TIMER_INT; + + outb(devpriv->b_ModeSelectRegister, + dev->iobase + APCI3120_WRITE_MODE_SELECT); + + } + + b_DummyRead = inb(dev->iobase + APCI3120_TIMER_STATUS_REGISTER); + + } + + if ((int_daq & 0x4) && (devpriv->b_InterruptMode == APCI3120_DMA_MODE)) { + if (devpriv->b_AiCyclicAcquisition == APCI3120_ENABLE) { + + /****************************/ + /* Clear Timer Write TC INT */ + /****************************/ + + outl(APCI3120_CLEAR_WRITE_TC_INT, + devpriv->i_IobaseAmcc + + APCI3120_AMCC_OP_REG_INTCSR); + + /************************************/ + /* Clears the timer status register */ + /************************************/ + inw(dev->iobase + APCI3120_TIMER_STATUS_REGISTER); + v_APCI3120_InterruptDma(irq, d); // do some data transfer + } else { + /* Stops the Timer */ + outw(devpriv-> + us_OutputRegister & APCI3120_DISABLE_TIMER0 & + APCI3120_DISABLE_TIMER1, + dev->iobase + APCI3120_WR_ADDRESS); + } + + } + + return; +} + +/* ++----------------------------------------------------------------------------+ +| Function name :int i_APCI3120_InterruptHandleEos(struct comedi_device *dev) | +| | +| | ++----------------------------------------------------------------------------+ +| Task : This function handles EOS interrupt. | +| This function copies the acquired data(from FIFO) | +| to Comedi buffer. | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| | +| | ++----------------------------------------------------------------------------+ +| Return Value : 0 | +| | ++----------------------------------------------------------------------------+ +*/ + +/* + * int i_APCI3120_InterruptHandleEos(struct comedi_device *dev) +{ + int n_chan,i; + short *data; + struct comedi_subdevice *s=dev->subdevices+0; + struct comedi_async *async = s->async; + data=async->data+async->buf_int_ptr; + n_chan=devpriv->ui_AiNbrofChannels; + + for(i=0;i<n_chan;i++) + { + data[i]=inw(dev->iobase+0); + } + async->buf_int_count+=n_chan*sizeof(short); + async->buf_int_ptr+=n_chan*sizeof(short); + comedi_eos(dev,s); + if (s->async->buf_int_ptr>=s->async->data_len) // for buffer rool over + { +*//* buffer rollover */ +/* s->async->buf_int_ptr=0; + comedi_eobuf(dev,s); + } + return 0; +}*/ +int i_APCI3120_InterruptHandleEos(struct comedi_device * dev) +{ + int n_chan, i; + struct comedi_subdevice *s = dev->subdevices + 0; + int err = 1; + + n_chan = devpriv->ui_AiNbrofChannels; + + s->async->events = 0; + + for (i = 0; i < n_chan; i++) + err &= comedi_buf_put(s->async, inw(dev->iobase + 0)); + + s->async->events |= COMEDI_CB_EOS; + + if (err == 0) + s->async->events |= COMEDI_CB_OVERFLOW; + + comedi_event(dev, s); + + return 0; +} + +/* ++----------------------------------------------------------------------------+ +| Function name : void v_APCI3120_InterruptDma(int irq, void *d) | +| | ++----------------------------------------------------------------------------+ +| Task : This is a handler for the DMA interrupt | +| This function copies the data to Comedi Buffer. | +| For continuous DMA it reinitializes the DMA operation. | +| For single mode DMA it stop the acquisition. | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : int irq, void *d | +| | ++----------------------------------------------------------------------------+ +| Return Value : void | +| | ++----------------------------------------------------------------------------+ +*/ + +void v_APCI3120_InterruptDma(int irq, void *d) +{ + struct comedi_device *dev = d; + struct comedi_subdevice *s = dev->subdevices + 0; + unsigned int next_dma_buf, samplesinbuf; + unsigned long low_word, high_word, var; + + UINT ui_Tmp; + samplesinbuf = + devpriv->ui_DmaBufferUsesize[devpriv->ui_DmaActualBuffer] - + inl(devpriv->i_IobaseAmcc + AMCC_OP_REG_MWTC); + + if (samplesinbuf < + devpriv->ui_DmaBufferUsesize[devpriv->ui_DmaActualBuffer]) { + comedi_error(dev, "Interrupted DMA transfer!"); + } + if (samplesinbuf & 1) { + comedi_error(dev, "Odd count of bytes in DMA ring!"); + i_APCI3120_StopCyclicAcquisition(dev, s); + devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE; + + return; + } + samplesinbuf = samplesinbuf >> 1; // number of received samples + if (devpriv->b_DmaDoubleBuffer) { + // switch DMA buffers if is used double buffering + next_dma_buf = 1 - devpriv->ui_DmaActualBuffer; + + ui_Tmp = AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO; + outl(ui_Tmp, devpriv->i_IobaseAddon + AMCC_OP_REG_AGCSTS); + + // changed since 16 bit interface for add on + outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0); + outw(APCI3120_ENABLE_TRANSFER_ADD_ON_LOW, + devpriv->i_IobaseAddon + 2); + outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0); + outw(APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH, devpriv->i_IobaseAddon + 2); // 0x1000 is out putted in windows driver + + var = devpriv->ul_DmaBufferHw[next_dma_buf]; + low_word = var & 0xffff; + var = devpriv->ul_DmaBufferHw[next_dma_buf]; + high_word = var / 65536; + + /* DMA Start Adress Low */ + outw(APCI3120_ADD_ON_MWAR_LOW, devpriv->i_IobaseAddon + 0); + outw(low_word, devpriv->i_IobaseAddon + 2); + + /* DMA Start Adress High */ + outw(APCI3120_ADD_ON_MWAR_HIGH, devpriv->i_IobaseAddon + 0); + outw(high_word, devpriv->i_IobaseAddon + 2); + + var = devpriv->ui_DmaBufferUsesize[next_dma_buf]; + low_word = var & 0xffff; + var = devpriv->ui_DmaBufferUsesize[next_dma_buf]; + high_word = var / 65536; + + /* Nbr of acquisition LOW */ + outw(APCI3120_ADD_ON_MWTC_LOW, devpriv->i_IobaseAddon + 0); + outw(low_word, devpriv->i_IobaseAddon + 2); + + /* Nbr of acquisition HIGH */ + outw(APCI3120_ADD_ON_MWTC_HIGH, devpriv->i_IobaseAddon + 0); + outw(high_word, devpriv->i_IobaseAddon + 2); + + // To configure A2P FIFO + // ENABLE A2P FIFO WRITE AND ENABLE AMWEN + // AMWEN_ENABLE | A2P_FIFO_WRITE_ENABLE (0x01|0x02)=0x03 + outw(3, devpriv->i_IobaseAddon + 4); + //initialise end of dma interrupt AINT_WRITE_COMPL = ENABLE_WRITE_TC_INT(ADDI) + outl((APCI3120_FIFO_ADVANCE_ON_BYTE_2 | + APCI3120_ENABLE_WRITE_TC_INT), + devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR); + + } +/*UPDATE-0.7.57->0.7.68 + ptr=(short *)devpriv->ul_DmaBufferVirtual[devpriv->ui_DmaActualBuffer]; + + + // if there is not enough space left in the buffer to copy all data contained in the DMABufferVirtual + if(s->async->buf_int_ptr+samplesinbuf*sizeof(short)>=devpriv->ui_AiDataLength) + { + m=(devpriv->ui_AiDataLength-s->async->buf_int_ptr)/sizeof(short); + v_APCI3120_InterruptDmaMoveBlock16bit(dev,s,(void *)ptr,((void *)(devpriv->AiData))+s->async->buf_int_ptr,m); + s->async->buf_int_count+=m*sizeof(short); + ptr+=m*sizeof(short); + samplesinbuf-=m; + s->async->buf_int_ptr=0; + comedi_eobuf(dev,s); + } + + if (samplesinbuf) + { + v_APCI3120_InterruptDmaMoveBlock16bit(dev,s,(void *)ptr,((void *)(devpriv->AiData))+s->async->buf_int_ptr,samplesinbuf); + + s->async->buf_int_count+=samplesinbuf*sizeof(short); + s->async->buf_int_ptr+=samplesinbuf*sizeof(short); + if (!(devpriv->ui_AiFlags & TRIG_WAKE_EOS)) + { + comedi_bufcheck(dev,s); + } + } + if (!devpriv->b_AiContinuous) + if ( devpriv->ui_AiActualScan>=devpriv->ui_AiNbrofScans ) + { + // all data sampled + i_APCI3120_StopCyclicAcquisition(dev,s); + devpriv->b_AiCyclicAcquisition=APCI3120_DISABLE; + //DPRINTK("\n Single DMA completed..\n"); + comedi_done(dev,s); + return; + } +*/ + if (samplesinbuf) { + v_APCI3120_InterruptDmaMoveBlock16bit(dev, s, + devpriv->ul_DmaBufferVirtual[devpriv-> + ui_DmaActualBuffer], samplesinbuf); + + if (!(devpriv->ui_AiFlags & TRIG_WAKE_EOS)) { + s->async->events |= COMEDI_CB_EOS; + comedi_event(dev, s); + } + } + if (!devpriv->b_AiContinuous) + if (devpriv->ui_AiActualScan >= devpriv->ui_AiNbrofScans) { + // all data sampled + i_APCI3120_StopCyclicAcquisition(dev, s); + devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE; + s->async->events |= COMEDI_CB_EOA; + comedi_event(dev, s); + return; + } + + if (devpriv->b_DmaDoubleBuffer) { // switch dma buffers + devpriv->ui_DmaActualBuffer = 1 - devpriv->ui_DmaActualBuffer; + } else { + // restart DMA if is not used double buffering + //ADDED REINITIALISE THE DMA + ui_Tmp = AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO; + outl(ui_Tmp, devpriv->i_IobaseAddon + AMCC_OP_REG_AGCSTS); + + // changed since 16 bit interface for add on + outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0); + outw(APCI3120_ENABLE_TRANSFER_ADD_ON_LOW, + devpriv->i_IobaseAddon + 2); + outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0); + outw(APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH, devpriv->i_IobaseAddon + 2); // + // A2P FIFO MANAGEMENT + // A2P fifo reset & transfer control enable + outl(APCI3120_A2P_FIFO_MANAGEMENT, + devpriv->i_IobaseAmcc + AMCC_OP_REG_MCSR); + + var = devpriv->ul_DmaBufferHw[0]; + low_word = var & 0xffff; + var = devpriv->ul_DmaBufferHw[0]; + high_word = var / 65536; + outw(APCI3120_ADD_ON_MWAR_LOW, devpriv->i_IobaseAddon + 0); + outw(low_word, devpriv->i_IobaseAddon + 2); + outw(APCI3120_ADD_ON_MWAR_HIGH, devpriv->i_IobaseAddon + 0); + outw(high_word, devpriv->i_IobaseAddon + 2); + + var = devpriv->ui_DmaBufferUsesize[0]; + low_word = var & 0xffff; //changed + var = devpriv->ui_DmaBufferUsesize[0]; + high_word = var / 65536; + outw(APCI3120_ADD_ON_MWTC_LOW, devpriv->i_IobaseAddon + 0); + outw(low_word, devpriv->i_IobaseAddon + 2); + outw(APCI3120_ADD_ON_MWTC_HIGH, devpriv->i_IobaseAddon + 0); + outw(high_word, devpriv->i_IobaseAddon + 2); + + // To configure A2P FIFO + //ENABLE A2P FIFO WRITE AND ENABLE AMWEN + // AMWEN_ENABLE | A2P_FIFO_WRITE_ENABLE (0x01|0x02)=0x03 + outw(3, devpriv->i_IobaseAddon + 4); + //initialise end of dma interrupt AINT_WRITE_COMPL = ENABLE_WRITE_TC_INT(ADDI) + outl((APCI3120_FIFO_ADVANCE_ON_BYTE_2 | + APCI3120_ENABLE_WRITE_TC_INT), + devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR); + } +} + +/* ++----------------------------------------------------------------------------+ +| Function name :void v_APCI3120_InterruptDmaMoveBlock16bit(comedi_device| +|*dev,struct comedi_subdevice *s,short *dma,short *data,int n) | +| | ++----------------------------------------------------------------------------+ +| Task : This function copies the data from DMA buffer to the | +| Comedi buffer | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| struct comedi_subdevice *s | +| short *dma | +| short *data,int n | ++----------------------------------------------------------------------------+ +| Return Value : void | +| | ++----------------------------------------------------------------------------+ +*/ + +/*void v_APCI3120_InterruptDmaMoveBlock16bit(struct comedi_device *dev,struct comedi_subdevice *s,short *dma,short *data,int n) +{ + int i,j,m; + + j=s->async->cur_chan; + m=devpriv->ui_AiActualScanPosition; + for(i=0;i<n;i++) + { + *data=*dma; + data++; dma++; + j++; + if(j>=devpriv->ui_AiNbrofChannels) + { + m+=j; + j=0; + if(m>=devpriv->ui_AiScanLength) + { + m=0; + devpriv->ui_AiActualScan++; + if (devpriv->ui_AiFlags & TRIG_WAKE_EOS) +;//UPDATE-0.7.57->0.7.68 comedi_eos(dev,s); + } + } + } + devpriv->ui_AiActualScanPosition=m; + s->async->cur_chan=j; + +} +*/ +void v_APCI3120_InterruptDmaMoveBlock16bit(struct comedi_device * dev, + struct comedi_subdevice * s, short * dma_buffer, unsigned int num_samples) +{ + devpriv->ui_AiActualScan += + (s->async->cur_chan + num_samples) / devpriv->ui_AiScanLength; + s->async->cur_chan += num_samples; + s->async->cur_chan %= devpriv->ui_AiScanLength; + + cfc_write_array_to_buffer(s, dma_buffer, num_samples * sizeof(short)); +} + +/* ++----------------------------------------------------------------------------+ +| TIMER SUBDEVICE | ++----------------------------------------------------------------------------+ +*/ + +/* ++----------------------------------------------------------------------------+ +| Function name :int i_APCI3120_InsnConfigTimer(struct comedi_device *dev, | +| struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) | +| | ++----------------------------------------------------------------------------+ +| Task :Configure Timer 2 | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| struct comedi_subdevice *s | +| struct comedi_insn *insn | +| unsigned int *data | +| | +| data[0]= TIMER configure as timer | +| = WATCHDOG configure as watchdog | +| data[1] = Timer constant | +| data[2] = Timer2 interrupt (1)enable or(0) disable | +| | ++----------------------------------------------------------------------------+ +| Return Value : | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_InsnConfigTimer(struct comedi_device * dev, struct comedi_subdevice * s, + struct comedi_insn * insn, unsigned int * data) +{ + + UINT ui_Timervalue2; + USHORT us_TmpValue; + BYTE b_Tmp; + + if (!data[1]) + comedi_error(dev, "config:No timer constant !"); + + devpriv->b_Timer2Interrupt = (BYTE) data[2]; // save info whether to enable or disable interrupt + + ui_Timervalue2 = data[1] / 1000; // convert nano seconds to u seconds + + //this_board->i_hwdrv_InsnConfigTimer(dev, ui_Timervalue2,(BYTE)data[0]); + us_TmpValue = (USHORT) inw(devpriv->iobase + APCI3120_RD_STATUS); + + //EL250804: Testing if board APCI3120 have the new Quartz or if it is an APCI3001 + // and calculate the time value to set in the timer + if ((us_TmpValue & 0x00B0) == 0x00B0 + || !strcmp(this_board->pc_DriverName, "apci3001")) { + //Calculate the time value to set in the timer + ui_Timervalue2 = ui_Timervalue2 / 50; + } else { + //Calculate the time value to set in the timer + ui_Timervalue2 = ui_Timervalue2 / 70; + } + + //Reset gate 2 of Timer 2 to disable it (Set Bit D14 to 0) + devpriv->us_OutputRegister = + devpriv->us_OutputRegister & APCI3120_DISABLE_TIMER2; + outw(devpriv->us_OutputRegister, devpriv->iobase + APCI3120_WR_ADDRESS); + + // Disable TIMER Interrupt + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister & APCI3120_DISABLE_TIMER_INT & 0xEF; + + // Disable Eoc and Eos Interrupts + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister & APCI3120_DISABLE_EOC_INT & + APCI3120_DISABLE_EOS_INT; + outb(devpriv->b_ModeSelectRegister, + devpriv->iobase + APCI3120_WRITE_MODE_SELECT); + if (data[0] == APCI3120_TIMER) //initialize timer + { + + //devpriv->b_ModeSelectRegister=devpriv->b_ModeSelectRegister| APCI3120_ENABLE_TIMER_INT ; + //outb(devpriv->b_ModeSelectRegister,devpriv->iobase+APCI3120_WRITE_MODE_SELECT); + + //Set the Timer 2 in mode 2(Timer) + devpriv->b_TimerSelectMode = + (devpriv-> + b_TimerSelectMode & 0x0F) | APCI3120_TIMER_2_MODE_2; + outb(devpriv->b_TimerSelectMode, + devpriv->iobase + APCI3120_TIMER_CRT1); + + //Configure the timer 2 for writing the LOW WORD of timer is Delay value + //You must make a b_tmp variable with DigitalOutPutRegister because at Address_1+APCI3120_TIMER_CRT0 + //you can set the digital output and configure the timer 2,and if you don't make this, digital output + //are erase (Set to 0) + + //Writing LOW WORD + b_Tmp = ((devpriv-> + b_DigitalOutputRegister) & 0xF0) | + APCI3120_SELECT_TIMER_2_LOW_WORD; + outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0); + outw(LOWORD(ui_Timervalue2), + devpriv->iobase + APCI3120_TIMER_VALUE); + + //Writing HIGH WORD + b_Tmp = ((devpriv-> + b_DigitalOutputRegister) & 0xF0) | + APCI3120_SELECT_TIMER_2_HIGH_WORD; + outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0); + outw(HIWORD(ui_Timervalue2), + devpriv->iobase + APCI3120_TIMER_VALUE); + // timer2 in Timer mode enabled + devpriv->b_Timer2Mode = APCI3120_TIMER; + + } else // Initialize Watch dog + { + + //Set the Timer 2 in mode 5(Watchdog) + + devpriv->b_TimerSelectMode = + (devpriv-> + b_TimerSelectMode & 0x0F) | APCI3120_TIMER_2_MODE_5; + outb(devpriv->b_TimerSelectMode, + devpriv->iobase + APCI3120_TIMER_CRT1); + + //Configure the timer 2 for writing the LOW WORD of timer is Delay value + //You must make a b_tmp variable with DigitalOutPutRegister because at Address_1+APCI3120_TIMER_CRT0 + //you can set the digital output and configure the timer 2,and if you don't make this, digital output + //are erase (Set to 0) + + //Writing LOW WORD + b_Tmp = ((devpriv-> + b_DigitalOutputRegister) & 0xF0) | + APCI3120_SELECT_TIMER_2_LOW_WORD; + outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0); + outw(LOWORD(ui_Timervalue2), + devpriv->iobase + APCI3120_TIMER_VALUE); + + //Writing HIGH WORD + b_Tmp = ((devpriv-> + b_DigitalOutputRegister) & 0xF0) | + APCI3120_SELECT_TIMER_2_HIGH_WORD; + outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0); + + outw(HIWORD(ui_Timervalue2), + devpriv->iobase + APCI3120_TIMER_VALUE); + //watchdog enabled + devpriv->b_Timer2Mode = APCI3120_WATCHDOG; + + } + + return insn->n; + +} + +/* ++----------------------------------------------------------------------------+ +| Function name :int i_APCI3120_InsnWriteTimer(struct comedi_device *dev, | +| struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data) | +| | ++----------------------------------------------------------------------------+ +| Task : To start and stop the timer | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| struct comedi_subdevice *s | +| struct comedi_insn *insn | +| unsigned int *data | +| | +| data[0] = 1 (start) | +| data[0] = 0 (stop ) | +| data[0] = 2 (write new value) | +| data[1]= new value | +| | +| devpriv->b_Timer2Mode = 0 DISABLE | +| 1 Timer | +| 2 Watch dog | +| | ++----------------------------------------------------------------------------+ +| Return Value : | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_InsnWriteTimer(struct comedi_device * dev, struct comedi_subdevice * s, + struct comedi_insn * insn, unsigned int * data) +{ + + UINT ui_Timervalue2 = 0; + USHORT us_TmpValue; + BYTE b_Tmp; + + if ((devpriv->b_Timer2Mode != APCI3120_WATCHDOG) + && (devpriv->b_Timer2Mode != APCI3120_TIMER)) { + comedi_error(dev, "\nwrite:timer2 not configured "); + return -EINVAL; + } + + if (data[0] == 2) // write new value + { + if (devpriv->b_Timer2Mode != APCI3120_TIMER) { + comedi_error(dev, + "write :timer2 not configured in TIMER MODE"); + return -EINVAL; + } + + if (data[1]) + ui_Timervalue2 = data[1]; + else + ui_Timervalue2 = 0; + } + + //this_board->i_hwdrv_InsnWriteTimer(dev,data[0],ui_Timervalue2); + + switch (data[0]) { + case APCI3120_START: + + // Reset FC_TIMER BIT + inb(devpriv->iobase + APCI3120_TIMER_STATUS_REGISTER); + if (devpriv->b_Timer2Mode == APCI3120_TIMER) //start timer + { + //Enable Timer + devpriv->b_ModeSelectRegister = + devpriv->b_ModeSelectRegister & 0x0B; + } else //start watch dog + { + //Enable WatchDog + devpriv->b_ModeSelectRegister = + (devpriv-> + b_ModeSelectRegister & 0x0B) | + APCI3120_ENABLE_WATCHDOG; + } + + //enable disable interrupt + if ((devpriv->b_Timer2Interrupt) == APCI3120_ENABLE) { + + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister | + APCI3120_ENABLE_TIMER_INT; + // save the task structure to pass info to user + devpriv->tsk_Current = current; + } else { + + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister & + APCI3120_DISABLE_TIMER_INT; + } + outb(devpriv->b_ModeSelectRegister, + devpriv->iobase + APCI3120_WRITE_MODE_SELECT); + + if (devpriv->b_Timer2Mode == APCI3120_TIMER) //start timer + { + //For Timer mode is Gate2 must be activated **timer started + devpriv->us_OutputRegister = + devpriv-> + us_OutputRegister | APCI3120_ENABLE_TIMER2; + outw(devpriv->us_OutputRegister, + devpriv->iobase + APCI3120_WR_ADDRESS); + } + + break; + + case APCI3120_STOP: + if (devpriv->b_Timer2Mode == APCI3120_TIMER) { + //Disable timer + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister & + APCI3120_DISABLE_TIMER_COUNTER; + } else { + //Disable WatchDog + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister & + APCI3120_DISABLE_WATCHDOG; + } + // Disable timer interrupt + devpriv->b_ModeSelectRegister = + devpriv-> + b_ModeSelectRegister & APCI3120_DISABLE_TIMER_INT; + + // Write above states to register + outb(devpriv->b_ModeSelectRegister, + devpriv->iobase + APCI3120_WRITE_MODE_SELECT); + + // Reset Gate 2 + devpriv->us_OutputRegister = + devpriv->us_OutputRegister & APCI3120_DISABLE_TIMER_INT; + outw(devpriv->us_OutputRegister, + devpriv->iobase + APCI3120_WR_ADDRESS); + + // Reset FC_TIMER BIT + inb(devpriv->iobase + APCI3120_TIMER_STATUS_REGISTER); + + // Disable timer + //devpriv->b_Timer2Mode=APCI3120_DISABLE; + + break; + + case 2: //write new value to Timer + if (devpriv->b_Timer2Mode != APCI3120_TIMER) { + comedi_error(dev, + "write :timer2 not configured in TIMER MODE"); + return -EINVAL; + } + // ui_Timervalue2=data[1]; // passed as argument + us_TmpValue = + (USHORT) inw(devpriv->iobase + APCI3120_RD_STATUS); + + //EL250804: Testing if board APCI3120 have the new Quartz or if it is an APCI3001 + // and calculate the time value to set in the timer + if ((us_TmpValue & 0x00B0) == 0x00B0 + || !strcmp(this_board->pc_DriverName, "apci3001")) { + //Calculate the time value to set in the timer + ui_Timervalue2 = ui_Timervalue2 / 50; + } else { + //Calculate the time value to set in the timer + ui_Timervalue2 = ui_Timervalue2 / 70; + } + //Writing LOW WORD + b_Tmp = ((devpriv-> + b_DigitalOutputRegister) & 0xF0) | + APCI3120_SELECT_TIMER_2_LOW_WORD; + outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0); + + outw(LOWORD(ui_Timervalue2), + devpriv->iobase + APCI3120_TIMER_VALUE); + + //Writing HIGH WORD + b_Tmp = ((devpriv-> + b_DigitalOutputRegister) & 0xF0) | + APCI3120_SELECT_TIMER_2_HIGH_WORD; + outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0); + + outw(HIWORD(ui_Timervalue2), + devpriv->iobase + APCI3120_TIMER_VALUE); + + break; + default: + return -EINVAL; // Not a valid input + } + + return insn->n; +} + +/* ++----------------------------------------------------------------------------+ +| Function name : int i_APCI3120_InsnReadTimer(struct comedi_device *dev, | +| struct comedi_subdevice *s,struct comedi_insn *insn, unsigned int *data) | +| | +| | ++----------------------------------------------------------------------------+ +| Task : read the Timer value | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| struct comedi_subdevice *s | +| struct comedi_insn *insn | +| unsigned int *data | +| | ++----------------------------------------------------------------------------+ +| Return Value : | +| for Timer: data[0]= Timer constant | +| | +| for watchdog: data[0]=0 (still running) | +| data[0]=1 (run down) | +| | ++----------------------------------------------------------------------------+ +*/ +int i_APCI3120_InsnReadTimer(struct comedi_device * dev, struct comedi_subdevice * s, + struct comedi_insn * insn, unsigned int * data) +{ + BYTE b_Tmp; + USHORT us_TmpValue, us_TmpValue_2, us_StatusValue; + + if ((devpriv->b_Timer2Mode != APCI3120_WATCHDOG) + && (devpriv->b_Timer2Mode != APCI3120_TIMER)) { + comedi_error(dev, "\nread:timer2 not configured "); + } + + //this_board->i_hwdrv_InsnReadTimer(dev,data); + if (devpriv->b_Timer2Mode == APCI3120_TIMER) { + + //Read the LOW WORD of Timer 2 register + b_Tmp = ((devpriv-> + b_DigitalOutputRegister) & 0xF0) | + APCI3120_SELECT_TIMER_2_LOW_WORD; + outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0); + + us_TmpValue = inw(devpriv->iobase + APCI3120_TIMER_VALUE); + + //Read the HIGH WORD of Timer 2 register + b_Tmp = ((devpriv-> + b_DigitalOutputRegister) & 0xF0) | + APCI3120_SELECT_TIMER_2_HIGH_WORD; + outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0); + + us_TmpValue_2 = inw(devpriv->iobase + APCI3120_TIMER_VALUE); + + // combining both words + data[0] = (UINT) ((us_TmpValue) | ((us_TmpValue_2) << 16)); + + } else // Read watch dog status + { + + us_StatusValue = inw(devpriv->iobase + APCI3120_RD_STATUS); + us_StatusValue = + ((us_StatusValue & APCI3120_FC_TIMER) >> 12) & 1; + if (us_StatusValue == 1) { + // RESET FC_TIMER BIT + inb(devpriv->iobase + APCI3120_TIMER_STATUS_REGISTER); + } + data[0] = us_StatusValue; // when data[0] = 1 then the watch dog has rundown + } + return insn->n; +} + +/* ++----------------------------------------------------------------------------+ +| DIGITAL INPUT SUBDEVICE | ++----------------------------------------------------------------------------+ +*/ + +/* ++----------------------------------------------------------------------------+ +| Function name :int i_APCI3120_InsnReadDigitalInput(struct comedi_device *dev, | +| struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data) | +| | +| | ++----------------------------------------------------------------------------+ +| Task : Reads the value of the specified Digital input channel| +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| struct comedi_subdevice *s | +| struct comedi_insn *insn | +| unsigned int *data | ++----------------------------------------------------------------------------+ +| Return Value : | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_InsnReadDigitalInput(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + UINT ui_Chan, ui_TmpValue; + + ui_Chan = CR_CHAN(insn->chanspec); // channel specified + + //this_board->i_hwdrv_InsnReadDigitalInput(dev,ui_Chan,data); + if (ui_Chan >= 0 && ui_Chan <= 3) { + ui_TmpValue = (UINT) inw(devpriv->iobase + APCI3120_RD_STATUS); + + // since only 1 channel reqd to bring it to last bit it is rotated + // 8 +(chan - 1) times then ANDed with 1 for last bit. + *data = (ui_TmpValue >> (ui_Chan + 8)) & 1; + //return 0; + } else { + // comedi_error(dev," chan spec wrong"); + return -EINVAL; // "sorry channel spec wrong " + } + return insn->n; + +} + +/* ++----------------------------------------------------------------------------+ +| Function name :int i_APCI3120_InsnBitsDigitalInput(struct comedi_device *dev, | +|struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data) | +| | ++----------------------------------------------------------------------------+ +| Task : Reads the value of the Digital input Port i.e.4channels| +| value is returned in data[0] | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| struct comedi_subdevice *s | +| struct comedi_insn *insn | +| unsigned int *data | ++----------------------------------------------------------------------------+ +| Return Value : | +| | ++----------------------------------------------------------------------------+ +*/ +int i_APCI3120_InsnBitsDigitalInput(struct comedi_device * dev, struct comedi_subdevice * s, + struct comedi_insn * insn, unsigned int * data) +{ + UINT ui_TmpValue; + ui_TmpValue = (UINT) inw(devpriv->iobase + APCI3120_RD_STATUS); + /***** state of 4 channels in the 11, 10, 9, 8 bits of status reg + rotated right 8 times to bring them to last four bits + ANDed with oxf for value. + *****/ + + *data = (ui_TmpValue >> 8) & 0xf; + //this_board->i_hwdrv_InsnBitsDigitalInput(dev,data); + return insn->n; +} + +/* ++----------------------------------------------------------------------------+ +| DIGITAL OUTPUT SUBDEVICE | ++----------------------------------------------------------------------------+ +*/ +/* ++----------------------------------------------------------------------------+ +| Function name :int i_APCI3120_InsnConfigDigitalOutput(struct comedi_device | +| *dev,struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) | +| | ++----------------------------------------------------------------------------+ +| Task :Configure the output memory ON or OFF | +| | ++----------------------------------------------------------------------------+ +| Input Parameters :struct comedi_device *dev | +| struct comedi_subdevice *s | +| struct comedi_insn *insn | +| unsigned int *data | ++----------------------------------------------------------------------------+ +| Return Value : | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_InsnConfigDigitalOutput(struct comedi_device * dev, + struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data) +{ + + if ((data[0] != 0) && (data[0] != 1)) { + comedi_error(dev, + "Not a valid Data !!! ,Data should be 1 or 0\n"); + return -EINVAL; + } + if (data[0]) { + devpriv->b_OutputMemoryStatus = APCI3120_ENABLE; + + } else { + devpriv->b_OutputMemoryStatus = APCI3120_DISABLE; + devpriv->b_DigitalOutputRegister = 0; + } + if (!devpriv->b_OutputMemoryStatus) { + ui_Temp = 0; + + } //if(!devpriv->b_OutputMemoryStatus ) + + return insn->n; +} + +/* ++----------------------------------------------------------------------------+ +| Function name :int i_APCI3120_InsnBitsDigitalOutput(struct comedi_device *dev, | +| struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data) | +| | ++----------------------------------------------------------------------------+ +| Task : write diatal output port | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| struct comedi_subdevice *s | +| struct comedi_insn *insn | +| unsigned int *data | + data[0] Value to be written + data[1] :1 Set digital o/p ON + data[1] 2 Set digital o/p OFF with memory ON ++----------------------------------------------------------------------------+ +| Return Value : | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_InsnBitsDigitalOutput(struct comedi_device * dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + if ((data[0] > this_board->i_DoMaxdata) || (data[0] < 0)) { + + comedi_error(dev, "Data is not valid !!! \n"); + return -EINVAL; + } + + switch (data[1]) { + case 1: + data[0] = (data[0] << 4) | devpriv->b_DigitalOutputRegister; + break; + + case 2: + data[0] = data[0]; + break; + default: + printk("\nThe parameter passed is in error \n"); + return -EINVAL; + } // switch(data[1]) + outb(data[0], devpriv->iobase + APCI3120_DIGITAL_OUTPUT); + + devpriv->b_DigitalOutputRegister = data[0] & 0xF0; + + return insn->n; + +} + +/* ++----------------------------------------------------------------------------+ +| Function name :int i_APCI3120_InsnWriteDigitalOutput(struct comedi_device *dev,| +|struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) | +| | ++----------------------------------------------------------------------------+ +| Task : Write digiatl output | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| struct comedi_subdevice *s | +| struct comedi_insn *insn | +| unsigned int *data | + data[0] Value to be written + data[1] :1 Set digital o/p ON + data[1] 2 Set digital o/p OFF with memory ON ++----------------------------------------------------------------------------+ +| Return Value : | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_InsnWriteDigitalOutput(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + + UINT ui_Temp1; + + UINT ui_NoOfChannel = CR_CHAN(insn->chanspec); // get the channel + + if ((data[0] != 0) && (data[0] != 1)) { + comedi_error(dev, + "Not a valid Data !!! ,Data should be 1 or 0\n"); + return -EINVAL; + } + if ((ui_NoOfChannel > (this_board->i_NbrDoChannel - 1)) + || (ui_NoOfChannel < 0)) { + comedi_error(dev, + "This board doesn't have specified channel !!! \n"); + return -EINVAL; + } + + switch (data[1]) { + case 1: + data[0] = (data[0] << ui_NoOfChannel); +//ES05 data[0]=(data[0]<<4)|ui_Temp; + data[0] = (data[0] << 4) | devpriv->b_DigitalOutputRegister; + break; + + case 2: + data[0] = ~data[0] & 0x1; + ui_Temp1 = 1; + ui_Temp1 = ui_Temp1 << ui_NoOfChannel; + ui_Temp1 = ui_Temp1 << 4; +//ES05 ui_Temp=ui_Temp|ui_Temp1; + devpriv->b_DigitalOutputRegister = + devpriv->b_DigitalOutputRegister | ui_Temp1; + + data[0] = (data[0] << ui_NoOfChannel) ^ 0xf; + data[0] = data[0] << 4; +//ES05 data[0]=data[0]& ui_Temp; + data[0] = data[0] & devpriv->b_DigitalOutputRegister; + break; + default: + printk("\nThe parameter passed is in error \n"); + return -EINVAL; + } // switch(data[1]) + outb(data[0], devpriv->iobase + APCI3120_DIGITAL_OUTPUT); + +//ES05 ui_Temp=data[0] & 0xf0; + devpriv->b_DigitalOutputRegister = data[0] & 0xf0; + return (insn->n); + +} + +/* ++----------------------------------------------------------------------------+ +| ANALOG OUTPUT SUBDEVICE | ++----------------------------------------------------------------------------+ +*/ + +/* ++----------------------------------------------------------------------------+ +| Function name :int i_APCI3120_InsnWriteAnalogOutput(struct comedi_device *dev,| +|struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data) | +| | ++----------------------------------------------------------------------------+ +| Task : Write analog output | +| | ++----------------------------------------------------------------------------+ +| Input Parameters : struct comedi_device *dev | +| struct comedi_subdevice *s | +| struct comedi_insn *insn | +| unsigned int *data | ++----------------------------------------------------------------------------+ +| Return Value : | +| | ++----------------------------------------------------------------------------+ +*/ + +int i_APCI3120_InsnWriteAnalogOutput(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + UINT ui_Range, ui_Channel; + USHORT us_TmpValue; + + ui_Range = CR_RANGE(insn->chanspec); + ui_Channel = CR_CHAN(insn->chanspec); + + //this_board->i_hwdrv_InsnWriteAnalogOutput(dev, ui_Range, ui_Channel,data[0]); + if (ui_Range) // if 1 then unipolar + { + + if (data[0] != 0) + data[0] = + ((((ui_Channel & 0x03) << 14) & 0xC000) | (1 << + 13) | (data[0] + 8191)); + else + data[0] = + ((((ui_Channel & 0x03) << 14) & 0xC000) | (1 << + 13) | 8192); + + } else // if 0 then bipolar + { + data[0] = + ((((ui_Channel & 0x03) << 14) & 0xC000) | (0 << 13) | + data[0]); + + } + + //out put n values at the given channel. + // rt_printk("\nwaiting for DA_READY BIT"); + do //Waiting of DA_READY BIT + { + us_TmpValue = + ((USHORT) inw(devpriv->iobase + + APCI3120_RD_STATUS)) & 0x0001; + } while (us_TmpValue != 0x0001); + + if (ui_Channel <= 3) + // for channel 0-3 out at the register 1 (wrDac1-8) + // data[i] typecasted to ushort since word write is to be done + outw((USHORT) data[0], + devpriv->iobase + APCI3120_ANALOG_OUTPUT_1); + else + // for channel 4-7 out at the register 2 (wrDac5-8) + //data[i] typecasted to ushort since word write is to be done + outw((USHORT) data[0], + devpriv->iobase + APCI3120_ANALOG_OUTPUT_2); + + return insn->n; +} |