hwdrv_apci035.c revision c995fe9475e062bab6f5a45ed28cd2d3d955ef43
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 shoud also find the complete GPL in the COPYING file accompanying this source code.
21
22@endverbatim
23*/
24/*
25
26  +-----------------------------------------------------------------------+
27  | (C) ADDI-DATA GmbH          Dieselstraße 3       D-77833 Ottersweier  |
28  +-----------------------------------------------------------------------+
29  | Tel : +49 (0) 7223/9493-0     | email    : info@addi-data.com         |
30  | Fax : +49 (0) 7223/9493-92    | Internet : http://www.addi-data.com   |
31  +-------------------------------+---------------------------------------+
32  | Project     : APCI-035        | Compiler   : GCC                      |
33  | Module name : hwdrv_apci035.c | Version    : 2.96                     |
34  +-------------------------------+---------------------------------------+
35  | Project manager: Eric Stolz   | Date       :  02/12/2002              |
36  +-------------------------------+---------------------------------------+
37  | Description :   Hardware Layer Acces For APCI-035                     |
38  +-----------------------------------------------------------------------+
39  |                             UPDATES                                   |
40  +----------+-----------+------------------------------------------------+
41  |   Date   |   Author  |          Description of updates                |
42  +----------+-----------+------------------------------------------------+
43  |          |           |                                                |
44  |          |           |                                                |
45  |          |           |                                                |
46  +----------+-----------+------------------------------------------------+
47*/
48
49/*
50+----------------------------------------------------------------------------+
51|                               Included files                               |
52+----------------------------------------------------------------------------+
53*/
54#include "hwdrv_apci035.h"
55INT i_WatchdogNbr = 0;
56INT i_Temp = 0;
57INT i_Flag = 1;
58/*
59+----------------------------------------------------------------------------+
60| Function   Name   : int i_APCI035_ConfigTimerWatchdog                      |
61|			  (comedi_device *dev,comedi_subdevice *s,               |
62|                      comedi_insn *insn,lsampl_t *data)                     |
63+----------------------------------------------------------------------------+
64| Task              : Configures The Timer , Counter or Watchdog             |
65+----------------------------------------------------------------------------+
66| Input Parameters  : comedi_device *dev : Driver handle                     |
67|                     UINT *data         : Data Pointer contains             |
68|                                          configuration parameters as below |
69|                                                                            |
70|					  data[0]            : 0 Configure As Timer      |
71|										   1 Configure As Watchdog   |
72                              data[1]            : Watchdog number
73|					  data[2]            : Time base Unit            |
74|					  data[3]			 : Reload Value			     |
75                              data[4]            : External Trigger          |
76                                                   1:Enable
77                                                   0:Disable
78                              data[5]            :External Trigger Level
79                                                  00 Trigger Disabled
80                                                  01 Trigger Enabled (Low level)
81                                                  10 Trigger Enabled (High Level)
82                                                  11 Trigger Enabled (High/Low level)
83                              data[6]            : External Gate            |
84                                                   1:Enable
85                                                   0:Disable
86                              data[7]            : External Gate level
87                                                  00 Gate Disabled
88                                                  01 Gate Enabled (Low level)
89                                                  10 Gate Enabled (High Level)
90                              data[8]            :Warning Relay
91                                                  1: ENABLE
92                                                  0: DISABLE
93                              data[9]            :Warning Delay available
94                              data[10]           :Warning Relay Time unit
95                              data[11]           :Warning Relay Time Reload value
96                              data[12]           :Reset Relay
97                                                  1 : ENABLE
98                                                  0 : DISABLE
99                              data[13]           :Interrupt
100                                                  1 : ENABLE
101                                                  0 : DISABLE
102
103|
104+----------------------------------------------------------------------------+
105| Output Parameters :	--													 |
106+----------------------------------------------------------------------------+
107| Return Value      : TRUE  : No error occur                                 |
108|		            : FALSE : Error occur. Return the error          |
109|			                                                         |
110+----------------------------------------------------------------------------+
111*/
112INT i_APCI035_ConfigTimerWatchdog(comedi_device * dev, comedi_subdevice * s,
113	comedi_insn * insn, lsampl_t * data)
114{
115	UINT ui_Status = 0;
116	UINT ui_Command = 0;
117	UINT ui_Mode = 0;
118	i_Temp = 0;
119	devpriv->tsk_Current = current;
120	devpriv->b_TimerSelectMode = data[0];
121	i_WatchdogNbr = data[1];
122	if (data[0] == 0) {
123		ui_Mode = 2;
124	} else {
125		ui_Mode = 0;
126	}
127//ui_Command = inl(devpriv->iobase+((i_WatchdogNbr-1)*32)+12);
128	ui_Command = 0;
129//ui_Command = ui_Command & 0xFFFFF9FEUL;
130	outl(ui_Command, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
131	ui_Command = 0;
132	ui_Command = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
133/************************/
134/* Set the reload value */
135/************************/
136	outl(data[3], devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 4);
137/*********************/
138/* Set the time unit */
139/*********************/
140	outl(data[2], devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 8);
141	if (data[0] == ADDIDATA_TIMER) {
142
143		 /******************************/
144		/* Set the mode :             */
145		/* - Disable the hardware     */
146		/* - Disable the counter mode */
147		/* - Disable the warning      */
148		/* - Disable the reset        */
149		/* - Enable the timer mode    */
150		/* - Set the timer mode       */
151		 /******************************/
152
153		ui_Command =
154			(ui_Command & 0xFFF719E2UL) | ui_Mode << 13UL | 0x10UL;
155
156	}			//if (data[0] == ADDIDATA_TIMER)
157	else {
158		if (data[0] == ADDIDATA_WATCHDOG) {
159
160		 /******************************/
161			/* Set the mode :             */
162			/* - Disable the hardware     */
163			/* - Disable the counter mode */
164			/* - Disable the warning      */
165			/* - Disable the reset        */
166			/* - Disable the timer mode   */
167		 /******************************/
168
169			ui_Command = ui_Command & 0xFFF819E2UL;
170
171		} else {
172			printk("\n The parameter for Timer/watchdog selection is in error\n");
173			return -EINVAL;
174		}
175	}
176	outl(ui_Command, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
177	ui_Command = 0;
178	ui_Command = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
179/********************************/
180/* Disable the hardware trigger */
181/********************************/
182	ui_Command = ui_Command & 0xFFFFF89FUL;
183	if (data[4] == ADDIDATA_ENABLE) {
184    /**********************************/
185		/* Set the hardware trigger level */
186    /**********************************/
187		ui_Command = ui_Command | (data[5] << 5);
188	}
189	outl(ui_Command, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
190	ui_Command = 0;
191	ui_Command = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
192/*****************************/
193/* Disable the hardware gate */
194/*****************************/
195	ui_Command = ui_Command & 0xFFFFF87FUL;
196	if (data[6] == ADDIDATA_ENABLE) {
197/*******************************/
198/* Set the hardware gate level */
199/*******************************/
200		ui_Command = ui_Command | (data[7] << 7);
201	}
202	outl(ui_Command, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
203	ui_Command = 0;
204	ui_Command = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
205/*******************************/
206/* Disable the hardware output */
207/*******************************/
208	ui_Command = ui_Command & 0xFFFFF9FBUL;
209/*********************************/
210/* Set the hardware output level */
211/*********************************/
212	ui_Command = ui_Command | (data[8] << 2);
213	outl(ui_Command, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
214	if (data[9] == ADDIDATA_ENABLE) {
215   /************************/
216		/* Set the reload value */
217   /************************/
218		outl(data[11],
219			devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 24);
220   /**********************/
221		/* Set the time unite */
222   /**********************/
223		outl(data[10],
224			devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 28);
225	}
226
227	ui_Command = 0;
228	ui_Command = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
229 /*******************************/
230	/* Disable the hardware output */
231 /*******************************/
232	ui_Command = ui_Command & 0xFFFFF9F7UL;
233   /*********************************/
234	/* Set the hardware output level */
235   /*********************************/
236	ui_Command = ui_Command | (data[12] << 3);
237	outl(ui_Command, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
238 /*************************************/
239 /**  Enable the watchdog interrupt  **/
240 /*************************************/
241	ui_Command = 0;
242	ui_Command = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
243/*******************************/
244/* Set the interrupt selection */
245/*******************************/
246	ui_Status = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 16);
247
248	ui_Command = (ui_Command & 0xFFFFF9FDUL) | (data[13] << 1);
249	outl(ui_Command, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
250
251	return insn->n;
252}
253
254/*
255+----------------------------------------------------------------------------+
256| Function   Name   : int i_APCI035_StartStopWriteTimerWatchdog              |
257|			  (comedi_device *dev,comedi_subdevice *s,               |
258|                      comedi_insn *insn,lsampl_t *data)                     |
259+----------------------------------------------------------------------------+
260| Task              : Start / Stop The Selected Timer , or Watchdog  |
261+----------------------------------------------------------------------------+
262| Input Parameters  : comedi_device *dev : Driver handle                     |
263|                     UINT *data         : Data Pointer contains             |
264|                                          configuration parameters as below |
265|					                                                 |
266|					  data[0] : 0 - Stop Selected Timer/Watchdog     |
267|					            1 - Start Selected Timer/Watchdog    |
268|					            2 - Trigger Selected Timer/Watchdog  |
269|					            3 - Stop All Timer/Watchdog          |
270|					            4 - Start All Timer/Watchdog         |
271|					            5 - Trigger All Timer/Watchdog       |
272|					                                                 |
273+----------------------------------------------------------------------------+
274| Output Parameters :	--													 |
275+----------------------------------------------------------------------------+
276| Return Value      : TRUE  : No error occur                                 |
277|		            : FALSE : Error occur. Return the error			 |
278|					                                                 |
279+----------------------------------------------------------------------------+
280*/
281INT i_APCI035_StartStopWriteTimerWatchdog(comedi_device * dev,
282	comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
283{
284	UINT ui_Command = 0;
285	INT i_Count = 0;
286	if (data[0] == 1) {
287		ui_Command =
288			inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
289	 /**********************/
290		/* Start the hardware */
291	 /**********************/
292		ui_Command = (ui_Command & 0xFFFFF9FFUL) | 0x1UL;
293		outl(ui_Command,
294			devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
295	}			// if  (data[0]==1)
296	if (data[0] == 2) {
297		ui_Command =
298			inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
299	 /***************************/
300		/* Set the trigger command */
301	 /***************************/
302		ui_Command = (ui_Command & 0xFFFFF9FFUL) | 0x200UL;
303		outl(ui_Command,
304			devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
305	}
306
307	if (data[0] == 0)	//Stop The Watchdog
308	{
309		//Stop The Watchdog
310		ui_Command = 0;
311		//ui_Command = inl(devpriv->iobase+((i_WatchdogNbr-1)*32)+12);
312		//ui_Command = ui_Command & 0xFFFFF9FEUL;
313		outl(ui_Command,
314			devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 12);
315	}			//  if (data[1]==0)
316	if (data[0] == 3)	//stop all Watchdogs
317	{
318		ui_Command = 0;
319		for (i_Count = 1; i_Count <= 4; i_Count++) {
320			if (devpriv->b_TimerSelectMode == ADDIDATA_WATCHDOG) {
321				ui_Command = 0x2UL;
322			} else {
323				ui_Command = 0x10UL;
324			}
325			i_WatchdogNbr = i_Count;
326			outl(ui_Command,
327				devpriv->iobase + ((i_WatchdogNbr - 1) * 32) +
328				0);
329		}
330
331	}
332	if (data[0] == 4)	//start all Watchdogs
333	{
334		ui_Command = 0;
335		for (i_Count = 1; i_Count <= 4; i_Count++) {
336			if (devpriv->b_TimerSelectMode == ADDIDATA_WATCHDOG) {
337				ui_Command = 0x1UL;
338			} else {
339				ui_Command = 0x8UL;
340			}
341			i_WatchdogNbr = i_Count;
342			outl(ui_Command,
343				devpriv->iobase + ((i_WatchdogNbr - 1) * 32) +
344				0);
345		}
346	}
347	if (data[0] == 5)	//trigger all Watchdogs
348	{
349		ui_Command = 0;
350		for (i_Count = 1; i_Count <= 4; i_Count++) {
351			if (devpriv->b_TimerSelectMode == ADDIDATA_WATCHDOG) {
352				ui_Command = 0x4UL;
353			} else {
354				ui_Command = 0x20UL;
355			}
356
357			i_WatchdogNbr = i_Count;
358			outl(ui_Command,
359				devpriv->iobase + ((i_WatchdogNbr - 1) * 32) +
360				0);
361		}
362		i_Temp = 1;
363	}
364	return insn->n;
365}
366
367/*
368+----------------------------------------------------------------------------+
369| Function   Name   : int i_APCI035_ReadTimerWatchdog                        |
370|			  (comedi_device *dev,comedi_subdevice *s,               |
371|                      comedi_insn *insn,lsampl_t *data)                     |
372+----------------------------------------------------------------------------+
373| Task              : Read The Selected Timer , Counter or Watchdog          |
374+----------------------------------------------------------------------------+
375| Input Parameters  : comedi_device *dev : Driver handle                     |
376|                     UINT *data         : Data Pointer contains             |
377|                                          configuration parameters as below |
378|                                                                            |
379|     																	 |
380+----------------------------------------------------------------------------+
381| Output Parameters :	data[0]            : software trigger status
382              data[1]            : hardware trigger status
383|     				data[2]            : Software clear status
384                        data[3]            : Overflow status
385                     data[4]            : Timer actual value
386
387
388+----------------------------------------------------------------------------+
389| Return Value      : TRUE  : No error occur                                 |
390|		            : FALSE : Error occur. Return the error          |
391|			                                                         |
392+----------------------------------------------------------------------------+
393*/
394INT i_APCI035_ReadTimerWatchdog(comedi_device * dev, comedi_subdevice * s,
395	comedi_insn * insn, lsampl_t * data)
396{
397	UINT ui_Status = 0;	// Status register
398	i_WatchdogNbr = insn->unused[0];
399	      /******************/
400	/* Get the status */
401	      /******************/
402	ui_Status = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 16);
403      /***********************************/
404	/* Get the software trigger status */
405      /***********************************/
406	data[0] = ((ui_Status >> 1) & 1);
407      /***********************************/
408	/* Get the hardware trigger status */
409      /***********************************/
410	data[1] = ((ui_Status >> 2) & 1);
411      /*********************************/
412	/* Get the software clear status */
413      /*********************************/
414	data[2] = ((ui_Status >> 3) & 1);
415      /***************************/
416	/* Get the overflow status */
417      /***************************/
418	data[3] = ((ui_Status >> 0) & 1);
419	if (devpriv->b_TimerSelectMode == ADDIDATA_TIMER) {
420		data[4] = inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 0);
421
422	}			//  if  (devpriv->b_TimerSelectMode==ADDIDATA_TIMER)
423
424	return insn->n;
425}
426
427/*
428+----------------------------------------------------------------------------+
429| Function   Name   : INT i_APCI035_ConfigAnalogInput                        |
430|			  (comedi_device *dev,comedi_subdevice *s,               |
431|                      comedi_insn *insn,lsampl_t *data)                     |
432+----------------------------------------------------------------------------+
433| Task              : Configures The Analog Input Subdevice                  |
434+----------------------------------------------------------------------------+
435| Input Parameters  : comedi_device *dev      : Driver handle                |
436|                     comedi_subdevice *s     : Subdevice Pointer            |
437|                     comedi_insn *insn       : Insn Structure Pointer       |
438|                     lsampl_t *data          : Data Pointer contains        |
439|                                          configuration parameters as below |
440|                     data[0]                  : Warning delay value
441|                                                                            |
442+----------------------------------------------------------------------------+
443| Output Parameters :	--													 |
444+----------------------------------------------------------------------------+
445| Return Value      : TRUE  : No error occur                                 |
446|		            : FALSE : Error occur. Return the error          |
447|			                                                         |
448+----------------------------------------------------------------------------+
449*/
450INT i_APCI035_ConfigAnalogInput(comedi_device * dev, comedi_subdevice * s,
451	comedi_insn * insn, lsampl_t * data)
452{
453	devpriv->tsk_Current = current;
454	outl(0x200 | 0, devpriv->iobase + 128 + 0x4);
455	outl(0, devpriv->iobase + 128 + 0);
456/********************************/
457/* Initialise the warning value */
458/********************************/
459	outl(0x300 | 0, devpriv->iobase + 128 + 0x4);
460	outl((data[0] << 8), devpriv->iobase + 128 + 0);
461	outl(0x200000UL, devpriv->iobase + 128 + 12);
462
463	return insn->n;
464}
465
466/*
467+----------------------------------------------------------------------------+
468| Function   Name   : int i_APCI035_ReadAnalogInput                          |
469|			          (comedi_device *dev,comedi_subdevice *s,       |
470|                     comedi_insn *insn,lsampl_t *data)                      |
471+----------------------------------------------------------------------------+
472| Task              : Read  value  of the selected channel			         |
473+----------------------------------------------------------------------------+
474| Input Parameters  : comedi_device *dev      : Driver handle                |
475|                     UINT ui_NoOfChannels    : No Of Channels To read       |
476|                     UINT *data              : Data Pointer to read status  |
477+----------------------------------------------------------------------------+
478| Output Parameters :	--													 |
479|			          data[0]  : Digital Value Of Input              |
480|			                                                         |
481+----------------------------------------------------------------------------+
482| Return Value      : TRUE  : No error occur                                 |
483|		            : FALSE : Error occur. Return the error          |
484|			                                                         |
485+----------------------------------------------------------------------------+
486*/
487INT i_APCI035_ReadAnalogInput(comedi_device * dev, comedi_subdevice * s,
488	comedi_insn * insn, lsampl_t * data)
489{
490	UINT ui_CommandRegister = 0;
491/******************/
492/*  Set the start */
493/******************/
494	ui_CommandRegister = 0x80000;
495 /******************************/
496	/* Write the command register */
497 /******************************/
498	outl(ui_CommandRegister, devpriv->iobase + 128 + 8);
499
500/***************************************/
501/* Read the digital value of the input */
502/***************************************/
503	data[0] = inl(devpriv->iobase + 128 + 28);
504	return insn->n;
505}
506
507/*
508+----------------------------------------------------------------------------+
509| Function   Name   :  int i_APCI035_Reset(comedi_device *dev)			     |
510|					                                                         |
511+----------------------------------------------------------------------------+
512| Task              :Resets the registers of the card                        |
513+----------------------------------------------------------------------------+
514| Input Parameters  :                                                        |
515+----------------------------------------------------------------------------+
516| Output Parameters :	--													 |
517+----------------------------------------------------------------------------+
518| Return Value      :                                                        |
519|			                                                                 |
520+----------------------------------------------------------------------------+
521*/
522INT i_APCI035_Reset(comedi_device * dev)
523{
524	INT i_Count = 0;
525	for (i_Count = 1; i_Count <= 4; i_Count++) {
526		i_WatchdogNbr = i_Count;
527		outl(0x0, devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 0);	//stop all timers
528	}
529	outl(0x0, devpriv->iobase + 128 + 12);	//Disable the warning delay
530
531	return 0;
532}
533
534/*
535+----------------------------------------------------------------------------+
536| Function   Name   : static void v_APCI035_Interrupt					     |
537|					  (int irq , void *d)      |
538+----------------------------------------------------------------------------+
539| Task              : Interrupt processing Routine                           |
540+----------------------------------------------------------------------------+
541| Input Parameters  : int irq                 : irq number                   |
542|                     void *d                 : void pointer                 |
543+----------------------------------------------------------------------------+
544| Output Parameters :	--													 |
545+----------------------------------------------------------------------------+
546| Return Value      : TRUE  : No error occur                                 |
547|		            : FALSE : Error occur. Return the error          |
548|			                                                         |
549+----------------------------------------------------------------------------+
550*/
551static void v_APCI035_Interrupt(int irq, void *d)
552{
553	comedi_device *dev = d;
554	UINT ui_StatusRegister1 = 0;
555	UINT ui_StatusRegister2 = 0;
556	UINT ui_ReadCommand = 0;
557	UINT ui_ChannelNumber = 0;
558	UINT ui_DigitalTemperature = 0;
559	if (i_Temp == 1) {
560		i_WatchdogNbr = i_Flag;
561		i_Flag = i_Flag + 1;
562	}
563  /**************************************/
564	/* Read the interrupt status register of temperature Warning */
565  /**************************************/
566	ui_StatusRegister1 = inl(devpriv->iobase + 128 + 16);
567  /**************************************/
568	/* Read the interrupt status register for Watchdog/timer */
569   /**************************************/
570
571	ui_StatusRegister2 =
572		inl(devpriv->iobase + ((i_WatchdogNbr - 1) * 32) + 20);
573
574	if ((((ui_StatusRegister1) & 0x8) == 0x8))	//Test if warning relay interrupt
575	{
576	/**********************************/
577		/* Disable the temperature warning */
578	/**********************************/
579		ui_ReadCommand = inl(devpriv->iobase + 128 + 12);
580		ui_ReadCommand = ui_ReadCommand & 0xFFDF0000UL;
581		outl(ui_ReadCommand, devpriv->iobase + 128 + 12);
582      /***************************/
583		/* Read the channel number */
584      /***************************/
585		ui_ChannelNumber = inl(devpriv->iobase + 128 + 60);
586	/**************************************/
587		/* Read the digital temperature value */
588	/**************************************/
589		ui_DigitalTemperature = inl(devpriv->iobase + 128 + 60);
590		send_sig(SIGIO, devpriv->tsk_Current, 0);	// send signal to the sample
591	}			//if (((ui_StatusRegister1 & 0x8) == 0x8))
592
593	else {
594		if ((ui_StatusRegister2 & 0x1) == 0x1) {
595			send_sig(SIGIO, devpriv->tsk_Current, 0);	// send signal to the sample
596		}
597	}			//else if (((ui_StatusRegister1 & 0x8) == 0x8))
598
599	return;
600}
601