hwdrv_apci3120.c revision f69b0d6451679f1466381a46ac7ab671a7b5668c
1/**
2@verbatim
3
4Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
5
6	ADDI-DATA GmbH
7	Dieselstrasse 3
8	D-77833 Ottersweier
9	Tel: +19(0)7223/9493-0
10	Fax: +49(0)7223/9493-92
11	http://www.addi-data-com
12	info@addi-data.com
13
14This 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.
15
16This 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.
17
18You 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
19
20You should also find the complete GPL in the COPYING file accompanying this source code.
21
22@endverbatim
23*/
24/*
25  +-----------------------------------------------------------------------+
26  | (C) ADDI-DATA GmbH          Dieselstrasse 3      D-77833 Ottersweier  |
27  +-----------------------------------------------------------------------+
28  | Tel : +49 (0) 7223/9493-0     | email    : info@addi-data.com         |
29  | Fax : +49 (0) 7223/9493-92    | Internet : http://www.addi-data.com   |
30  +-----------------------------------------------------------------------+
31  | Project     : APCI-3120       | Compiler   : GCC                      |
32  | Module name : hwdrv_apci3120.c| Version    : 2.96                     |
33  +-------------------------------+---------------------------------------+
34  | Project manager: Eric Stolz   | Date       :  02/12/2002              |
35  +-----------------------------------------------------------------------+
36  | Description :APCI3120 Module.  Hardware abstraction Layer for APCI3120|
37  +-----------------------------------------------------------------------+
38  |                             UPDATE'S                                  |
39  +-----------------------------------------------------------------------+
40  |   Date   |   Author  |          Description of updates                |
41  +----------+-----------+------------------------------------------------+
42  |          | 		 | 						  |
43  |          |           |						  |
44  +----------+-----------+------------------------------------------------+
45*/
46
47#include "hwdrv_apci3120.h"
48static unsigned int ui_Temp;
49
50/* FUNCTION DEFINITIONS */
51
52/*
53+----------------------------------------------------------------------------+
54|                           ANALOG INPUT SUBDEVICE   		                 |
55+----------------------------------------------------------------------------+
56*/
57
58/*
59+----------------------------------------------------------------------------+
60| Function name     :int i_APCI3120_InsnConfigAnalogInput(struct comedi_device *dev,|
61|  struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data)					 |
62|                                            						         |
63+----------------------------------------------------------------------------+
64| Task              : Calls card specific function  					     |
65|                     										                 |
66+----------------------------------------------------------------------------+
67| Input Parameters  : struct comedi_device *dev									 |
68|                     struct comedi_subdevice *s									 |
69|                     struct comedi_insn *insn                                      |
70|                     unsigned int *data      					         		 |
71+----------------------------------------------------------------------------+
72| Return Value      :              					                         |
73|                    													     |
74+----------------------------------------------------------------------------+
75*/
76
77int i_APCI3120_InsnConfigAnalogInput(struct comedi_device *dev, struct comedi_subdevice *s,
78	struct comedi_insn *insn, unsigned int *data)
79{
80	unsigned int i;
81
82	if ((data[0] != APCI3120_EOC_MODE) && (data[0] != APCI3120_EOS_MODE))
83		return -1;
84
85	/*  Check for Conversion time to be added ?? */
86	devpriv->ui_EocEosConversionTime = data[2];
87
88	if (data[0] == APCI3120_EOS_MODE) {
89
90		/* Test the number of the channel */
91		for (i = 0; i < data[3]; i++) {
92
93			if (CR_CHAN(data[4 + i]) >= this_board->i_NbrAiChannel) {
94				printk("bad channel list\n");
95				return -2;
96			}
97		}
98
99		devpriv->b_InterruptMode = APCI3120_EOS_MODE;
100
101		if (data[1])
102			devpriv->b_EocEosInterrupt = APCI3120_ENABLE;
103		else
104			devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
105		/*  Copy channel list and Range List to devpriv */
106
107		devpriv->ui_AiNbrofChannels = data[3];
108		for (i = 0; i < devpriv->ui_AiNbrofChannels; i++)
109			devpriv->ui_AiChannelList[i] = data[4 + i];
110
111	} else {			/*  EOC */
112		devpriv->b_InterruptMode = APCI3120_EOC_MODE;
113		if (data[1])
114			devpriv->b_EocEosInterrupt = APCI3120_ENABLE;
115		else
116			devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
117	}
118
119	return insn->n;
120}
121
122/*
123+----------------------------------------------------------------------------+
124| Function name     :int i_APCI3120_InsnReadAnalogInput(struct comedi_device *dev,  |
125|			struct comedi_subdevice *s,struct comedi_insn *insn, unsigned int *data)	 |
126|                                            						         |
127+----------------------------------------------------------------------------+
128| Task              :  card specific function								 |
129|				Reads analog input in synchronous mode               |
130|			  EOC and EOS is selected as per configured              |
131|                     if no conversion time is set uses default conversion   |
132|			  time 10 microsec.					      				 |
133|                     										                 |
134+----------------------------------------------------------------------------+
135| Input Parameters  : struct comedi_device *dev									 |
136|                     struct comedi_subdevice *s									 |
137|                     struct comedi_insn *insn                                      |
138|                     unsigned int *data     									 |
139+----------------------------------------------------------------------------+
140| Return Value      :              					                         |
141|                    													     |
142+----------------------------------------------------------------------------+
143*/
144
145int i_APCI3120_InsnReadAnalogInput(struct comedi_device *dev, struct comedi_subdevice *s,
146	struct comedi_insn *insn, unsigned int *data)
147{
148	unsigned short us_ConvertTiming, us_TmpValue, i;
149	unsigned char b_Tmp;
150
151	/*  fix convertion time to 10 us */
152	if (!devpriv->ui_EocEosConversionTime) {
153		printk("No timer0 Value using 10 us\n");
154		us_ConvertTiming = 10;
155	} else
156		us_ConvertTiming = (unsigned short) (devpriv->ui_EocEosConversionTime / 1000);	/*  nano to useconds */
157
158	/*  this_board->i_hwdrv_InsnReadAnalogInput(dev,us_ConvertTiming,insn->n,&insn->chanspec,data,insn->unused[0]); */
159
160	/*  Clear software registers */
161	devpriv->b_TimerSelectMode = 0;
162	devpriv->b_ModeSelectRegister = 0;
163	devpriv->us_OutputRegister = 0;
164/* devpriv->b_DigitalOutputRegister=0; */
165
166	if (insn->unused[0] == 222) {	/*  second insn read */
167		for (i = 0; i < insn->n; i++)
168			data[i] = devpriv->ui_AiReadData[i];
169	} else {
170		devpriv->tsk_Current = current;	/*  Save the current process task structure */
171/*
172 * Testing if board have the new Quartz and calculate the time value
173 * to set in the timer
174 */
175
176		us_TmpValue =
177			(unsigned short) inw(devpriv->iobase + APCI3120_RD_STATUS);
178
179		/* EL250804: Testing if board APCI3120 have the new Quartz or if it is an APCI3001 */
180		if ((us_TmpValue & 0x00B0) == 0x00B0
181			|| !strcmp(this_board->pc_DriverName, "apci3001")) {
182			us_ConvertTiming = (us_ConvertTiming * 2) - 2;
183		} else {
184			us_ConvertTiming =
185				((us_ConvertTiming * 12926) / 10000) - 1;
186		}
187
188		us_TmpValue = (unsigned short) devpriv->b_InterruptMode;
189
190		switch (us_TmpValue) {
191
192		case APCI3120_EOC_MODE:
193
194/*
195 * Testing the interrupt flag and set the EOC bit Clears the FIFO
196 */
197			inw(devpriv->iobase + APCI3120_RESET_FIFO);
198
199			/*  Initialize the sequence array */
200
201			/* if (!i_APCI3120_SetupChannelList(dev,s,1,chanlist,0))  return -EINVAL; */
202
203			if (!i_APCI3120_SetupChannelList(dev, s, 1,
204					&insn->chanspec, 0))
205				return -EINVAL;
206
207			/* Initialize Timer 0 mode 4 */
208			devpriv->b_TimerSelectMode =
209				(devpriv->
210				b_TimerSelectMode & 0xFC) |
211				APCI3120_TIMER_0_MODE_4;
212			outb(devpriv->b_TimerSelectMode,
213				devpriv->iobase + APCI3120_TIMER_CRT1);
214
215			/*  Reset the scan bit and Disables the  EOS, DMA, EOC interrupt */
216			devpriv->b_ModeSelectRegister =
217				devpriv->
218				b_ModeSelectRegister & APCI3120_DISABLE_SCAN;
219
220			if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) {
221
222				/* Disables the EOS,DMA and enables the EOC interrupt */
223				devpriv->b_ModeSelectRegister =
224					(devpriv->
225					b_ModeSelectRegister &
226					APCI3120_DISABLE_EOS_INT) |
227					APCI3120_ENABLE_EOC_INT;
228				inw(devpriv->iobase);
229
230			} else {
231				devpriv->b_ModeSelectRegister =
232					devpriv->
233					b_ModeSelectRegister &
234					APCI3120_DISABLE_ALL_INTERRUPT_WITHOUT_TIMER;
235			}
236
237			outb(devpriv->b_ModeSelectRegister,
238				devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
239
240			/*  Sets gate 0 */
241			devpriv->us_OutputRegister =
242				(devpriv->
243				us_OutputRegister & APCI3120_CLEAR_PA_PR) |
244				APCI3120_ENABLE_TIMER0;
245			outw(devpriv->us_OutputRegister,
246				devpriv->iobase + APCI3120_WR_ADDRESS);
247
248			/*  Select Timer 0 */
249			b_Tmp = ((devpriv->
250					b_DigitalOutputRegister) & 0xF0) |
251				APCI3120_SELECT_TIMER_0_WORD;
252			outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
253
254			/* Set the convertion time */
255			outw(us_ConvertTiming,
256				devpriv->iobase + APCI3120_TIMER_VALUE);
257
258			us_TmpValue =
259				(unsigned short) inw(dev->iobase + APCI3120_RD_STATUS);
260
261			if (devpriv->b_EocEosInterrupt == APCI3120_DISABLE) {
262
263				do {
264					/*  Waiting for the end of conversion */
265					us_TmpValue =
266						inw(devpriv->iobase +
267						APCI3120_RD_STATUS);
268				} while ((us_TmpValue & APCI3120_EOC) ==
269					APCI3120_EOC);
270
271				/* Read the result in FIFO  and put it in insn data pointer */
272				us_TmpValue = inw(devpriv->iobase + 0);
273				*data = us_TmpValue;
274
275				inw(devpriv->iobase + APCI3120_RESET_FIFO);
276			}
277
278			break;
279
280		case APCI3120_EOS_MODE:
281
282			inw(devpriv->iobase);
283			/*  Clears the FIFO */
284			inw(devpriv->iobase + APCI3120_RESET_FIFO);
285			/*  clear PA PR  and disable timer 0 */
286
287			devpriv->us_OutputRegister =
288				(devpriv->
289				us_OutputRegister & APCI3120_CLEAR_PA_PR) |
290				APCI3120_DISABLE_TIMER0;
291
292			outw(devpriv->us_OutputRegister,
293				devpriv->iobase + APCI3120_WR_ADDRESS);
294
295			if (!i_APCI3120_SetupChannelList(dev, s,
296					devpriv->ui_AiNbrofChannels,
297					devpriv->ui_AiChannelList, 0))
298				return -EINVAL;
299
300			/* Initialize Timer 0 mode 2 */
301			devpriv->b_TimerSelectMode =
302				(devpriv->
303				b_TimerSelectMode & 0xFC) |
304				APCI3120_TIMER_0_MODE_2;
305			outb(devpriv->b_TimerSelectMode,
306				devpriv->iobase + APCI3120_TIMER_CRT1);
307
308			/* Select Timer 0 */
309			b_Tmp = ((devpriv->
310					b_DigitalOutputRegister) & 0xF0) |
311				APCI3120_SELECT_TIMER_0_WORD;
312			outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
313
314			/* Set the convertion time */
315			outw(us_ConvertTiming,
316				devpriv->iobase + APCI3120_TIMER_VALUE);
317
318			/* Set the scan bit */
319			devpriv->b_ModeSelectRegister =
320				devpriv->
321				b_ModeSelectRegister | APCI3120_ENABLE_SCAN;
322			outb(devpriv->b_ModeSelectRegister,
323				devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
324
325			/* If Interrupt function is loaded */
326			if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) {
327				/* Disables the EOC,DMA and enables the EOS interrupt */
328				devpriv->b_ModeSelectRegister =
329					(devpriv->
330					b_ModeSelectRegister &
331					APCI3120_DISABLE_EOC_INT) |
332					APCI3120_ENABLE_EOS_INT;
333				inw(devpriv->iobase);
334
335			} else
336				devpriv->b_ModeSelectRegister =
337					devpriv->
338					b_ModeSelectRegister &
339					APCI3120_DISABLE_ALL_INTERRUPT_WITHOUT_TIMER;
340
341			outb(devpriv->b_ModeSelectRegister,
342				devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
343
344			inw(devpriv->iobase + APCI3120_RD_STATUS);
345
346			/* Sets gate 0 */
347
348			devpriv->us_OutputRegister =
349				devpriv->
350				us_OutputRegister | APCI3120_ENABLE_TIMER0;
351			outw(devpriv->us_OutputRegister,
352				devpriv->iobase + APCI3120_WR_ADDRESS);
353
354			/* Start conversion */
355			outw(0, devpriv->iobase + APCI3120_START_CONVERSION);
356
357			/* Waiting of end of convertion if interrupt is not installed */
358			if (devpriv->b_EocEosInterrupt == APCI3120_DISABLE) {
359				/* Waiting the end of convertion */
360				do {
361					us_TmpValue =
362						inw(devpriv->iobase +
363						APCI3120_RD_STATUS);
364				} while ((us_TmpValue & APCI3120_EOS) !=
365					 APCI3120_EOS);
366
367				for (i = 0; i < devpriv->ui_AiNbrofChannels;
368					i++) {
369					/* Read the result in FIFO and write them in shared memory */
370					us_TmpValue = inw(devpriv->iobase);
371					data[i] = (unsigned int) us_TmpValue;
372				}
373
374				devpriv->b_InterruptMode = APCI3120_EOC_MODE;	/*  Restore defaults. */
375			}
376			break;
377
378		default:
379			printk("inputs wrong\n");
380
381		}
382		devpriv->ui_EocEosConversionTime = 0;	/*  re initializing the variable; */
383	}
384
385	return insn->n;
386
387}
388
389/*
390+----------------------------------------------------------------------------+
391| Function name     :int i_APCI3120_StopCyclicAcquisition(struct comedi_device *dev,|
392| 											     struct comedi_subdevice *s)|
393|                                        									 |
394+----------------------------------------------------------------------------+
395| Task              : Stops Cyclic acquisition  						     |
396|                     										                 |
397+----------------------------------------------------------------------------+
398| Input Parameters  : struct comedi_device *dev									 |
399|                     struct comedi_subdevice *s									 |
400|                                                 					         |
401+----------------------------------------------------------------------------+
402| Return Value      :0              					                     |
403|                    													     |
404+----------------------------------------------------------------------------+
405*/
406
407int i_APCI3120_StopCyclicAcquisition(struct comedi_device *dev, struct comedi_subdevice *s)
408{
409	/*  Disable A2P Fifo write and AMWEN signal */
410	outw(0, devpriv->i_IobaseAddon + 4);
411
412	/* Disable Bus Master ADD ON */
413	outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0);
414	outw(0, devpriv->i_IobaseAddon + 2);
415	outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0);
416	outw(0, devpriv->i_IobaseAddon + 2);
417
418	/* Disable BUS Master PCI */
419	outl(0, devpriv->i_IobaseAmcc + AMCC_OP_REG_MCSR);
420
421	/* outl(inl(devpriv->i_IobaseAmcc+AMCC_OP_REG_INTCSR)&(~AINT_WRITE_COMPL),
422	 * devpriv->i_IobaseAmcc+AMCC_OP_REG_INTCSR);  stop amcc irqs */
423
424	/* outl(inl(devpriv->i_IobaseAmcc+AMCC_OP_REG_MCSR)&(~EN_A2P_TRANSFERS),
425	 * devpriv->i_IobaseAmcc+AMCC_OP_REG_MCSR);  stop DMA */
426
427	/* Disable ext trigger */
428	i_APCI3120_ExttrigDisable(dev);
429
430	devpriv->us_OutputRegister = 0;
431	/* stop  counters */
432	outw(devpriv->
433		us_OutputRegister & APCI3120_DISABLE_TIMER0 &
434		APCI3120_DISABLE_TIMER1, dev->iobase + APCI3120_WR_ADDRESS);
435
436	outw(APCI3120_DISABLE_ALL_TIMER, dev->iobase + APCI3120_WR_ADDRESS);
437
438	/* DISABLE_ALL_INTERRUPT */
439	outb(APCI3120_DISABLE_ALL_INTERRUPT,
440		dev->iobase + APCI3120_WRITE_MODE_SELECT);
441	/* Flush FIFO */
442	inb(dev->iobase + APCI3120_RESET_FIFO);
443	inw(dev->iobase + APCI3120_RD_STATUS);
444	devpriv->ui_AiActualScan = 0;
445	devpriv->ui_AiActualScanPosition = 0;
446	s->async->cur_chan = 0;
447	devpriv->ui_AiBufferPtr = 0;
448	devpriv->b_AiContinuous = 0;
449	devpriv->ui_DmaActualBuffer = 0;
450
451	devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE;
452	devpriv->b_InterruptMode = APCI3120_EOC_MODE;
453	devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
454	i_APCI3120_Reset(dev);
455	return 0;
456}
457
458/*
459+----------------------------------------------------------------------------+
460| Function name     :int i_APCI3120_CommandTestAnalogInput(struct comedi_device *dev|
461|			,struct comedi_subdevice *s,struct comedi_cmd *cmd)					 |
462|                                        									 |
463+----------------------------------------------------------------------------+
464| Task              : Test validity for a command for cyclic anlog input     |
465|                       acquisition  						     			 |
466|                     										                 |
467+----------------------------------------------------------------------------+
468| Input Parameters  : struct comedi_device *dev									 |
469|                     struct comedi_subdevice *s									 |
470|                     struct comedi_cmd *cmd              					         |
471+----------------------------------------------------------------------------+
472| Return Value      :0              					                     |
473|                    													     |
474+----------------------------------------------------------------------------+
475*/
476
477int i_APCI3120_CommandTestAnalogInput(struct comedi_device *dev, struct comedi_subdevice *s,
478	struct comedi_cmd *cmd)
479{
480	int err = 0;
481	int tmp;		/*  divisor1,divisor2; */
482
483	/*  step 1: make sure trigger sources are trivially valid */
484
485	tmp = cmd->start_src;
486	cmd->start_src &= TRIG_NOW | TRIG_EXT;
487	if (!cmd->start_src || tmp != cmd->start_src)
488		err++;
489
490	tmp = cmd->scan_begin_src;
491	cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW;
492	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
493		err++;
494
495	tmp = cmd->convert_src;
496	cmd->convert_src &= TRIG_TIMER;
497	if (!cmd->convert_src || tmp != cmd->convert_src)
498		err++;
499
500	tmp = cmd->scan_end_src;
501	cmd->scan_end_src &= TRIG_COUNT;
502	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
503		err++;
504
505	tmp = cmd->stop_src;
506	cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
507	if (!cmd->stop_src || tmp != cmd->stop_src)
508		err++;
509
510	if (err)
511		return 1;
512
513	/* step 2: make sure trigger sources are unique and mutually compatible */
514
515	if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
516		err++;
517
518	if (cmd->scan_begin_src != TRIG_TIMER &&
519		cmd->scan_begin_src != TRIG_FOLLOW)
520		err++;
521
522	if (cmd->convert_src != TRIG_TIMER)
523		err++;
524
525	if (cmd->scan_end_src != TRIG_COUNT) {
526		cmd->scan_end_src = TRIG_COUNT;
527		err++;
528	}
529
530	if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
531		err++;
532
533	if (err)
534		return 2;
535
536	/*  step 3: make sure arguments are trivially compatible */
537
538	if (cmd->start_arg != 0) {
539		cmd->start_arg = 0;
540		err++;
541	}
542
543	if (cmd->scan_begin_src == TRIG_TIMER) {	/*  Test Delay timing */
544		if (cmd->scan_begin_arg < this_board->ui_MinDelaytimeNs) {
545			cmd->scan_begin_arg = this_board->ui_MinDelaytimeNs;
546			err++;
547		}
548	}
549
550	if (cmd->convert_src == TRIG_TIMER) {	/*  Test Acquisition timing */
551		if (cmd->scan_begin_src == TRIG_TIMER) {
552			if ((cmd->convert_arg)
553				&& (cmd->convert_arg <
554					this_board->ui_MinAcquisitiontimeNs)) {
555				cmd->convert_arg =
556					this_board->ui_MinAcquisitiontimeNs;
557				err++;
558			}
559		} else {
560			if (cmd->convert_arg <
561				this_board->ui_MinAcquisitiontimeNs) {
562				cmd->convert_arg =
563					this_board->ui_MinAcquisitiontimeNs;
564				err++;
565
566			}
567		}
568	}
569
570	if (!cmd->chanlist_len) {
571		cmd->chanlist_len = 1;
572		err++;
573	}
574	if (cmd->chanlist_len > this_board->i_AiChannelList) {
575		cmd->chanlist_len = this_board->i_AiChannelList;
576		err++;
577	}
578	if (cmd->stop_src == TRIG_COUNT) {
579		if (!cmd->stop_arg) {
580			cmd->stop_arg = 1;
581			err++;
582		}
583	} else {		/*  TRIG_NONE */
584		if (cmd->stop_arg != 0) {
585			cmd->stop_arg = 0;
586			err++;
587		}
588	}
589
590	if (err)
591		return 3;
592
593	/*  step 4: fix up any arguments */
594
595	if (cmd->convert_src == TRIG_TIMER) {
596
597		if (cmd->scan_begin_src == TRIG_TIMER &&
598			cmd->scan_begin_arg <
599			cmd->convert_arg * cmd->scan_end_arg) {
600			cmd->scan_begin_arg =
601				cmd->convert_arg * cmd->scan_end_arg;
602			err++;
603		}
604	}
605
606	if (err)
607		return 4;
608
609	return 0;
610}
611
612/*
613+----------------------------------------------------------------------------+
614| Function name     : int i_APCI3120_CommandAnalogInput(struct comedi_device *dev,  |
615|												struct comedi_subdevice *s) |
616|                                        									 |
617+----------------------------------------------------------------------------+
618| Task              : Does asynchronous acquisition                          |
619|                     Determines the mode 1 or 2.						     |
620|                     										                 |
621+----------------------------------------------------------------------------+
622| Input Parameters  : struct comedi_device *dev									 |
623|                     struct comedi_subdevice *s									 |
624|                     														 |
625+----------------------------------------------------------------------------+
626| Return Value      :              					                         |
627|                    													     |
628+----------------------------------------------------------------------------+
629*/
630
631int i_APCI3120_CommandAnalogInput(struct comedi_device *dev, struct comedi_subdevice *s)
632{
633	struct comedi_cmd *cmd = &s->async->cmd;
634
635	/* loading private structure with cmd structure inputs */
636	devpriv->ui_AiFlags = cmd->flags;
637	devpriv->ui_AiNbrofChannels = cmd->chanlist_len;
638	devpriv->ui_AiScanLength = cmd->scan_end_arg;
639	devpriv->pui_AiChannelList = cmd->chanlist;
640
641	/* UPDATE-0.7.57->0.7.68devpriv->AiData=s->async->data; */
642	devpriv->AiData = s->async->prealloc_buf;
643	/* UPDATE-0.7.57->0.7.68devpriv->ui_AiDataLength=s->async->data_len; */
644	devpriv->ui_AiDataLength = s->async->prealloc_bufsz;
645
646	if (cmd->stop_src == TRIG_COUNT)
647		devpriv->ui_AiNbrofScans = cmd->stop_arg;
648	else
649		devpriv->ui_AiNbrofScans = 0;
650
651	devpriv->ui_AiTimer0 = 0;	/*  variables changed to timer0,timer1 */
652	devpriv->ui_AiTimer1 = 0;
653	if ((devpriv->ui_AiNbrofScans == 0) || (devpriv->ui_AiNbrofScans == -1))
654		devpriv->b_AiContinuous = 1;	/*  user want neverending analog acquisition */
655	/*  stopped using cancel */
656
657	if (cmd->start_src == TRIG_EXT)
658		devpriv->b_ExttrigEnable = APCI3120_ENABLE;
659	else
660		devpriv->b_ExttrigEnable = APCI3120_DISABLE;
661
662	if (cmd->scan_begin_src == TRIG_FOLLOW) {
663		/*  mode 1 or 3 */
664		if (cmd->convert_src == TRIG_TIMER) {
665			/*  mode 1 */
666
667			devpriv->ui_AiTimer0 = cmd->convert_arg;	/*  timer constant in nano seconds */
668			/* return this_board->i_hwdrv_CommandAnalogInput(1,dev,s); */
669			return i_APCI3120_CyclicAnalogInput(1, dev, s);
670		}
671
672	}
673	if ((cmd->scan_begin_src == TRIG_TIMER)
674		&& (cmd->convert_src == TRIG_TIMER)) {
675		/*  mode 2 */
676		devpriv->ui_AiTimer1 = cmd->scan_begin_arg;
677		devpriv->ui_AiTimer0 = cmd->convert_arg;	/*  variable changed timer2 to timer0 */
678		/* return this_board->i_hwdrv_CommandAnalogInput(2,dev,s); */
679		return i_APCI3120_CyclicAnalogInput(2, dev, s);
680	}
681	return -1;
682}
683
684/*
685+----------------------------------------------------------------------------+
686| Function name     :  int i_APCI3120_CyclicAnalogInput(int mode,            |
687|		 	   struct comedi_device * dev,struct comedi_subdevice * s)			 |
688+----------------------------------------------------------------------------+
689| Task              : This is used for analog input cyclic acquisition       |
690|			  Performs the command operations.                       |
691|			  If DMA is configured does DMA initialization           |
692|			  otherwise does the acquisition with EOS interrupt.     |
693|                     										                 |
694+----------------------------------------------------------------------------+
695| Input Parameters  : 														 |
696|                     														 |
697|                                                 					         |
698+----------------------------------------------------------------------------+
699| Return Value      :              					                         |
700|                    													     |
701+----------------------------------------------------------------------------+
702*/
703
704int i_APCI3120_CyclicAnalogInput(int mode, struct comedi_device *dev,
705	struct comedi_subdevice *s)
706{
707	unsigned char b_Tmp;
708	unsigned int ui_Tmp, ui_DelayTiming = 0, ui_TimerValue1 = 0, dmalen0 =
709		0, dmalen1 = 0, ui_TimerValue2 =
710		0, ui_TimerValue0, ui_ConvertTiming;
711	unsigned short us_TmpValue;
712
713	/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
714	/* devpriv->b_AiCyclicAcquisition=APCI3120_ENABLE; */
715	/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
716
717	/*******************/
718	/* Resets the FIFO */
719	/*******************/
720	inb(dev->iobase + APCI3120_RESET_FIFO);
721
722	/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
723	/* inw(dev->iobase+APCI3120_RD_STATUS); */
724	/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
725
726	/***************************/
727	/* Acquisition initialized */
728	/***************************/
729	/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
730	devpriv->b_AiCyclicAcquisition = APCI3120_ENABLE;
731	/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
732
733	/*  clear software  registers */
734	devpriv->b_TimerSelectMode = 0;
735	devpriv->us_OutputRegister = 0;
736	devpriv->b_ModeSelectRegister = 0;
737	/* devpriv->b_DigitalOutputRegister=0; */
738
739	/* COMMENT JK 07.05.04: Followings calls are in i_APCI3120_StartAnalogInputAcquisition */
740
741	/****************************/
742	/* Clear Timer Write TC int */
743	/****************************/
744	outl(APCI3120_CLEAR_WRITE_TC_INT,
745		devpriv->i_IobaseAmcc + APCI3120_AMCC_OP_REG_INTCSR);
746
747	/************************************/
748	/* Clears the timer status register */
749	/************************************/
750
751	/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
752	/* inw(dev->iobase+APCI3120_TIMER_STATUS_REGISTER); */
753	/* inb(dev->iobase + APCI3120_TIMER_STATUS_REGISTER); */
754	/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
755
756	/**************************/
757	/* Disables All Timer     */
758	/* Sets PR and PA to 0    */
759	/**************************/
760	devpriv->us_OutputRegister = devpriv->us_OutputRegister &
761		APCI3120_DISABLE_TIMER0 &
762		APCI3120_DISABLE_TIMER1 & APCI3120_CLEAR_PA_PR;
763
764	outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS);
765
766	/*******************/
767	/* Resets the FIFO */
768	/*******************/
769	/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
770	inb(devpriv->iobase + APCI3120_RESET_FIFO);
771	/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
772
773	devpriv->ui_AiActualScan = 0;
774	devpriv->ui_AiActualScanPosition = 0;
775	s->async->cur_chan = 0;
776	devpriv->ui_AiBufferPtr = 0;
777	devpriv->ui_DmaActualBuffer = 0;
778
779	/*  value for timer2  minus -2 has to be done .....dunno y?? */
780	ui_TimerValue2 = devpriv->ui_AiNbrofScans - 2;
781	ui_ConvertTiming = devpriv->ui_AiTimer0;
782
783	if (mode == 2)
784		ui_DelayTiming = devpriv->ui_AiTimer1;
785
786   /**********************************/
787	/* Initializes the sequence array */
788   /**********************************/
789	if (!i_APCI3120_SetupChannelList(dev, s, devpriv->ui_AiNbrofChannels,
790			devpriv->pui_AiChannelList, 0))
791		return -EINVAL;
792
793	us_TmpValue = (unsigned short) inw(dev->iobase + APCI3120_RD_STATUS);
794/*** EL241003 : add this section in comment because floats must not be used
795	if((us_TmpValue & 0x00B0)==0x00B0)
796	 {
797		f_ConvertValue=(((float)ui_ConvertTiming * 0.002) - 2);
798		ui_TimerValue0=(unsigned int)f_ConvertValue;
799		if (mode==2)
800		{
801			f_DelayValue     = (((float)ui_DelayTiming * 0.00002) - 2);
802			ui_TimerValue1  =   (unsigned int) f_DelayValue;
803		}
804	 }
805	else
806	 {
807		f_ConvertValue=(((float)ui_ConvertTiming * 0.0012926) - 1);
808		ui_TimerValue0=(unsigned int)f_ConvertValue;
809		if (mode == 2)
810		{
811		     f_DelayValue     = (((float)ui_DelayTiming * 0.000012926) - 1);
812		     ui_TimerValue1  =   (unsigned int) f_DelayValue;
813		}
814	}
815***********************************************************************************************/
816/*** EL241003 Begin : add this section to replace floats calculation by integer calculations **/
817	/* EL250804: Testing if board APCI3120 have the new Quartz or if it is an APCI3001 */
818	if ((us_TmpValue & 0x00B0) == 0x00B0
819		|| !strcmp(this_board->pc_DriverName, "apci3001")) {
820		ui_TimerValue0 = ui_ConvertTiming * 2 - 2000;
821		ui_TimerValue0 = ui_TimerValue0 / 1000;
822
823		if (mode == 2) {
824			ui_DelayTiming = ui_DelayTiming / 1000;
825			ui_TimerValue1 = ui_DelayTiming * 2 - 200;
826			ui_TimerValue1 = ui_TimerValue1 / 100;
827		}
828	} else {
829		ui_ConvertTiming = ui_ConvertTiming / 1000;
830		ui_TimerValue0 = ui_ConvertTiming * 12926 - 10000;
831		ui_TimerValue0 = ui_TimerValue0 / 10000;
832
833		if (mode == 2) {
834			ui_DelayTiming = ui_DelayTiming / 1000;
835			ui_TimerValue1 = ui_DelayTiming * 12926 - 1;
836			ui_TimerValue1 = ui_TimerValue1 / 1000000;
837		}
838	}
839/*** EL241003 End ******************************************************************************/
840
841	if (devpriv->b_ExttrigEnable == APCI3120_ENABLE)
842		i_APCI3120_ExttrigEnable(dev);	/*  activate EXT trigger */
843	switch (mode) {
844	case 1:
845		/*  init timer0 in mode 2 */
846		devpriv->b_TimerSelectMode =
847			(devpriv->
848			b_TimerSelectMode & 0xFC) | APCI3120_TIMER_0_MODE_2;
849		outb(devpriv->b_TimerSelectMode,
850			dev->iobase + APCI3120_TIMER_CRT1);
851
852		/* Select Timer 0 */
853		b_Tmp = ((devpriv->
854				b_DigitalOutputRegister) & 0xF0) |
855			APCI3120_SELECT_TIMER_0_WORD;
856		outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
857		/* Set the convertion time */
858		outw(((unsigned short) ui_TimerValue0),
859			dev->iobase + APCI3120_TIMER_VALUE);
860		break;
861
862	case 2:
863		/*  init timer1 in mode 2 */
864		devpriv->b_TimerSelectMode =
865			(devpriv->
866			b_TimerSelectMode & 0xF3) | APCI3120_TIMER_1_MODE_2;
867		outb(devpriv->b_TimerSelectMode,
868			dev->iobase + APCI3120_TIMER_CRT1);
869
870		/* Select Timer 1 */
871		b_Tmp = ((devpriv->
872				b_DigitalOutputRegister) & 0xF0) |
873			APCI3120_SELECT_TIMER_1_WORD;
874		outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
875		/* Set the convertion time */
876		outw(((unsigned short) ui_TimerValue1),
877			dev->iobase + APCI3120_TIMER_VALUE);
878
879		/*  init timer0 in mode 2 */
880		devpriv->b_TimerSelectMode =
881			(devpriv->
882			b_TimerSelectMode & 0xFC) | APCI3120_TIMER_0_MODE_2;
883		outb(devpriv->b_TimerSelectMode,
884			dev->iobase + APCI3120_TIMER_CRT1);
885
886		/* Select Timer 0 */
887		b_Tmp = ((devpriv->
888				b_DigitalOutputRegister) & 0xF0) |
889			APCI3120_SELECT_TIMER_0_WORD;
890		outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
891
892		/* Set the convertion time */
893		outw(((unsigned short) ui_TimerValue0),
894			dev->iobase + APCI3120_TIMER_VALUE);
895		break;
896
897	}
898	/*    ##########common for all modes################# */
899
900	/***********************/
901	/* Clears the SCAN bit */
902	/***********************/
903
904	/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
905	/* devpriv->b_ModeSelectRegister=devpriv->b_ModeSelectRegister | APCI3120_DISABLE_SCAN; */
906
907	devpriv->b_ModeSelectRegister = devpriv->b_ModeSelectRegister &
908		APCI3120_DISABLE_SCAN;
909	/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
910
911	outb(devpriv->b_ModeSelectRegister,
912		dev->iobase + APCI3120_WRITE_MODE_SELECT);
913
914	/*  If DMA is disabled */
915	if (devpriv->us_UseDma == APCI3120_DISABLE) {
916		/*  disable EOC and enable EOS */
917		devpriv->b_InterruptMode = APCI3120_EOS_MODE;
918		devpriv->b_EocEosInterrupt = APCI3120_ENABLE;
919
920		devpriv->b_ModeSelectRegister =
921			(devpriv->
922			b_ModeSelectRegister & APCI3120_DISABLE_EOC_INT) |
923			APCI3120_ENABLE_EOS_INT;
924		outb(devpriv->b_ModeSelectRegister,
925			dev->iobase + APCI3120_WRITE_MODE_SELECT);
926
927		if (!devpriv->b_AiContinuous) {
928/*
929 * configure Timer2 For counting EOS Reset gate 2 of Timer 2 to
930 * disable it (Set Bit D14 to 0)
931 */
932			devpriv->us_OutputRegister =
933				devpriv->
934				us_OutputRegister & APCI3120_DISABLE_TIMER2;
935			outw(devpriv->us_OutputRegister,
936				dev->iobase + APCI3120_WR_ADDRESS);
937
938			/*  DISABLE TIMER intERRUPT */
939			devpriv->b_ModeSelectRegister =
940				devpriv->
941				b_ModeSelectRegister &
942				APCI3120_DISABLE_TIMER_INT & 0xEF;
943			outb(devpriv->b_ModeSelectRegister,
944				dev->iobase + APCI3120_WRITE_MODE_SELECT);
945
946			/* (1) Init timer 2 in mode 0 and write timer value */
947			devpriv->b_TimerSelectMode =
948				(devpriv->
949				b_TimerSelectMode & 0x0F) |
950				APCI3120_TIMER_2_MODE_0;
951			outb(devpriv->b_TimerSelectMode,
952				dev->iobase + APCI3120_TIMER_CRT1);
953
954			/* Writing LOW unsigned short */
955			b_Tmp = ((devpriv->
956					b_DigitalOutputRegister) & 0xF0) |
957				APCI3120_SELECT_TIMER_2_LOW_WORD;
958			outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
959			outw(LOWORD(ui_TimerValue2),
960				dev->iobase + APCI3120_TIMER_VALUE);
961
962			/* Writing HIGH unsigned short */
963			b_Tmp = ((devpriv->
964					b_DigitalOutputRegister) & 0xF0) |
965				APCI3120_SELECT_TIMER_2_HIGH_WORD;
966			outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
967			outw(HIWORD(ui_TimerValue2),
968				dev->iobase + APCI3120_TIMER_VALUE);
969
970			/* (2) Reset FC_TIMER BIT  Clearing timer status register */
971			inb(dev->iobase + APCI3120_TIMER_STATUS_REGISTER);
972			/*  enable timer counter and disable watch dog */
973			devpriv->b_ModeSelectRegister =
974				(devpriv->
975				b_ModeSelectRegister |
976				APCI3120_ENABLE_TIMER_COUNTER) &
977				APCI3120_DISABLE_WATCHDOG;
978			/*  select EOS clock input for timer 2 */
979			devpriv->b_ModeSelectRegister =
980				devpriv->
981				b_ModeSelectRegister |
982				APCI3120_TIMER2_SELECT_EOS;
983			/*  Enable timer2  interrupt */
984			devpriv->b_ModeSelectRegister =
985				devpriv->
986				b_ModeSelectRegister |
987				APCI3120_ENABLE_TIMER_INT;
988			outb(devpriv->b_ModeSelectRegister,
989				dev->iobase + APCI3120_WRITE_MODE_SELECT);
990			devpriv->b_Timer2Mode = APCI3120_COUNTER;
991			devpriv->b_Timer2Interrupt = APCI3120_ENABLE;
992		}
993	} else {
994		/* If DMA Enabled */
995
996		/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
997		/* inw(dev->iobase+0); reset EOC bit */
998		/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
999		devpriv->b_InterruptMode = APCI3120_DMA_MODE;
1000
1001		/************************************/
1002		/* Disables the EOC, EOS interrupt  */
1003		/************************************/
1004		devpriv->b_ModeSelectRegister = devpriv->b_ModeSelectRegister &
1005			APCI3120_DISABLE_EOC_INT & APCI3120_DISABLE_EOS_INT;
1006
1007		outb(devpriv->b_ModeSelectRegister,
1008			dev->iobase + APCI3120_WRITE_MODE_SELECT);
1009
1010		dmalen0 = devpriv->ui_DmaBufferSize[0];
1011		dmalen1 = devpriv->ui_DmaBufferSize[1];
1012
1013		if (!devpriv->b_AiContinuous) {
1014
1015			if (dmalen0 > (devpriv->ui_AiNbrofScans * devpriv->ui_AiScanLength * 2)) {	/*  must we fill full first buffer? */
1016				dmalen0 =
1017					devpriv->ui_AiNbrofScans *
1018					devpriv->ui_AiScanLength * 2;
1019			} else if (dmalen1 > (devpriv->ui_AiNbrofScans * devpriv->ui_AiScanLength * 2 - dmalen0))	/*  and must we fill full second buffer when first is once filled? */
1020				dmalen1 =
1021					devpriv->ui_AiNbrofScans *
1022					devpriv->ui_AiScanLength * 2 - dmalen0;
1023		}
1024
1025		if (devpriv->ui_AiFlags & TRIG_WAKE_EOS) {
1026			/*  don't we want wake up every scan? */
1027			if (dmalen0 > (devpriv->ui_AiScanLength * 2)) {
1028				dmalen0 = devpriv->ui_AiScanLength * 2;
1029				if (devpriv->ui_AiScanLength & 1)
1030					dmalen0 += 2;
1031			}
1032			if (dmalen1 > (devpriv->ui_AiScanLength * 2)) {
1033				dmalen1 = devpriv->ui_AiScanLength * 2;
1034				if (devpriv->ui_AiScanLength & 1)
1035					dmalen1 -= 2;
1036				if (dmalen1 < 4)
1037					dmalen1 = 4;
1038			}
1039		} else {	/*  isn't output buff smaller that our DMA buff? */
1040			if (dmalen0 > (devpriv->ui_AiDataLength))
1041				dmalen0 = devpriv->ui_AiDataLength;
1042			if (dmalen1 > (devpriv->ui_AiDataLength))
1043				dmalen1 = devpriv->ui_AiDataLength;
1044		}
1045		devpriv->ui_DmaBufferUsesize[0] = dmalen0;
1046		devpriv->ui_DmaBufferUsesize[1] = dmalen1;
1047
1048		/* Initialize DMA */
1049
1050/*
1051 * Set Transfer count enable bit and A2P_fifo reset bit in AGCSTS
1052 * register 1
1053 */
1054		ui_Tmp = AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO;
1055		outl(ui_Tmp, devpriv->i_IobaseAmcc + AMCC_OP_REG_AGCSTS);
1056
1057		/*  changed  since 16 bit interface for add on */
1058		/*********************/
1059		/* ENABLE BUS MASTER */
1060		/*********************/
1061		outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0);
1062		outw(APCI3120_ENABLE_TRANSFER_ADD_ON_LOW,
1063			devpriv->i_IobaseAddon + 2);
1064
1065		outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0);
1066		outw(APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH,
1067			devpriv->i_IobaseAddon + 2);
1068
1069/*
1070 * TO VERIFIED BEGIN JK 07.05.04: Comparison between WIN32 and Linux
1071 * driver
1072 */
1073		outw(0x1000, devpriv->i_IobaseAddon + 2);
1074		/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
1075
1076		/* 2 No change */
1077		/* A2P FIFO MANAGEMENT */
1078		/* A2P fifo reset & transfer control enable */
1079
1080		/***********************/
1081		/* A2P FIFO MANAGEMENT */
1082		/***********************/
1083		outl(APCI3120_A2P_FIFO_MANAGEMENT, devpriv->i_IobaseAmcc +
1084			APCI3120_AMCC_OP_MCSR);
1085
1086/*
1087 * 3
1088 * beginning address of dma buf The 32 bit address of dma buffer
1089 * is converted into two 16 bit addresses Can done by using _attach
1090 * and put into into an array array used may be for differnet pages
1091 */
1092
1093		/*  DMA Start Address Low */
1094		outw(APCI3120_ADD_ON_MWAR_LOW, devpriv->i_IobaseAddon + 0);
1095		outw((devpriv->ul_DmaBufferHw[0] & 0xFFFF),
1096			devpriv->i_IobaseAddon + 2);
1097
1098		/*************************/
1099		/* DMA Start Address High */
1100		/*************************/
1101		outw(APCI3120_ADD_ON_MWAR_HIGH, devpriv->i_IobaseAddon + 0);
1102		outw((devpriv->ul_DmaBufferHw[0] / 65536),
1103			devpriv->i_IobaseAddon + 2);
1104
1105/*
1106 * 4
1107 * amount of bytes to be transfered set transfer count used ADDON
1108 * MWTC register commented testing
1109 * outl(devpriv->ui_DmaBufferUsesize[0],
1110 * devpriv->i_IobaseAddon+AMCC_OP_REG_AMWTC);
1111 */
1112
1113		/**************************/
1114		/* Nbr of acquisition LOW */
1115		/**************************/
1116		outw(APCI3120_ADD_ON_MWTC_LOW, devpriv->i_IobaseAddon + 0);
1117		outw((devpriv->ui_DmaBufferUsesize[0] & 0xFFFF),
1118			devpriv->i_IobaseAddon + 2);
1119
1120		/***************************/
1121		/* Nbr of acquisition HIGH */
1122		/***************************/
1123		outw(APCI3120_ADD_ON_MWTC_HIGH, devpriv->i_IobaseAddon + 0);
1124		outw((devpriv->ui_DmaBufferUsesize[0] / 65536),
1125			devpriv->i_IobaseAddon + 2);
1126
1127/*
1128 * 5
1129 * To configure A2P FIFO testing outl(
1130 * FIFO_ADVANCE_ON_BYTE_2,devpriv->i_IobaseAmcc+AMCC_OP_REG_INTCSR);
1131 */
1132
1133		/******************/
1134		/* A2P FIFO RESET */
1135		/******************/
1136/*
1137 * TO VERIFY BEGIN JK 07.05.04: Comparison between WIN32 and Linux
1138 * driver
1139 */
1140		outl(0x04000000UL, devpriv->i_IobaseAmcc + AMCC_OP_REG_MCSR);
1141		/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
1142
1143/*
1144 * 6
1145 * ENABLE A2P FIFO WRITE AND ENABLE AMWEN AMWEN_ENABLE |
1146 * A2P_FIFO_WRITE_ENABLE (0x01|0x02)=0x03
1147 */
1148
1149		/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
1150		/* outw(3,devpriv->i_IobaseAddon + 4); */
1151		/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
1152
1153/*
1154 * 7
1155 * initialise end of dma interrupt AINT_WRITE_COMPL =
1156 * ENABLE_WRITE_TC_INT(ADDI)
1157 */
1158		/***************************************************/
1159		/* A2P FIFO CONFIGURATE, END OF DMA intERRUPT INIT */
1160		/***************************************************/
1161		outl((APCI3120_FIFO_ADVANCE_ON_BYTE_2 |
1162				APCI3120_ENABLE_WRITE_TC_INT),
1163			devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR);
1164
1165		/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
1166		/******************************************/
1167		/* ENABLE A2P FIFO WRITE AND ENABLE AMWEN */
1168		/******************************************/
1169		outw(3, devpriv->i_IobaseAddon + 4);
1170		/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
1171
1172		/******************/
1173		/* A2P FIFO RESET */
1174		/******************/
1175		/* BEGIN JK 07.05.04: Comparison between WIN32 and Linux driver */
1176		outl(0x04000000UL,
1177			devpriv->i_IobaseAmcc + APCI3120_AMCC_OP_MCSR);
1178		/* END JK 07.05.04: Comparison between WIN32 and Linux driver */
1179	}
1180
1181	if ((devpriv->us_UseDma == APCI3120_DISABLE)
1182		&& !devpriv->b_AiContinuous) {
1183		/*  set gate 2   to start conversion */
1184		devpriv->us_OutputRegister =
1185			devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER2;
1186		outw(devpriv->us_OutputRegister,
1187			dev->iobase + APCI3120_WR_ADDRESS);
1188	}
1189
1190	switch (mode) {
1191	case 1:
1192		/*  set gate 0   to start conversion */
1193		devpriv->us_OutputRegister =
1194			devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER0;
1195		outw(devpriv->us_OutputRegister,
1196			dev->iobase + APCI3120_WR_ADDRESS);
1197		break;
1198	case 2:
1199		/*  set  gate 0 and gate 1 */
1200		devpriv->us_OutputRegister =
1201			devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER1;
1202		devpriv->us_OutputRegister =
1203			devpriv->us_OutputRegister | APCI3120_ENABLE_TIMER0;
1204		outw(devpriv->us_OutputRegister,
1205			dev->iobase + APCI3120_WR_ADDRESS);
1206		break;
1207
1208	}
1209
1210	return 0;
1211
1212}
1213
1214/*
1215+----------------------------------------------------------------------------+
1216| 			intERNAL FUNCTIONS						                 |
1217+----------------------------------------------------------------------------+
1218*/
1219
1220/*
1221+----------------------------------------------------------------------------+
1222| Function name     : int i_APCI3120_Reset(struct comedi_device *dev)               |
1223|                                        									 |
1224|                                            						         |
1225+----------------------------------------------------------------------------+
1226| Task              : Hardware reset function   						     |
1227|                     										                 |
1228+----------------------------------------------------------------------------+
1229| Input Parameters  : 	struct comedi_device *dev									 |
1230|                     														 |
1231|                                                 					         |
1232+----------------------------------------------------------------------------+
1233| Return Value      :              					                         |
1234|                    													     |
1235+----------------------------------------------------------------------------+
1236*/
1237
1238int i_APCI3120_Reset(struct comedi_device *dev)
1239{
1240	unsigned int i;
1241	unsigned short us_TmpValue;
1242
1243	devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE;
1244	devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
1245	devpriv->b_InterruptMode = APCI3120_EOC_MODE;
1246	devpriv->ui_EocEosConversionTime = 0;	/*  set eoc eos conv time to 0 */
1247	devpriv->b_OutputMemoryStatus = 0;
1248
1249	/*  variables used in timer subdevice */
1250	devpriv->b_Timer2Mode = 0;
1251	devpriv->b_Timer2Interrupt = 0;
1252	devpriv->b_ExttrigEnable = 0;	/*  Disable ext trigger */
1253
1254	/* Disable all interrupts, watchdog for the anolog output */
1255	devpriv->b_ModeSelectRegister = 0;
1256	outb(devpriv->b_ModeSelectRegister,
1257		dev->iobase + APCI3120_WRITE_MODE_SELECT);
1258
1259	/*  Disables all counters, ext trigger and clears PA, PR */
1260	devpriv->us_OutputRegister = 0;
1261	outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS);
1262
1263/*
1264 * Code to set the all anolog o/p channel to 0v 8191 is decimal
1265 * value for zero(0 v)volt in bipolar mode(default)
1266 */
1267	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_1, dev->iobase + APCI3120_ANALOG_OUTPUT_1);	/* channel 1 */
1268	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_2, dev->iobase + APCI3120_ANALOG_OUTPUT_1);	/* channel 2 */
1269	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_3, dev->iobase + APCI3120_ANALOG_OUTPUT_1);	/* channel 3 */
1270	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_4, dev->iobase + APCI3120_ANALOG_OUTPUT_1);	/* channel 4 */
1271
1272	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_5, dev->iobase + APCI3120_ANALOG_OUTPUT_2);	/* channel 5 */
1273	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_6, dev->iobase + APCI3120_ANALOG_OUTPUT_2);	/* channel 6 */
1274	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_7, dev->iobase + APCI3120_ANALOG_OUTPUT_2);	/* channel 7 */
1275	outw(8191 | APCI3120_ANALOG_OP_CHANNEL_8, dev->iobase + APCI3120_ANALOG_OUTPUT_2);	/* channel 8 */
1276
1277	/*   Reset digital output to L0W */
1278
1279/* ES05  outb(0x0,dev->iobase+APCI3120_DIGITAL_OUTPUT); */
1280	udelay(10);
1281
1282	inw(dev->iobase + 0);	/* make a dummy read */
1283	inb(dev->iobase + APCI3120_RESET_FIFO);	/*  flush FIFO */
1284	inw(dev->iobase + APCI3120_RD_STATUS);	/*  flush A/D status register */
1285
1286	/* code to reset the RAM sequence */
1287	for (i = 0; i < 16; i++) {
1288		us_TmpValue = i << 8;	/* select the location */
1289		outw(us_TmpValue, dev->iobase + APCI3120_SEQ_RAM_ADDRESS);
1290	}
1291	return 0;
1292}
1293
1294/*
1295+----------------------------------------------------------------------------+
1296| Function name     : int i_APCI3120_SetupChannelList(struct comedi_device * dev,   |
1297|                     struct comedi_subdevice * s, int n_chan,unsigned int *chanlist|
1298|			  ,char check)											 |
1299|                                            						         |
1300+----------------------------------------------------------------------------+
1301| Task              :This function will first check channel list is ok or not|
1302|and then initialize the sequence RAM with the polarity, Gain,Channel number |
1303|If the last argument of function "check"is 1 then it only checks the channel|
1304|list is ok or not.												     		 |
1305|                     										                 |
1306+----------------------------------------------------------------------------+
1307| Input Parameters  : struct comedi_device * dev									 |
1308|                     struct comedi_subdevice * s									 |
1309|                     int n_chan                   					         |
1310			  unsigned int *chanlist
1311			  char check
1312+----------------------------------------------------------------------------+
1313| Return Value      :              					                         |
1314|                    													     |
1315+----------------------------------------------------------------------------+
1316*/
1317
1318int i_APCI3120_SetupChannelList(struct comedi_device *dev, struct comedi_subdevice *s,
1319	int n_chan, unsigned int *chanlist, char check)
1320{
1321	unsigned int i;		/* , differencial=0, bipolar=0; */
1322	unsigned int gain;
1323	unsigned short us_TmpValue;
1324
1325	/* correct channel and range number check itself comedi/range.c */
1326	if (n_chan < 1) {
1327		if (!check)
1328			comedi_error(dev, "range/channel list is empty!");
1329		return 0;
1330	}
1331	/*  All is ok, so we can setup channel/range list */
1332	if (check)
1333		return 1;
1334
1335	/* Code  to set the PA and PR...Here it set PA to 0.. */
1336	devpriv->us_OutputRegister =
1337		devpriv->us_OutputRegister & APCI3120_CLEAR_PA_PR;
1338	devpriv->us_OutputRegister = ((n_chan - 1) & 0xf) << 8;
1339	outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS);
1340
1341	for (i = 0; i < n_chan; i++) {
1342		/*  store range list to card */
1343		us_TmpValue = CR_CHAN(chanlist[i]);	/*  get channel number; */
1344
1345		if (CR_RANGE(chanlist[i]) < APCI3120_BIPOLAR_RANGES)
1346			us_TmpValue &= ((~APCI3120_UNIPOLAR) & 0xff);	/*  set bipolar */
1347		else
1348			us_TmpValue |= APCI3120_UNIPOLAR;	/*  enable unipolar...... */
1349
1350		gain = CR_RANGE(chanlist[i]);	/*  get gain number */
1351		us_TmpValue |= ((gain & 0x03) << 4);	/* <<4 for G0 and G1 bit in RAM */
1352		us_TmpValue |= i << 8;	/* To select the RAM LOCATION.... */
1353		outw(us_TmpValue, dev->iobase + APCI3120_SEQ_RAM_ADDRESS);
1354
1355		printk("\n Gain = %i",
1356			(((unsigned char)CR_RANGE(chanlist[i]) & 0x03) << 2));
1357		printk("\n Channel = %i", CR_CHAN(chanlist[i]));
1358		printk("\n Polarity = %i", us_TmpValue & APCI3120_UNIPOLAR);
1359	}
1360	return 1;		/*  we can serve this with scan logic */
1361}
1362
1363/*
1364+----------------------------------------------------------------------------+
1365| Function name     :	int i_APCI3120_ExttrigEnable(struct comedi_device * dev)    |
1366|                                        									 |
1367|                                            						         |
1368+----------------------------------------------------------------------------+
1369| Task              : 	Enable the external trigger						     |
1370|                     										                 |
1371+----------------------------------------------------------------------------+
1372| Input Parameters  : 	struct comedi_device * dev									 |
1373|                     														 |
1374|                                                 					         |
1375+----------------------------------------------------------------------------+
1376| Return Value      :      0        					                         |
1377|                    													     |
1378+----------------------------------------------------------------------------+
1379*/
1380
1381int i_APCI3120_ExttrigEnable(struct comedi_device *dev)
1382{
1383
1384	devpriv->us_OutputRegister |= APCI3120_ENABLE_EXT_TRIGGER;
1385	outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS);
1386	return 0;
1387}
1388
1389/*
1390+----------------------------------------------------------------------------+
1391| Function name     : 	int i_APCI3120_ExttrigDisable(struct comedi_device * dev)   |
1392|                                        									 |
1393+----------------------------------------------------------------------------+
1394| Task              : 	Disables the external trigger					     |
1395|                     										                 |
1396+----------------------------------------------------------------------------+
1397| Input Parameters  : 	struct comedi_device * dev									 |
1398|                     														 |
1399|                                                 					         |
1400+----------------------------------------------------------------------------+
1401| Return Value      :    0          					                         |
1402|                    													     |
1403+----------------------------------------------------------------------------+
1404*/
1405
1406int i_APCI3120_ExttrigDisable(struct comedi_device *dev)
1407{
1408	devpriv->us_OutputRegister &= ~APCI3120_ENABLE_EXT_TRIGGER;
1409	outw(devpriv->us_OutputRegister, dev->iobase + APCI3120_WR_ADDRESS);
1410	return 0;
1411}
1412
1413/*
1414+----------------------------------------------------------------------------+
1415|                    intERRUPT FUNCTIONS		    		                 |
1416+----------------------------------------------------------------------------+
1417*/
1418
1419/*
1420+----------------------------------------------------------------------------+
1421| Function name     : void v_APCI3120_Interrupt(int irq, void *d) 								 |
1422|                                        									 |
1423|                                            						         |
1424+----------------------------------------------------------------------------+
1425| Task              :Interrupt handler for APCI3120                        	 |
1426|			 When interrupt occurs this gets called.                 |
1427|			 First it finds which interrupt has been generated and   |
1428|			 handles  corresponding interrupt                        |
1429|                     										                 |
1430+----------------------------------------------------------------------------+
1431| Input Parameters  : 	int irq 											 |
1432|                        void *d											 |
1433|                                                 					         |
1434+----------------------------------------------------------------------------+
1435| Return Value      : void         					                         |
1436|                    													     |
1437+----------------------------------------------------------------------------+
1438*/
1439
1440void v_APCI3120_Interrupt(int irq, void *d)
1441{
1442	struct comedi_device *dev = d;
1443	unsigned short int_daq;
1444
1445	unsigned int int_amcc, ui_Check, i;
1446	unsigned short us_TmpValue;
1447	unsigned char b_DummyRead;
1448
1449	struct comedi_subdevice *s = dev->subdevices + 0;
1450	ui_Check = 1;
1451
1452	int_daq = inw(dev->iobase + APCI3120_RD_STATUS) & 0xf000;	/*  get IRQ reasons */
1453	int_amcc = inl(devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR);	/*  get AMCC int register */
1454
1455	if ((!int_daq) && (!(int_amcc & ANY_S593X_INT))) {
1456		comedi_error(dev, "IRQ from unknown source");
1457		return;
1458	}
1459
1460	outl(int_amcc | 0x00ff0000, devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR);	/*  shutdown IRQ reasons in AMCC */
1461
1462	int_daq = (int_daq >> 12) & 0xF;
1463
1464	if (devpriv->b_ExttrigEnable == APCI3120_ENABLE) {
1465		/* Disable ext trigger */
1466		i_APCI3120_ExttrigDisable(dev);
1467		devpriv->b_ExttrigEnable = APCI3120_DISABLE;
1468	}
1469	/* clear the timer 2 interrupt */
1470	inb(devpriv->i_IobaseAmcc + APCI3120_TIMER_STATUS_REGISTER);
1471
1472	if (int_amcc & MASTER_ABORT_INT)
1473		comedi_error(dev, "AMCC IRQ - MASTER DMA ABORT!");
1474	if (int_amcc & TARGET_ABORT_INT)
1475		comedi_error(dev, "AMCC IRQ - TARGET DMA ABORT!");
1476
1477	/*  Ckeck if EOC interrupt */
1478	if (((int_daq & 0x8) == 0)
1479		&& (devpriv->b_InterruptMode == APCI3120_EOC_MODE)) {
1480		if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) {
1481
1482			/*  Read the AI Value */
1483
1484			devpriv->ui_AiReadData[0] =
1485				(unsigned int) inw(devpriv->iobase + 0);
1486			devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
1487			send_sig(SIGIO, devpriv->tsk_Current, 0);	/*  send signal to the sample */
1488		} else {
1489			/* Disable EOC Interrupt */
1490			devpriv->b_ModeSelectRegister =
1491				devpriv->
1492				b_ModeSelectRegister & APCI3120_DISABLE_EOC_INT;
1493			outb(devpriv->b_ModeSelectRegister,
1494				devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
1495
1496		}
1497	}
1498
1499	/*  Check If EOS interrupt */
1500	if ((int_daq & 0x2) && (devpriv->b_InterruptMode == APCI3120_EOS_MODE)) {
1501
1502		if (devpriv->b_EocEosInterrupt == APCI3120_ENABLE) {	/*  enable this in without DMA ??? */
1503
1504			if (devpriv->b_AiCyclicAcquisition == APCI3120_ENABLE) {
1505				ui_Check = 0;
1506				i_APCI3120_InterruptHandleEos(dev);
1507				devpriv->ui_AiActualScan++;
1508				devpriv->b_ModeSelectRegister =
1509					devpriv->
1510					b_ModeSelectRegister |
1511					APCI3120_ENABLE_EOS_INT;
1512				outb(devpriv->b_ModeSelectRegister,
1513					dev->iobase +
1514					APCI3120_WRITE_MODE_SELECT);
1515			} else {
1516				ui_Check = 0;
1517				for (i = 0; i < devpriv->ui_AiNbrofChannels;
1518					i++) {
1519					us_TmpValue = inw(devpriv->iobase + 0);
1520					devpriv->ui_AiReadData[i] =
1521						(unsigned int) us_TmpValue;
1522				}
1523				devpriv->b_EocEosInterrupt = APCI3120_DISABLE;
1524				devpriv->b_InterruptMode = APCI3120_EOC_MODE;
1525
1526				send_sig(SIGIO, devpriv->tsk_Current, 0);	/*  send signal to the sample */
1527
1528			}
1529
1530		} else {
1531			devpriv->b_ModeSelectRegister =
1532				devpriv->
1533				b_ModeSelectRegister & APCI3120_DISABLE_EOS_INT;
1534			outb(devpriv->b_ModeSelectRegister,
1535				dev->iobase + APCI3120_WRITE_MODE_SELECT);
1536			devpriv->b_EocEosInterrupt = APCI3120_DISABLE;	/* Default settings */
1537			devpriv->b_InterruptMode = APCI3120_EOC_MODE;
1538		}
1539
1540	}
1541	/* Timer2 interrupt */
1542	if (int_daq & 0x1) {
1543
1544		switch (devpriv->b_Timer2Mode) {
1545		case APCI3120_COUNTER:
1546
1547			devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE;
1548			devpriv->b_ModeSelectRegister =
1549				devpriv->
1550				b_ModeSelectRegister & APCI3120_DISABLE_EOS_INT;
1551			outb(devpriv->b_ModeSelectRegister,
1552				dev->iobase + APCI3120_WRITE_MODE_SELECT);
1553
1554			/*  stop timer 2 */
1555			devpriv->us_OutputRegister =
1556				devpriv->
1557				us_OutputRegister & APCI3120_DISABLE_ALL_TIMER;
1558			outw(devpriv->us_OutputRegister,
1559				dev->iobase + APCI3120_WR_ADDRESS);
1560
1561			/* stop timer 0 and timer 1 */
1562			i_APCI3120_StopCyclicAcquisition(dev, s);
1563			devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE;
1564
1565			/* UPDATE-0.7.57->0.7.68comedi_done(dev,s); */
1566			s->async->events |= COMEDI_CB_EOA;
1567			comedi_event(dev, s);
1568
1569			break;
1570
1571		case APCI3120_TIMER:
1572
1573			/* Send a signal to from kernel to user space */
1574			send_sig(SIGIO, devpriv->tsk_Current, 0);
1575			break;
1576
1577		case APCI3120_WATCHDOG:
1578
1579			/* Send a signal to from kernel to user space */
1580			send_sig(SIGIO, devpriv->tsk_Current, 0);
1581			break;
1582
1583		default:
1584
1585			/*  disable Timer Interrupt */
1586
1587			devpriv->b_ModeSelectRegister =
1588				devpriv->
1589				b_ModeSelectRegister &
1590				APCI3120_DISABLE_TIMER_INT;
1591
1592			outb(devpriv->b_ModeSelectRegister,
1593				dev->iobase + APCI3120_WRITE_MODE_SELECT);
1594
1595		}
1596
1597		b_DummyRead = inb(dev->iobase + APCI3120_TIMER_STATUS_REGISTER);
1598
1599	}
1600
1601	if ((int_daq & 0x4) && (devpriv->b_InterruptMode == APCI3120_DMA_MODE)) {
1602		if (devpriv->b_AiCyclicAcquisition == APCI3120_ENABLE) {
1603
1604			/****************************/
1605			/* Clear Timer Write TC int */
1606			/****************************/
1607
1608			outl(APCI3120_CLEAR_WRITE_TC_INT,
1609				devpriv->i_IobaseAmcc +
1610				APCI3120_AMCC_OP_REG_INTCSR);
1611
1612			/************************************/
1613			/* Clears the timer status register */
1614			/************************************/
1615			inw(dev->iobase + APCI3120_TIMER_STATUS_REGISTER);
1616			v_APCI3120_InterruptDma(irq, d);	/*  do some data transfer */
1617		} else {
1618			/* Stops the Timer */
1619			outw(devpriv->
1620				us_OutputRegister & APCI3120_DISABLE_TIMER0 &
1621				APCI3120_DISABLE_TIMER1,
1622				dev->iobase + APCI3120_WR_ADDRESS);
1623		}
1624
1625	}
1626
1627	return;
1628}
1629
1630/*
1631+----------------------------------------------------------------------------+
1632| Function name     :int i_APCI3120_InterruptHandleEos(struct comedi_device *dev)   |
1633|                                        									 |
1634|                                            						         |
1635+----------------------------------------------------------------------------+
1636| Task              : This function handles EOS interrupt.                   |
1637|                     This function copies the acquired data(from FIFO)      |
1638|				to Comedi buffer.		 							 |
1639|                     										                 |
1640+----------------------------------------------------------------------------+
1641| Input Parameters  : struct comedi_device *dev									 |
1642|                     														 |
1643|                                                 					         |
1644+----------------------------------------------------------------------------+
1645| Return Value      : 0            					                         |
1646|                    													     |
1647+----------------------------------------------------------------------------+
1648*/
1649
1650
1651int i_APCI3120_InterruptHandleEos(struct comedi_device *dev)
1652{
1653	int n_chan, i;
1654	struct comedi_subdevice *s = dev->subdevices + 0;
1655	int err = 1;
1656
1657	n_chan = devpriv->ui_AiNbrofChannels;
1658
1659	s->async->events = 0;
1660
1661	for (i = 0; i < n_chan; i++)
1662		err &= comedi_buf_put(s->async, inw(dev->iobase + 0));
1663
1664	s->async->events |= COMEDI_CB_EOS;
1665
1666	if (err == 0)
1667		s->async->events |= COMEDI_CB_OVERFLOW;
1668
1669	comedi_event(dev, s);
1670
1671	return 0;
1672}
1673
1674/*
1675+----------------------------------------------------------------------------+
1676| Function name     : void v_APCI3120_InterruptDma(int irq, void *d) 									 |
1677|                                        									 |
1678+----------------------------------------------------------------------------+
1679| Task              : This is a handler for the DMA interrupt                |
1680|			  This function copies the data to Comedi Buffer.        |
1681|			  For continuous DMA it reinitializes the DMA operation. |
1682|			  For single mode DMA it stop the acquisition.           |
1683|													     			 |
1684+----------------------------------------------------------------------------+
1685| Input Parameters  : int irq, void *d				 |
1686|                     														 |
1687+----------------------------------------------------------------------------+
1688| Return Value      :  void        					                         |
1689|                    													     |
1690+----------------------------------------------------------------------------+
1691*/
1692
1693void v_APCI3120_InterruptDma(int irq, void *d)
1694{
1695	struct comedi_device *dev = d;
1696	struct comedi_subdevice *s = dev->subdevices + 0;
1697	unsigned int next_dma_buf, samplesinbuf;
1698	unsigned long low_word, high_word, var;
1699
1700	unsigned int ui_Tmp;
1701	samplesinbuf =
1702		devpriv->ui_DmaBufferUsesize[devpriv->ui_DmaActualBuffer] -
1703		inl(devpriv->i_IobaseAmcc + AMCC_OP_REG_MWTC);
1704
1705	if (samplesinbuf <
1706		devpriv->ui_DmaBufferUsesize[devpriv->ui_DmaActualBuffer]) {
1707		comedi_error(dev, "Interrupted DMA transfer!");
1708	}
1709	if (samplesinbuf & 1) {
1710		comedi_error(dev, "Odd count of bytes in DMA ring!");
1711		i_APCI3120_StopCyclicAcquisition(dev, s);
1712		devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE;
1713
1714		return;
1715	}
1716	samplesinbuf = samplesinbuf >> 1;	/*  number of received samples */
1717	if (devpriv->b_DmaDoubleBuffer) {
1718		/*  switch DMA buffers if is used double buffering */
1719		next_dma_buf = 1 - devpriv->ui_DmaActualBuffer;
1720
1721		ui_Tmp = AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO;
1722		outl(ui_Tmp, devpriv->i_IobaseAddon + AMCC_OP_REG_AGCSTS);
1723
1724		/*  changed  since 16 bit interface for add on */
1725		outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0);
1726		outw(APCI3120_ENABLE_TRANSFER_ADD_ON_LOW,
1727			devpriv->i_IobaseAddon + 2);
1728		outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0);
1729		outw(APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH, devpriv->i_IobaseAddon + 2);	/*  0x1000 is out putted in windows driver */
1730
1731		var = devpriv->ul_DmaBufferHw[next_dma_buf];
1732		low_word = var & 0xffff;
1733		var = devpriv->ul_DmaBufferHw[next_dma_buf];
1734		high_word = var / 65536;
1735
1736		/* DMA Start Address Low */
1737		outw(APCI3120_ADD_ON_MWAR_LOW, devpriv->i_IobaseAddon + 0);
1738		outw(low_word, devpriv->i_IobaseAddon + 2);
1739
1740		/* DMA Start Address High */
1741		outw(APCI3120_ADD_ON_MWAR_HIGH, devpriv->i_IobaseAddon + 0);
1742		outw(high_word, devpriv->i_IobaseAddon + 2);
1743
1744		var = devpriv->ui_DmaBufferUsesize[next_dma_buf];
1745		low_word = var & 0xffff;
1746		var = devpriv->ui_DmaBufferUsesize[next_dma_buf];
1747		high_word = var / 65536;
1748
1749		/* Nbr of acquisition LOW */
1750		outw(APCI3120_ADD_ON_MWTC_LOW, devpriv->i_IobaseAddon + 0);
1751		outw(low_word, devpriv->i_IobaseAddon + 2);
1752
1753		/* Nbr of acquisition HIGH */
1754		outw(APCI3120_ADD_ON_MWTC_HIGH, devpriv->i_IobaseAddon + 0);
1755		outw(high_word, devpriv->i_IobaseAddon + 2);
1756
1757/*
1758 * To configure A2P FIFO
1759 * ENABLE A2P FIFO WRITE AND ENABLE AMWEN
1760 * AMWEN_ENABLE | A2P_FIFO_WRITE_ENABLE (0x01|0x02)=0x03
1761 */
1762		outw(3, devpriv->i_IobaseAddon + 4);
1763		/* initialise end of dma interrupt  AINT_WRITE_COMPL = ENABLE_WRITE_TC_INT(ADDI) */
1764		outl((APCI3120_FIFO_ADVANCE_ON_BYTE_2 |
1765				APCI3120_ENABLE_WRITE_TC_INT),
1766			devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR);
1767
1768	}
1769	if (samplesinbuf) {
1770		v_APCI3120_InterruptDmaMoveBlock16bit(dev, s,
1771			devpriv->ul_DmaBufferVirtual[devpriv->
1772				ui_DmaActualBuffer], samplesinbuf);
1773
1774		if (!(devpriv->ui_AiFlags & TRIG_WAKE_EOS)) {
1775			s->async->events |= COMEDI_CB_EOS;
1776			comedi_event(dev, s);
1777		}
1778	}
1779	if (!devpriv->b_AiContinuous)
1780		if (devpriv->ui_AiActualScan >= devpriv->ui_AiNbrofScans) {
1781			/*  all data sampled */
1782			i_APCI3120_StopCyclicAcquisition(dev, s);
1783			devpriv->b_AiCyclicAcquisition = APCI3120_DISABLE;
1784			s->async->events |= COMEDI_CB_EOA;
1785			comedi_event(dev, s);
1786			return;
1787		}
1788
1789	if (devpriv->b_DmaDoubleBuffer) {	/*  switch dma buffers */
1790		devpriv->ui_DmaActualBuffer = 1 - devpriv->ui_DmaActualBuffer;
1791	} else {
1792/*
1793 * restart DMA if is not used double buffering
1794 * ADDED REINITIALISE THE DMA
1795 */
1796		ui_Tmp = AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO;
1797		outl(ui_Tmp, devpriv->i_IobaseAddon + AMCC_OP_REG_AGCSTS);
1798
1799		/*  changed  since 16 bit interface for add on */
1800		outw(APCI3120_ADD_ON_AGCSTS_LOW, devpriv->i_IobaseAddon + 0);
1801		outw(APCI3120_ENABLE_TRANSFER_ADD_ON_LOW,
1802			devpriv->i_IobaseAddon + 2);
1803		outw(APCI3120_ADD_ON_AGCSTS_HIGH, devpriv->i_IobaseAddon + 0);
1804		outw(APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH, devpriv->i_IobaseAddon + 2);	/*  */
1805/*
1806 * A2P FIFO MANAGEMENT
1807 * A2P fifo reset & transfer control enable
1808 */
1809		outl(APCI3120_A2P_FIFO_MANAGEMENT,
1810			devpriv->i_IobaseAmcc + AMCC_OP_REG_MCSR);
1811
1812		var = devpriv->ul_DmaBufferHw[0];
1813		low_word = var & 0xffff;
1814		var = devpriv->ul_DmaBufferHw[0];
1815		high_word = var / 65536;
1816		outw(APCI3120_ADD_ON_MWAR_LOW, devpriv->i_IobaseAddon + 0);
1817		outw(low_word, devpriv->i_IobaseAddon + 2);
1818		outw(APCI3120_ADD_ON_MWAR_HIGH, devpriv->i_IobaseAddon + 0);
1819		outw(high_word, devpriv->i_IobaseAddon + 2);
1820
1821		var = devpriv->ui_DmaBufferUsesize[0];
1822		low_word = var & 0xffff;	/* changed */
1823		var = devpriv->ui_DmaBufferUsesize[0];
1824		high_word = var / 65536;
1825		outw(APCI3120_ADD_ON_MWTC_LOW, devpriv->i_IobaseAddon + 0);
1826		outw(low_word, devpriv->i_IobaseAddon + 2);
1827		outw(APCI3120_ADD_ON_MWTC_HIGH, devpriv->i_IobaseAddon + 0);
1828		outw(high_word, devpriv->i_IobaseAddon + 2);
1829
1830/*
1831 * To configure A2P FIFO
1832 * ENABLE A2P FIFO WRITE AND ENABLE AMWEN
1833 * AMWEN_ENABLE | A2P_FIFO_WRITE_ENABLE (0x01|0x02)=0x03
1834 */
1835		outw(3, devpriv->i_IobaseAddon + 4);
1836		/* initialise end of dma interrupt  AINT_WRITE_COMPL = ENABLE_WRITE_TC_INT(ADDI) */
1837		outl((APCI3120_FIFO_ADVANCE_ON_BYTE_2 |
1838				APCI3120_ENABLE_WRITE_TC_INT),
1839			devpriv->i_IobaseAmcc + AMCC_OP_REG_INTCSR);
1840	}
1841}
1842
1843/*
1844+----------------------------------------------------------------------------+
1845| Function name     :void v_APCI3120_InterruptDmaMoveBlock16bit(comedi_device|
1846|*dev,struct comedi_subdevice *s,short *dma,short *data,int n)				     |
1847|                                        									 |
1848+----------------------------------------------------------------------------+
1849| Task              : This function copies the data from DMA buffer to the   |
1850|				 Comedi buffer  									 |
1851|                     										                 |
1852+----------------------------------------------------------------------------+
1853| Input Parameters  : struct comedi_device *dev									 |
1854|                     struct comedi_subdevice *s									 |
1855|                     short *dma											 |
1856|                     short *data,int n          					         |
1857+----------------------------------------------------------------------------+
1858| Return Value      : void         					                         |
1859|                    													     |
1860+----------------------------------------------------------------------------+
1861*/
1862
1863void v_APCI3120_InterruptDmaMoveBlock16bit(struct comedi_device *dev,
1864	struct comedi_subdevice *s, short *dma_buffer, unsigned int num_samples)
1865{
1866	devpriv->ui_AiActualScan +=
1867		(s->async->cur_chan + num_samples) / devpriv->ui_AiScanLength;
1868	s->async->cur_chan += num_samples;
1869	s->async->cur_chan %= devpriv->ui_AiScanLength;
1870
1871	cfc_write_array_to_buffer(s, dma_buffer, num_samples * sizeof(short));
1872}
1873
1874/*
1875+----------------------------------------------------------------------------+
1876|                           TIMER SUBDEVICE   		                         |
1877+----------------------------------------------------------------------------+
1878*/
1879
1880/*
1881+----------------------------------------------------------------------------+
1882| Function name     :int i_APCI3120_InsnConfigTimer(struct comedi_device *dev,          |
1883|	struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data) 			     |
1884|                                        									 |
1885+----------------------------------------------------------------------------+
1886| Task              :Configure Timer 2  								     |
1887|                     										                 |
1888+----------------------------------------------------------------------------+
1889| Input Parameters  : struct comedi_device *dev									 |
1890|                     struct comedi_subdevice *s									 |
1891|                     struct comedi_insn *insn                                      |
1892|                     unsigned int *data 										 |
1893|                     														 |
1894|                      data[0]= TIMER  configure as timer                    |
1895|              				 = WATCHDOG configure as watchdog				 |
1896|       			  data[1] = Timer constant							 |
1897|       			  data[2] = Timer2 interrupt (1)enable or(0) disable |
1898|                                                 					         |
1899+----------------------------------------------------------------------------+
1900| Return Value      :              					                         |
1901|                    													     |
1902+----------------------------------------------------------------------------+
1903*/
1904
1905int i_APCI3120_InsnConfigTimer(struct comedi_device *dev, struct comedi_subdevice *s,
1906	struct comedi_insn *insn, unsigned int *data)
1907{
1908
1909	unsigned int ui_Timervalue2;
1910	unsigned short us_TmpValue;
1911	unsigned char b_Tmp;
1912
1913	if (!data[1])
1914		comedi_error(dev, "config:No timer constant !");
1915
1916	devpriv->b_Timer2Interrupt = (unsigned char) data[2];	/*  save info whether to enable or disable interrupt */
1917
1918	ui_Timervalue2 = data[1] / 1000;	/*  convert nano seconds  to u seconds */
1919
1920	/* this_board->i_hwdrv_InsnConfigTimer(dev, ui_Timervalue2,(unsigned char)data[0]); */
1921	us_TmpValue = (unsigned short) inw(devpriv->iobase + APCI3120_RD_STATUS);
1922
1923/*
1924 * EL250804: Testing if board APCI3120 have the new Quartz or if it
1925 * is an APCI3001 and calculate the time value to set in the timer
1926 */
1927	if ((us_TmpValue & 0x00B0) == 0x00B0
1928		|| !strcmp(this_board->pc_DriverName, "apci3001")) {
1929		/* Calculate the time value to set in the timer */
1930		ui_Timervalue2 = ui_Timervalue2 / 50;
1931	} else {
1932		/* Calculate the time value to set in the timer */
1933		ui_Timervalue2 = ui_Timervalue2 / 70;
1934	}
1935
1936	/* Reset gate 2 of Timer 2 to disable it (Set Bit D14 to 0) */
1937	devpriv->us_OutputRegister =
1938		devpriv->us_OutputRegister & APCI3120_DISABLE_TIMER2;
1939	outw(devpriv->us_OutputRegister, devpriv->iobase + APCI3120_WR_ADDRESS);
1940
1941	/*  Disable TIMER Interrupt */
1942	devpriv->b_ModeSelectRegister =
1943		devpriv->
1944		b_ModeSelectRegister & APCI3120_DISABLE_TIMER_INT & 0xEF;
1945
1946	/*  Disable Eoc and Eos Interrupts */
1947	devpriv->b_ModeSelectRegister =
1948		devpriv->
1949		b_ModeSelectRegister & APCI3120_DISABLE_EOC_INT &
1950		APCI3120_DISABLE_EOS_INT;
1951	outb(devpriv->b_ModeSelectRegister,
1952		devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
1953	if (data[0] == APCI3120_TIMER) {	/* initialize timer */
1954		/* devpriv->b_ModeSelectRegister=devpriv->b_ModeSelectRegister |
1955		 * APCI3120_ENABLE_TIMER_INT; */
1956
1957		/* outb(devpriv->b_ModeSelectRegister,devpriv->iobase+APCI3120_WRITE_MODE_SELECT); */
1958
1959		/* Set the Timer 2 in mode 2(Timer) */
1960		devpriv->b_TimerSelectMode =
1961			(devpriv->
1962			b_TimerSelectMode & 0x0F) | APCI3120_TIMER_2_MODE_2;
1963		outb(devpriv->b_TimerSelectMode,
1964			devpriv->iobase + APCI3120_TIMER_CRT1);
1965
1966/*
1967 * Configure the timer 2 for writing the LOW unsigned short of timer
1968 * is Delay value You must make a b_tmp variable with
1969 * DigitalOutPutRegister because at Address_1+APCI3120_TIMER_CRT0
1970 * you can set the digital output and configure the timer 2,and if
1971 * you don't make this, digital output are erase (Set to 0)
1972 */
1973
1974		/* Writing LOW unsigned short */
1975		b_Tmp = ((devpriv->
1976				b_DigitalOutputRegister) & 0xF0) |
1977			APCI3120_SELECT_TIMER_2_LOW_WORD;
1978		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
1979		outw(LOWORD(ui_Timervalue2),
1980			devpriv->iobase + APCI3120_TIMER_VALUE);
1981
1982		/* Writing HIGH unsigned short */
1983		b_Tmp = ((devpriv->
1984				b_DigitalOutputRegister) & 0xF0) |
1985			APCI3120_SELECT_TIMER_2_HIGH_WORD;
1986		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
1987		outw(HIWORD(ui_Timervalue2),
1988			devpriv->iobase + APCI3120_TIMER_VALUE);
1989		/*  timer2 in Timer mode enabled */
1990		devpriv->b_Timer2Mode = APCI3120_TIMER;
1991
1992	} else {			/*  Initialize Watch dog */
1993
1994		/* Set the Timer 2 in mode 5(Watchdog) */
1995
1996		devpriv->b_TimerSelectMode =
1997			(devpriv->
1998			b_TimerSelectMode & 0x0F) | APCI3120_TIMER_2_MODE_5;
1999		outb(devpriv->b_TimerSelectMode,
2000			devpriv->iobase + APCI3120_TIMER_CRT1);
2001
2002/*
2003 * Configure the timer 2 for writing the LOW unsigned short of timer
2004 * is Delay value You must make a b_tmp variable with
2005 * DigitalOutPutRegister because at Address_1+APCI3120_TIMER_CRT0
2006 * you can set the digital output and configure the timer 2,and if
2007 * you don't make this, digital output are erase (Set to 0)
2008 */
2009
2010		/* Writing LOW unsigned short */
2011		b_Tmp = ((devpriv->
2012				b_DigitalOutputRegister) & 0xF0) |
2013			APCI3120_SELECT_TIMER_2_LOW_WORD;
2014		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
2015		outw(LOWORD(ui_Timervalue2),
2016			devpriv->iobase + APCI3120_TIMER_VALUE);
2017
2018		/* Writing HIGH unsigned short */
2019		b_Tmp = ((devpriv->
2020				b_DigitalOutputRegister) & 0xF0) |
2021			APCI3120_SELECT_TIMER_2_HIGH_WORD;
2022		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
2023
2024		outw(HIWORD(ui_Timervalue2),
2025			devpriv->iobase + APCI3120_TIMER_VALUE);
2026		/* watchdog enabled */
2027		devpriv->b_Timer2Mode = APCI3120_WATCHDOG;
2028
2029	}
2030
2031	return insn->n;
2032
2033}
2034
2035/*
2036+----------------------------------------------------------------------------+
2037| Function name     :int i_APCI3120_InsnWriteTimer(struct comedi_device *dev,           |
2038|                    struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data)  |
2039|                                            						         |
2040+----------------------------------------------------------------------------+
2041| Task              :    To start and stop the timer		                 |
2042+----------------------------------------------------------------------------+
2043| Input Parameters  : struct comedi_device *dev									 |
2044|                     struct comedi_subdevice *s									 |
2045|                     struct comedi_insn *insn                                      |
2046|                     unsigned int *data                                         |
2047|                                                                            |
2048|				data[0] = 1 (start)                                  |
2049|				data[0] = 0 (stop )                                  |
2050|	 			data[0] = 2  (write new value)                       |
2051|	   			data[1]= new value                                   |
2052|                                                                            |
2053|    				devpriv->b_Timer2Mode =  0 DISABLE                   |
2054|	                     					 1 Timer                     |
2055|					 					 2 Watch dog			     |
2056|                                                 					         |
2057+----------------------------------------------------------------------------+
2058| Return Value      :              					                         |
2059|                    													     |
2060+----------------------------------------------------------------------------+
2061*/
2062
2063int i_APCI3120_InsnWriteTimer(struct comedi_device *dev, struct comedi_subdevice *s,
2064	struct comedi_insn *insn, unsigned int *data)
2065{
2066
2067	unsigned int ui_Timervalue2 = 0;
2068	unsigned short us_TmpValue;
2069	unsigned char b_Tmp;
2070
2071	if ((devpriv->b_Timer2Mode != APCI3120_WATCHDOG)
2072		&& (devpriv->b_Timer2Mode != APCI3120_TIMER)) {
2073		comedi_error(dev, "\nwrite:timer2  not configured ");
2074		return -EINVAL;
2075	}
2076
2077	if (data[0] == 2) {	/*  write new value */
2078		if (devpriv->b_Timer2Mode != APCI3120_TIMER) {
2079			comedi_error(dev,
2080				"write :timer2  not configured  in TIMER MODE");
2081			return -EINVAL;
2082		}
2083
2084		if (data[1])
2085			ui_Timervalue2 = data[1];
2086		else
2087			ui_Timervalue2 = 0;
2088	}
2089
2090	/* this_board->i_hwdrv_InsnWriteTimer(dev,data[0],ui_Timervalue2); */
2091
2092	switch (data[0]) {
2093	case APCI3120_START:
2094
2095		/*  Reset FC_TIMER BIT */
2096		inb(devpriv->iobase + APCI3120_TIMER_STATUS_REGISTER);
2097		if (devpriv->b_Timer2Mode == APCI3120_TIMER) {	/* start timer */
2098			/* Enable Timer */
2099			devpriv->b_ModeSelectRegister =
2100				devpriv->b_ModeSelectRegister & 0x0B;
2101		} else {		/* start watch dog */
2102			/* Enable WatchDog */
2103			devpriv->b_ModeSelectRegister =
2104				(devpriv->
2105				b_ModeSelectRegister & 0x0B) |
2106				APCI3120_ENABLE_WATCHDOG;
2107		}
2108
2109		/* enable disable interrupt */
2110		if ((devpriv->b_Timer2Interrupt) == APCI3120_ENABLE) {
2111
2112			devpriv->b_ModeSelectRegister =
2113				devpriv->
2114				b_ModeSelectRegister |
2115				APCI3120_ENABLE_TIMER_INT;
2116			/*  save the task structure to pass info to user */
2117			devpriv->tsk_Current = current;
2118		} else {
2119
2120			devpriv->b_ModeSelectRegister =
2121				devpriv->
2122				b_ModeSelectRegister &
2123				APCI3120_DISABLE_TIMER_INT;
2124		}
2125		outb(devpriv->b_ModeSelectRegister,
2126			devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
2127
2128		if (devpriv->b_Timer2Mode == APCI3120_TIMER) {	/* start timer */
2129			/* For Timer mode is  Gate2 must be activated   **timer started */
2130			devpriv->us_OutputRegister =
2131				devpriv->
2132				us_OutputRegister | APCI3120_ENABLE_TIMER2;
2133			outw(devpriv->us_OutputRegister,
2134				devpriv->iobase + APCI3120_WR_ADDRESS);
2135		}
2136
2137		break;
2138
2139	case APCI3120_STOP:
2140		if (devpriv->b_Timer2Mode == APCI3120_TIMER) {
2141			/* Disable timer */
2142			devpriv->b_ModeSelectRegister =
2143				devpriv->
2144				b_ModeSelectRegister &
2145				APCI3120_DISABLE_TIMER_COUNTER;
2146		} else {
2147			/* Disable WatchDog */
2148			devpriv->b_ModeSelectRegister =
2149				devpriv->
2150				b_ModeSelectRegister &
2151				APCI3120_DISABLE_WATCHDOG;
2152		}
2153		/*  Disable timer interrupt */
2154		devpriv->b_ModeSelectRegister =
2155			devpriv->
2156			b_ModeSelectRegister & APCI3120_DISABLE_TIMER_INT;
2157
2158		/*  Write above states  to register */
2159		outb(devpriv->b_ModeSelectRegister,
2160			devpriv->iobase + APCI3120_WRITE_MODE_SELECT);
2161
2162		/*  Reset Gate 2 */
2163		devpriv->us_OutputRegister =
2164			devpriv->us_OutputRegister & APCI3120_DISABLE_TIMER_INT;
2165		outw(devpriv->us_OutputRegister,
2166			devpriv->iobase + APCI3120_WR_ADDRESS);
2167
2168		/*  Reset FC_TIMER BIT */
2169		inb(devpriv->iobase + APCI3120_TIMER_STATUS_REGISTER);
2170
2171		/* Disable timer */
2172		/* devpriv->b_Timer2Mode=APCI3120_DISABLE;  */
2173
2174		break;
2175
2176	case 2:		/* write new value to Timer */
2177		if (devpriv->b_Timer2Mode != APCI3120_TIMER) {
2178			comedi_error(dev,
2179				"write :timer2  not configured  in TIMER MODE");
2180			return -EINVAL;
2181		}
2182		/*  ui_Timervalue2=data[1]; // passed as argument */
2183		us_TmpValue =
2184			(unsigned short) inw(devpriv->iobase + APCI3120_RD_STATUS);
2185
2186/*
2187 * EL250804: Testing if board APCI3120 have the new Quartz or if it
2188 * is an APCI3001 and calculate the time value to set in the timer
2189 */
2190		if ((us_TmpValue & 0x00B0) == 0x00B0
2191			|| !strcmp(this_board->pc_DriverName, "apci3001")) {
2192			/* Calculate the time value to set in the timer */
2193			ui_Timervalue2 = ui_Timervalue2 / 50;
2194		} else {
2195			/* Calculate the time value to set in the timer */
2196			ui_Timervalue2 = ui_Timervalue2 / 70;
2197		}
2198		/* Writing LOW unsigned short */
2199		b_Tmp = ((devpriv->
2200				b_DigitalOutputRegister) & 0xF0) |
2201			APCI3120_SELECT_TIMER_2_LOW_WORD;
2202		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
2203
2204		outw(LOWORD(ui_Timervalue2),
2205			devpriv->iobase + APCI3120_TIMER_VALUE);
2206
2207		/* Writing HIGH unsigned short */
2208		b_Tmp = ((devpriv->
2209				b_DigitalOutputRegister) & 0xF0) |
2210			APCI3120_SELECT_TIMER_2_HIGH_WORD;
2211		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
2212
2213		outw(HIWORD(ui_Timervalue2),
2214			devpriv->iobase + APCI3120_TIMER_VALUE);
2215
2216		break;
2217	default:
2218		return -EINVAL;	/*  Not a valid input */
2219	}
2220
2221	return insn->n;
2222}
2223
2224/*
2225+----------------------------------------------------------------------------+
2226| Function name     : int i_APCI3120_InsnReadTimer(struct comedi_device *dev,           |
2227|		struct comedi_subdevice *s,struct comedi_insn *insn, unsigned int *data) 		 |
2228|                                        									 |
2229|                                            						         |
2230+----------------------------------------------------------------------------+
2231| Task              : read the Timer value 				                 	 |
2232+----------------------------------------------------------------------------+
2233| Input Parameters  : 	struct comedi_device *dev									 |
2234|                     struct comedi_subdevice *s									 |
2235|                     struct comedi_insn *insn                                      |
2236|                     unsigned int *data 										 |
2237|                     														 |
2238+----------------------------------------------------------------------------+
2239| Return Value      :   													 |
2240|			for Timer:	data[0]= Timer constant						 |
2241|																	 |
2242|         		for watchdog: data[0]=0 (still running)                  |
2243|	               			  data[0]=1  (run down)            			 |
2244|                    													     |
2245+----------------------------------------------------------------------------+
2246*/
2247int i_APCI3120_InsnReadTimer(struct comedi_device *dev, struct comedi_subdevice *s,
2248	struct comedi_insn *insn, unsigned int *data)
2249{
2250	unsigned char b_Tmp;
2251	unsigned short us_TmpValue, us_TmpValue_2, us_StatusValue;
2252
2253	if ((devpriv->b_Timer2Mode != APCI3120_WATCHDOG)
2254		&& (devpriv->b_Timer2Mode != APCI3120_TIMER)) {
2255		comedi_error(dev, "\nread:timer2  not configured ");
2256	}
2257
2258	/* this_board->i_hwdrv_InsnReadTimer(dev,data); */
2259	if (devpriv->b_Timer2Mode == APCI3120_TIMER) {
2260
2261		/* Read the LOW unsigned short of Timer 2 register */
2262		b_Tmp = ((devpriv->
2263				b_DigitalOutputRegister) & 0xF0) |
2264			APCI3120_SELECT_TIMER_2_LOW_WORD;
2265		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
2266
2267		us_TmpValue = inw(devpriv->iobase + APCI3120_TIMER_VALUE);
2268
2269		/* Read the HIGH unsigned short of Timer 2 register */
2270		b_Tmp = ((devpriv->
2271				b_DigitalOutputRegister) & 0xF0) |
2272			APCI3120_SELECT_TIMER_2_HIGH_WORD;
2273		outb(b_Tmp, devpriv->iobase + APCI3120_TIMER_CRT0);
2274
2275		us_TmpValue_2 = inw(devpriv->iobase + APCI3120_TIMER_VALUE);
2276
2277		/*  combining both words */
2278		data[0] = (unsigned int) ((us_TmpValue) | ((us_TmpValue_2) << 16));
2279
2280	} else {			/*  Read watch dog status */
2281
2282		us_StatusValue = inw(devpriv->iobase + APCI3120_RD_STATUS);
2283		us_StatusValue =
2284			((us_StatusValue & APCI3120_FC_TIMER) >> 12) & 1;
2285		if (us_StatusValue == 1) {
2286			/*  RESET FC_TIMER BIT */
2287			inb(devpriv->iobase + APCI3120_TIMER_STATUS_REGISTER);
2288		}
2289		data[0] = us_StatusValue;	/*  when data[0] = 1 then the watch dog has rundown */
2290	}
2291	return insn->n;
2292}
2293
2294/*
2295+----------------------------------------------------------------------------+
2296|                           DIGITAL INPUT SUBDEVICE   		                 |
2297+----------------------------------------------------------------------------+
2298*/
2299
2300/*
2301+----------------------------------------------------------------------------+
2302| Function name     :int i_APCI3120_InsnReadDigitalInput(struct comedi_device *dev,     |
2303|			struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data)   |
2304|                                        									 |
2305|                                            						         |
2306+----------------------------------------------------------------------------+
2307| Task              : Reads the value of the specified  Digital input channel|
2308|                     										                 |
2309+----------------------------------------------------------------------------+
2310| Input Parameters  : struct comedi_device *dev									 |
2311|                     struct comedi_subdevice *s									 |
2312|                     struct comedi_insn *insn                                      |
2313|                     unsigned int *data 										 |
2314+----------------------------------------------------------------------------+
2315| Return Value      :              					                         |
2316|                    													     |
2317+----------------------------------------------------------------------------+
2318*/
2319
2320int i_APCI3120_InsnReadDigitalInput(struct comedi_device *dev,
2321				    struct comedi_subdevice *s,
2322				    struct comedi_insn *insn,
2323				    unsigned int *data)
2324{
2325	unsigned int ui_Chan, ui_TmpValue;
2326
2327	ui_Chan = CR_CHAN(insn->chanspec);	/*  channel specified */
2328
2329	/* this_board->i_hwdrv_InsnReadDigitalInput(dev,ui_Chan,data); */
2330	if (ui_Chan <= 3) {
2331		ui_TmpValue = (unsigned int) inw(devpriv->iobase + APCI3120_RD_STATUS);
2332
2333/*
2334 * since only 1 channel reqd to bring it to last bit it is rotated 8
2335 * +(chan - 1) times then ANDed with 1 for last bit.
2336 */
2337		*data = (ui_TmpValue >> (ui_Chan + 8)) & 1;
2338		/* return 0; */
2339	} else {
2340		/*       comedi_error(dev," chan spec wrong"); */
2341		return -EINVAL;	/*  "sorry channel spec wrong " */
2342	}
2343	return insn->n;
2344
2345}
2346
2347/*
2348+----------------------------------------------------------------------------+
2349| Function name     :int i_APCI3120_InsnBitsDigitalInput(struct comedi_device *dev, |
2350|struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data)                      |
2351|                                        									 |
2352+----------------------------------------------------------------------------+
2353| Task              : Reads the value of the Digital input Port i.e.4channels|
2354|   value is returned in data[0]											 |
2355|                     										                 |
2356+----------------------------------------------------------------------------+
2357| Input Parameters  : struct comedi_device *dev									 |
2358|                     struct comedi_subdevice *s									 |
2359|                     struct comedi_insn *insn                                      |
2360|                     unsigned int *data 										 |
2361+----------------------------------------------------------------------------+
2362| Return Value      :              					                         |
2363|                    													     |
2364+----------------------------------------------------------------------------+
2365*/
2366int i_APCI3120_InsnBitsDigitalInput(struct comedi_device *dev, struct comedi_subdevice *s,
2367	struct comedi_insn *insn, unsigned int *data)
2368{
2369	unsigned int ui_TmpValue;
2370	ui_TmpValue = (unsigned int) inw(devpriv->iobase + APCI3120_RD_STATUS);
2371	/*****	state of 4 channels  in the 11, 10, 9, 8   bits of status reg
2372			rotated right 8 times to bring them to last four bits
2373			ANDed with oxf for  value.
2374	*****/
2375
2376	*data = (ui_TmpValue >> 8) & 0xf;
2377	/* this_board->i_hwdrv_InsnBitsDigitalInput(dev,data); */
2378	return insn->n;
2379}
2380
2381/*
2382+----------------------------------------------------------------------------+
2383|                           DIGITAL OUTPUT SUBDEVICE   		                 |
2384+----------------------------------------------------------------------------+
2385*/
2386/*
2387+----------------------------------------------------------------------------+
2388| Function name     :int i_APCI3120_InsnConfigDigitalOutput(struct comedi_device    |
2389| *dev,struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data)				 |
2390|                                            						         |
2391+----------------------------------------------------------------------------+
2392| Task              :Configure the output memory ON or OFF				     |
2393|                     										                 |
2394+----------------------------------------------------------------------------+
2395| Input Parameters  :struct comedi_device *dev									 	 |
2396|                     struct comedi_subdevice *s									 |
2397|                     struct comedi_insn *insn                                      |
2398|                     unsigned int *data 										 |
2399+----------------------------------------------------------------------------+
2400| Return Value      :              					                         |
2401|                    													     |
2402+----------------------------------------------------------------------------+
2403*/
2404
2405int i_APCI3120_InsnConfigDigitalOutput(struct comedi_device *dev,
2406	struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
2407{
2408
2409	if ((data[0] != 0) && (data[0] != 1)) {
2410		comedi_error(dev,
2411			"Not a valid Data !!! ,Data should be 1 or 0\n");
2412		return -EINVAL;
2413	}
2414	if (data[0]) {
2415		devpriv->b_OutputMemoryStatus = APCI3120_ENABLE;
2416
2417	} else {
2418		devpriv->b_OutputMemoryStatus = APCI3120_DISABLE;
2419		devpriv->b_DigitalOutputRegister = 0;
2420	}
2421	if (!devpriv->b_OutputMemoryStatus)
2422		ui_Temp = 0;
2423				/* if(!devpriv->b_OutputMemoryStatus ) */
2424
2425	return insn->n;
2426}
2427
2428/*
2429+----------------------------------------------------------------------------+
2430| Function name     :int i_APCI3120_InsnBitsDigitalOutput(struct comedi_device *dev,    |
2431|		struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data) 		 |
2432|                                        									 |
2433+----------------------------------------------------------------------------+
2434| Task              : write diatal output port							     |
2435|                     										                 |
2436+----------------------------------------------------------------------------+
2437| Input Parameters  : struct comedi_device *dev									 |
2438|                     struct comedi_subdevice *s									 |
2439|                     struct comedi_insn *insn                                      |
2440|                     unsigned int *data 										 |
2441|                      data[0]     Value to be written
2442|                      data[1]    :1 Set digital o/p ON
2443|                      data[1]     2 Set digital o/p OFF with memory ON
2444+----------------------------------------------------------------------------+
2445| Return Value      :              					                         |
2446|                    													     |
2447+----------------------------------------------------------------------------+
2448*/
2449
2450int i_APCI3120_InsnBitsDigitalOutput(struct comedi_device *dev,
2451				     struct comedi_subdevice *s,
2452				     struct comedi_insn *insn,
2453				     unsigned int *data)
2454{
2455	if ((data[0] > this_board->i_DoMaxdata) || (data[0] < 0)) {
2456
2457		comedi_error(dev, "Data is not valid !!! \n");
2458		return -EINVAL;
2459	}
2460
2461	switch (data[1]) {
2462	case 1:
2463		data[0] = (data[0] << 4) | devpriv->b_DigitalOutputRegister;
2464		break;
2465
2466	case 2:
2467		data[0] = data[0];
2468		break;
2469	default:
2470		printk("\nThe parameter passed is in error \n");
2471		return -EINVAL;
2472	}			/*  switch(data[1]) */
2473	outb(data[0], devpriv->iobase + APCI3120_DIGITAL_OUTPUT);
2474
2475	devpriv->b_DigitalOutputRegister = data[0] & 0xF0;
2476
2477	return insn->n;
2478
2479}
2480
2481/*
2482+----------------------------------------------------------------------------+
2483| Function name		:int i_APCI3120_InsnWriteDigitalOutput(struct comedi_device *dev,|
2484|struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data)	|
2485|										|
2486+----------------------------------------------------------------------------+
2487| Task			: Write digiatl output					|
2488| 										|
2489+----------------------------------------------------------------------------+
2490| Input Parameters	: struct comedi_device *dev				|
2491|			struct comedi_subdevice *s			 	|
2492|			struct comedi_insn *insn				|
2493|			unsigned int *data					|
2494			data[0]     Value to be written
2495			data[1]    :1 Set digital o/p ON
2496			data[1]     2 Set digital o/p OFF with memory ON
2497+----------------------------------------------------------------------------+
2498| Return Value		:							|
2499|										|
2500+----------------------------------------------------------------------------+
2501*/
2502
2503int i_APCI3120_InsnWriteDigitalOutput(struct comedi_device *dev,
2504				      struct comedi_subdevice *s,
2505				      struct comedi_insn *insn,
2506				      unsigned int *data)
2507{
2508
2509	unsigned int ui_Temp1;
2510
2511	unsigned int ui_NoOfChannel = CR_CHAN(insn->chanspec);	/*  get the channel */
2512
2513	if ((data[0] != 0) && (data[0] != 1)) {
2514		comedi_error(dev,
2515			"Not a valid Data !!! ,Data should be 1 or 0\n");
2516		return -EINVAL;
2517	}
2518	if (ui_NoOfChannel > this_board->i_NbrDoChannel - 1) {
2519		comedi_error(dev,
2520			"This board doesn't have specified channel !!! \n");
2521		return -EINVAL;
2522	}
2523
2524	switch (data[1]) {
2525	case 1:
2526		data[0] = (data[0] << ui_NoOfChannel);
2527/* ES05                   data[0]=(data[0]<<4)|ui_Temp; */
2528		data[0] = (data[0] << 4) | devpriv->b_DigitalOutputRegister;
2529		break;
2530
2531	case 2:
2532		data[0] = ~data[0] & 0x1;
2533		ui_Temp1 = 1;
2534		ui_Temp1 = ui_Temp1 << ui_NoOfChannel;
2535		ui_Temp1 = ui_Temp1 << 4;
2536/* ES05                   ui_Temp=ui_Temp|ui_Temp1; */
2537		devpriv->b_DigitalOutputRegister =
2538			devpriv->b_DigitalOutputRegister | ui_Temp1;
2539
2540		data[0] = (data[0] << ui_NoOfChannel) ^ 0xf;
2541		data[0] = data[0] << 4;
2542/* ES05                   data[0]=data[0]& ui_Temp; */
2543		data[0] = data[0] & devpriv->b_DigitalOutputRegister;
2544		break;
2545	default:
2546		printk("\nThe parameter passed is in error \n");
2547		return -EINVAL;
2548	}			/*  switch(data[1]) */
2549	outb(data[0], devpriv->iobase + APCI3120_DIGITAL_OUTPUT);
2550
2551/* ES05        ui_Temp=data[0] & 0xf0; */
2552	devpriv->b_DigitalOutputRegister = data[0] & 0xf0;
2553	return insn->n;
2554
2555}
2556
2557/*
2558+----------------------------------------------------------------------------+
2559|                            ANALOG OUTPUT SUBDEVICE                         |
2560+----------------------------------------------------------------------------+
2561*/
2562
2563/*
2564+----------------------------------------------------------------------------+
2565| Function name     :int i_APCI3120_InsnWriteAnalogOutput(struct comedi_device *dev,|
2566|struct comedi_subdevice *s, struct comedi_insn *insn,unsigned int *data)			             |
2567|                                        									 |
2568+----------------------------------------------------------------------------+
2569| Task              : Write  analog output   							     |
2570|                     										                 |
2571+----------------------------------------------------------------------------+
2572| Input Parameters  : struct comedi_device *dev									 |
2573|                     struct comedi_subdevice *s									 |
2574|                     struct comedi_insn *insn                                      |
2575|                     unsigned int *data  										 |
2576+----------------------------------------------------------------------------+
2577| Return Value      :              					                         |
2578|                    													     |
2579+----------------------------------------------------------------------------+
2580*/
2581
2582int i_APCI3120_InsnWriteAnalogOutput(struct comedi_device *dev,
2583				     struct comedi_subdevice *s,
2584				     struct comedi_insn *insn,
2585				     unsigned int *data)
2586{
2587	unsigned int ui_Range, ui_Channel;
2588	unsigned short us_TmpValue;
2589
2590	ui_Range = CR_RANGE(insn->chanspec);
2591	ui_Channel = CR_CHAN(insn->chanspec);
2592
2593	/* this_board->i_hwdrv_InsnWriteAnalogOutput(dev, ui_Range, ui_Channel,data[0]); */
2594	if (ui_Range) {		/*  if 1 then unipolar */
2595
2596		if (data[0] != 0)
2597			data[0] =
2598				((((ui_Channel & 0x03) << 14) & 0xC000) | (1 <<
2599					13) | (data[0] + 8191));
2600		else
2601			data[0] =
2602				((((ui_Channel & 0x03) << 14) & 0xC000) | (1 <<
2603					13) | 8192);
2604
2605	} else {			/*  if 0 then   bipolar */
2606		data[0] =
2607			((((ui_Channel & 0x03) << 14) & 0xC000) | (0 << 13) |
2608			data[0]);
2609
2610	}
2611
2612/*
2613 * out put n values at the given channel. printk("\nwaiting for
2614 * DA_READY BIT");
2615 */
2616	do {			/* Waiting of DA_READY BIT */
2617		us_TmpValue =
2618			((unsigned short) inw(devpriv->iobase +
2619				APCI3120_RD_STATUS)) & 0x0001;
2620	} while (us_TmpValue != 0x0001);
2621
2622	if (ui_Channel <= 3)
2623/*
2624 * for channel 0-3 out at the register 1 (wrDac1-8) data[i]
2625 * typecasted to ushort since word write is to be done
2626 */
2627		outw((unsigned short) data[0],
2628			devpriv->iobase + APCI3120_ANALOG_OUTPUT_1);
2629	else
2630/*
2631 * for channel 4-7 out at the register 2 (wrDac5-8) data[i]
2632 * typecasted to ushort since word write is to be done
2633 */
2634		outw((unsigned short) data[0],
2635			devpriv->iobase + APCI3120_ANALOG_OUTPUT_2);
2636
2637	return insn->n;
2638}
2639