| /** |
| @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; |
| } |