xref: /csrg-svn/sys/vax/stand/up.c (revision 9974)
1*9974Ssam /*	up.c	4.1	82/12/26	*/
2*9974Ssam 
3*9974Ssam #include "../h/param.h"
4*9974Ssam #include "../h/inode.h"
5*9974Ssam #include "../h/fs.h"
6*9974Ssam #include "../h/dkbad.h"
7*9974Ssam #include "../h/vmmac.h"
8*9974Ssam 
9*9974Ssam #include "../vax/pte.h"
10*9974Ssam #include "../vaxuba/upreg.h"
11*9974Ssam #include "../vaxuba/ubareg.h"
12*9974Ssam 
13*9974Ssam #include "nsaio.h"
14*9974Ssam #include "savax.h"
15*9974Ssam 
16*9974Ssam #define updevctl(io, func)  (up_ctl[io->i_unit] = func)
17*9974Ssam 
18*9974Ssam struct  dkbad upbad[MAXNUBA*8];		/* bad block pointers */
19*9974Ssam u_short	ubastd[] = { 0776700 };
20*9974Ssam char	up_gottype[MAXNUBA*8] = { 0 };
21*9974Ssam char	up_type[MAXNUBA*8] = { 0 };
22*9974Ssam char	up_ctl[MAXNUBA*8]  = { 0 };
23*9974Ssam short	up_off[] = { 0, 27, 68, -1, -1, -1, -1, 82 };
24*9974Ssam short	fj_off[] = { 0, 50, 0, -1, -1, -1, -1, 155 };
25*9974Ssam /* this is called upam instead of am because hp.c has a similar array */
26*9974Ssam short	upam_off[] = { 0, 32, 0, 668, 723, 778, 668, 98 };
27*9974Ssam struct upst {
28*9974Ssam 	short nsect;
29*9974Ssam 	short ntrak;
30*9974Ssam 	short nspc;
31*9974Ssam 	short ncyl;
32*9974Ssam 	short *off;
33*9974Ssam } upst[] = {
34*9974Ssam /*  sectors,   surfaces,sect/cyl,cylind,		*/
35*9974Ssam 	32,	19,	32*19,	823,	up_off,
36*9974Ssam 	32,	10,	32*10,	823,	fj_off,
37*9974Ssam 	32,	16,	32*16,	1024,	upam_off,
38*9974Ssam };
39*9974Ssam u_char	up_offset[16] = {
40*9974Ssam 	UPOF_P400, UPOF_M400, UPOF_P400, UPOF_M400,
41*9974Ssam 	UPOF_P800, UPOF_M800, UPOF_P800, UPOF_M800,
42*9974Ssam 	UPOF_P1200, UPOF_M1200, UPOF_P1200, UPOF_M1200,
43*9974Ssam 	0, 0, 0, 0
44*9974Ssam };
45*9974Ssam 
46*9974Ssam upopen(io)
47*9974Ssam 	register struct iob *io;
48*9974Ssam {
49*9974Ssam 	register struct updevice *upaddr =
50*9974Ssam 	    (struct updevice *)ubamem(io->i_unit, ubastd[0]);
51*9974Ssam 	register struct upst *st;
52*9974Ssam 	struct iob tio;
53*9974Ssam 	int i = 0;
54*9974Ssam 
55*9974Ssam 	while ((upaddr->upcs1 & UP_DVA) == 0) /* infinite wait */
56*9974Ssam 		;
57*9974Ssam 	st = &upst[up_type[io->i_unit]];
58*9974Ssam 	if (up_gottype[io->i_unit] == 0) {
59*9974Ssam 		upaddr->uphr = UPHR_MAXTRAK;
60*9974Ssam 		if (upaddr->uphr == 9)
61*9974Ssam 			up_type[io->i_unit] = 1;	/* fuji kludge */
62*9974Ssam 		else if (upaddr->uphr == 15)
63*9974Ssam 			up_type[io->i_unit] = 2;	/* capricorn kludge */
64*9974Ssam 		upaddr->upcs2 = UPCS2_CLR;
65*9974Ssam #ifdef DEBUG
66*9974Ssam 		printf("Unittype=%d\n",up_type[io->i_unit]);
67*9974Ssam #endif
68*9974Ssam 		st = &upst[up_type[io->i_unit]];
69*9974Ssam 
70*9974Ssam 	/* read in bad block ptrs */
71*9974Ssam 		tio = *io;		/* copy the contents of the io structure
72*9974Ssam 					 * to tio for use during the bb pointer
73*9974Ssam 					 * read operation */
74*9974Ssam 		tio.i_bn = (st->nspc * st->ncyl - st->nsect);
75*9974Ssam 		tio.i_ma = (char *)&upbad[tio.i_unit];
76*9974Ssam 		tio.i_cc = sizeof(upbad);
77*9974Ssam 		for (i=0; i<5; i++) {
78*9974Ssam 			if (upstrategy(&tio, READ) == sizeof(upbad)) break;
79*9974Ssam 			tio.i_bn += 2;
80*9974Ssam 		}
81*9974Ssam 		if (i == 5) {
82*9974Ssam 			printf("Unable to read bad block ptrs\n");
83*9974Ssam 			for (i=0; i<126; i++) {
84*9974Ssam 				upbad[io->i_unit].bt_bad[i].bt_cyl = -1;
85*9974Ssam 				upbad[io->i_unit].bt_bad[i].bt_trksec = -1;
86*9974Ssam 			}
87*9974Ssam 		}
88*9974Ssam 		up_gottype[io->i_unit] = 1;
89*9974Ssam 	}
90*9974Ssam 	if (io->i_boff < 0 || io->i_boff > 7 || st->off[io->i_boff] == -1)
91*9974Ssam 		_stop("up bad unit");
92*9974Ssam 	io->i_boff = st->off[io->i_boff] * st->nspc;
93*9974Ssam 	updevctl(io, NORMAL);
94*9974Ssam }
95*9974Ssam 
96*9974Ssam upstrategy(io, func)
97*9974Ssam 	register struct iob *io;
98*9974Ssam {
99*9974Ssam 	int unit, cn, tn, sn;
100*9974Ssam 	daddr_t bn;
101*9974Ssam 	int recal, info, waitdry;
102*9974Ssam 	register struct updevice *upaddr =
103*9974Ssam 	    (struct updevice *)ubamem(io->i_unit, ubastd[0]);
104*9974Ssam 	register struct upst *st = &upst[up_type[io->i_unit]];
105*9974Ssam 
106*9974Ssam 	if (func == READ)
107*9974Ssam 		io->i_flgs |= IO_READ;
108*9974Ssam 	else
109*9974Ssam 		io->i_flgs |= IO_WRITE;
110*9974Ssam 	unit = io->i_unit;
111*9974Ssam 	io->i_errcnt = 0;
112*9974Ssam 	recal = 3;
113*9974Ssam 	upaddr->upcs2 = unit;
114*9974Ssam 	if ((upaddr->upds & UPDS_VV) == 0) {
115*9974Ssam 		upaddr->upcs1 = UP_DCLR|UP_GO;
116*9974Ssam 		upaddr->upcs1 = UP_PRESET|UP_GO;
117*9974Ssam 		upaddr->upof = UPOF_FMT22;
118*9974Ssam 	}
119*9974Ssam 	if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY)
120*9974Ssam 		_stop("up not ready");
121*9974Ssam 	info = ubasetup(io, 1);
122*9974Ssam 	upaddr->upwc = -io->i_cc / sizeof (short);
123*9974Ssam 	upaddr->upba = info;
124*9974Ssam readmore:
125*9974Ssam 	bn = io->i_bn + btop(io->i_cc + upaddr->upwc*sizeof(short));
126*9974Ssam 	while((upaddr->upds & UPDS_DRY) == 0) ;
127*9974Ssam 	if (upstart(io, bn) != 0)
128*9974Ssam 		return (-1);
129*9974Ssam 	do {
130*9974Ssam 		DELAY(25);
131*9974Ssam 	} while ((upaddr->upcs1 & UP_RDY) == 0);
132*9974Ssam 
133*9974Ssam 	if (((upaddr->upds&UPDS_ERR) | (upaddr->upcs1&UP_TRE)) == 0 )
134*9974Ssam 		return(io->i_cc);
135*9974Ssam 
136*9974Ssam #ifdef LOGALLERRS
137*9974Ssam 	printf("uper: (c,t,s)=(%d,%d,%d) cs2=%b er1=%b er2=%b wc=%x\n",
138*9974Ssam 			upaddr->updc, upaddr->upda>>8, (upaddr->upda&0x1f-1),
139*9974Ssam 		    	upaddr->upcs2, UPCS2_BITS, upaddr->uper1,
140*9974Ssam 			UPER1_BITS, upaddr->uper2, UPER2_BITS,-upaddr->upwc);
141*9974Ssam #endif
142*9974Ssam 	waitdry = 0;
143*9974Ssam 	while ((upaddr->upds & UPDS_DRY) == 0) {
144*9974Ssam 		if (++waitdry > 512)
145*9974Ssam 			break;
146*9974Ssam 	}
147*9974Ssam 	if (++io->i_errcnt > 27) {
148*9974Ssam 		/*
149*9974Ssam 		 * After 28 retries (16 without offset, and
150*9974Ssam 		 * 12 with offset positioning) give up.
151*9974Ssam 		 */
152*9974Ssam 		io->i_error = IOERR_HER;
153*9974Ssam hard:
154*9974Ssam 		if (upaddr->upcs2 & UPCS2_WCE) io->i_error=IOERR_WCK;
155*9974Ssam 		bn = io->i_bn + btop(io->i_cc + upaddr->upwc*sizeof(short));
156*9974Ssam 		cn = bn/st->nspc;
157*9974Ssam 		sn = bn%st->nspc;
158*9974Ssam 		tn = sn/st->nsect;
159*9974Ssam 		sn = sn%st->nsect;
160*9974Ssam 		printf("up error: (cyl,trk,sec)=(%d,%d,%d) cs2=%b er1=%b er2=%b\n",
161*9974Ssam 		    	cn, tn, sn,
162*9974Ssam 		    	upaddr->upcs2, UPCS2_BITS, upaddr->uper1,
163*9974Ssam 			UPER1_BITS, upaddr->uper2, UPER2_BITS);
164*9974Ssam 		upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO;
165*9974Ssam 		return (io->i_cc + upaddr->upwc*sizeof(short));
166*9974Ssam 	} else
167*9974Ssam 		if (upaddr->uper1&UPER1_WLE) {
168*9974Ssam 		/*
169*9974Ssam 		 * Give up on write locked devices
170*9974Ssam 		 * immediately.
171*9974Ssam 		 */
172*9974Ssam 		printf("up%d: write locked\n", unit);
173*9974Ssam 		return(-1);
174*9974Ssam 		}
175*9974Ssam #ifndef NOBADSECT
176*9974Ssam 	else if (upaddr->uper2 & UPER2_BSE) {
177*9974Ssam 		if (upecc( io, BSE))
178*9974Ssam 			goto success;
179*9974Ssam 		else {
180*9974Ssam 			io->i_error = IOERR_BSE;
181*9974Ssam 			goto hard;
182*9974Ssam 		}
183*9974Ssam 	}
184*9974Ssam #endif
185*9974Ssam 	else {
186*9974Ssam 		/*
187*9974Ssam 		 * Retriable error.
188*9974Ssam 		 * If a soft ecc, correct it
189*9974Ssam 		 * Otherwise fall through and retry the transfer
190*9974Ssam 		 */
191*9974Ssam 		if ((upaddr->uper1&(UPER1_DCK|UPER1_ECH))==UPER1_DCK) {
192*9974Ssam 			upecc( io, ECC);
193*9974Ssam 			goto success;
194*9974Ssam 		} else {
195*9974Ssam 			io->i_active = 0; /* force retry */
196*9974Ssam 		}
197*9974Ssam 	}
198*9974Ssam 	/*
199*9974Ssam 	 * Clear drive error and, every eight attempts,
200*9974Ssam 	 * (starting with the fourth)
201*9974Ssam 	 * recalibrate to clear the slate.
202*9974Ssam 	 */
203*9974Ssam 	upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO;
204*9974Ssam 	if ((io->i_errcnt&07) == 4 && io->i_active == 0) {
205*9974Ssam 		upaddr->upcs1 = UP_RECAL|UP_GO;
206*9974Ssam 		recal = 0;
207*9974Ssam 		goto nextrecal;
208*9974Ssam 	}
209*9974Ssam 	/*
210*9974Ssam 	 * Advance recalibration finite state machine
211*9974Ssam 	 * if recalibrate in progress, through
212*9974Ssam 	 *	RECAL
213*9974Ssam 	 *	SEEK
214*9974Ssam 	 *	OFFSET (optional)
215*9974Ssam 	 *	RETRY
216*9974Ssam 	 */
217*9974Ssam 	switch (recal) {
218*9974Ssam 
219*9974Ssam 	case 1:
220*9974Ssam 		upaddr->updc = cn;
221*9974Ssam 		upaddr->upcs1 = UP_SEEK|UP_GO;
222*9974Ssam 		goto nextrecal;
223*9974Ssam 	case 2:
224*9974Ssam 		if (io->i_errcnt < 16 || (func & READ) == 0)
225*9974Ssam 			goto donerecal;
226*9974Ssam 		upaddr->upof = up_offset[io->i_errcnt & 017] | UPOF_FMT22;
227*9974Ssam 		upaddr->upcs1 = UP_OFFSET|UP_GO;
228*9974Ssam 	nextrecal:
229*9974Ssam 		recal++;
230*9974Ssam 		io->i_active = 1;
231*9974Ssam 		goto readmore;
232*9974Ssam 	donerecal:
233*9974Ssam 	case 3:
234*9974Ssam 		recal = 0;
235*9974Ssam 		io->i_active = 0;
236*9974Ssam 		break;
237*9974Ssam 	}
238*9974Ssam 	/*
239*9974Ssam 	 * If still ``active'', then don't need any more retries.
240*9974Ssam 	 */
241*9974Ssam 	if (io->i_active) {
242*9974Ssam 		/*
243*9974Ssam 		 * If we were offset positioning,
244*9974Ssam 		 * return to centerline.
245*9974Ssam 		 */
246*9974Ssam 		if (io->i_errcnt >= 16) {
247*9974Ssam 			upaddr->upof = UPOF_FMT22;
248*9974Ssam 			upaddr->upcs1 = UP_RTC|UP_GO;
249*9974Ssam 			while (!upaddr->upds & UPDS_DRY) /* removed PIP test*/
250*9974Ssam 				DELAY(25);
251*9974Ssam 		}
252*9974Ssam 		goto readmore;
253*9974Ssam 	}
254*9974Ssam success:
255*9974Ssam 	io->i_active = 1;
256*9974Ssam 	if (upaddr->upwc != 0) {
257*9974Ssam 		goto readmore;
258*9974Ssam 	}
259*9974Ssam 	/*
260*9974Ssam 	 * Release unibus
261*9974Ssam 	 */
262*9974Ssam 	ubafree(io, info);
263*9974Ssam 	return (io->i_cc);
264*9974Ssam }
265*9974Ssam 
266*9974Ssam /*
267*9974Ssam  * Correct an ECC error, and restart the i/o to complete
268*9974Ssam  * the transfer if necessary.  This is quite complicated because
269*9974Ssam  * the transfer may be going to an odd memory address base and/or
270*9974Ssam  * across a page boundary.
271*9974Ssam  */
272*9974Ssam upecc( io, flag)
273*9974Ssam 	register struct iob *io;
274*9974Ssam 	int flag;
275*9974Ssam {
276*9974Ssam 	register struct updevice *up =
277*9974Ssam 		(struct updevice *)ubamem(io->i_unit, ubastd[0]);
278*9974Ssam 	register struct upst *st;
279*9974Ssam 	register int i;
280*9974Ssam 	caddr_t addr;
281*9974Ssam 	int bit, byte, npf, mask;
282*9974Ssam 	int bn, twc, bbn;
283*9974Ssam 
284*9974Ssam 	/*
285*9974Ssam 	 * Npf is the number of sectors transferred before the sector
286*9974Ssam 	 * containing the ECC error, bn is the current block number
287*9974Ssam 	 */
288*9974Ssam 	npf = btop((up->upwc * sizeof(short)) + io->i_cc);
289*9974Ssam 	mask = up->upec2;
290*9974Ssam #ifdef UPECCDEBUG
291*9974Ssam 	printf("npf %d mask 0x%x pos %d wc 0x%x\n",npf,mask,up->upec1,-up->upwc);
292*9974Ssam #endif
293*9974Ssam 	bn = io->i_bn + npf + 1 ;
294*9974Ssam 	st = &upst[up_type[io->i_unit]];
295*9974Ssam 	twc = up->upwc;
296*9974Ssam 	io->i_active = 2;
297*9974Ssam 	/*
298*9974Ssam 	 * action taken depends on the flag
299*9974Ssam 	 */
300*9974Ssam 	if (flag == ECC) {
301*9974Ssam 		mask = up->upec2;
302*9974Ssam 		printf("up%d: soft ecc sn%d\n", io->i_unit, io->i_bn + npf +1);
303*9974Ssam 		/*
304*9974Ssam 		 * Compute the
305*9974Ssam 		 * byte and bit position of the error.  The variable i
306*9974Ssam 		 * is the byte offset in the transfer.
307*9974Ssam 		 */
308*9974Ssam 		i = up->upec1 - 1;		/* -1 makes 0 origin */
309*9974Ssam 		bit = i&07;
310*9974Ssam 		i = (i&~07)>>3;
311*9974Ssam 		byte = i;
312*9974Ssam 		up->upcs1 = UP_TRE|UP_DCLR|UP_GO;
313*9974Ssam 		/*
314*9974Ssam 		 * Correct while possible bits remain of mask.  Since mask
315*9974Ssam 		 * contains 11 bits, we continue while the bit offset is > -11.
316*9974Ssam 		 * Also watch out for end of this block and the end of the whole
317*9974Ssam 		 * transfer.
318*9974Ssam 		 */
319*9974Ssam 		while (i < 512 && (int)ptob(npf)+i < io->i_cc && bit > -11) {
320*9974Ssam 			/*
321*9974Ssam 			 * addr = vax base addr + ( number of sectors transferred
322*9974Ssam 			 *	  before the error sector times the sector size)
323*9974Ssam 			 *	  + byte number
324*9974Ssam 			 */
325*9974Ssam 			addr = io->i_ma + (npf*512) + byte;
326*9974Ssam #ifdef UPECCDEBUG
327*9974Ssam 			printf("addr %x old: %x ",addr, *addr);
328*9974Ssam #endif
329*9974Ssam 			*addr ^= (mask << bit);
330*9974Ssam #ifdef UPECCDEBUG
331*9974Ssam 			printf("new: %x\n", *addr);
332*9974Ssam #endif
333*9974Ssam 			byte++;
334*9974Ssam 			i++;
335*9974Ssam 			bit -= 8;
336*9974Ssam 		}
337*9974Ssam #ifndef NOBADSECT
338*9974Ssam 	} else if (flag == BSE) {
339*9974Ssam 		/*
340*9974Ssam 		 * if not in bad sector table, return 0
341*9974Ssam 		 */
342*9974Ssam 		if ((bbn = isbad(&upbad[io->i_unit], st, bn)) < 0)
343*9974Ssam 			return(0);
344*9974Ssam 		up->upcs1 = UP_TRE|UP_DCLR|UP_GO;
345*9974Ssam 		bbn = st->ncyl * st->nspc -st->nsect - 1 - bbn;
346*9974Ssam 		twc = up->upwc + 512;
347*9974Ssam 		up->upwc = -(512 / sizeof (short));
348*9974Ssam #ifdef UPECCDEBUG
349*9974Ssam 		printf("revector to block %d\n", bbn);
350*9974Ssam #endif
351*9974Ssam 		/*
352*9974Ssam 	 	* Clear the drive & read the replacement sector.
353*9974Ssam 	 	* If this is in the middle of a transfer, then set up the
354*9974Ssam 	 	* controller registers in a normal fashion.
355*9974Ssam 	 	* The ub-address need not be changed.
356*9974Ssam 	 	*/
357*9974Ssam 		while ( up->upcs1 & UP_RDY == 0)
358*9974Ssam 			;
359*9974Ssam 		up->upcs1 = UP_TRE|UP_DCLR|UP_GO;
360*9974Ssam 		if (upstart(io, bbn) != 0) return (0);
361*9974Ssam 		io->i_errcnt = 0;
362*9974Ssam 		do {
363*9974Ssam 			DELAY(25);
364*9974Ssam 		} while ( up->upcs1 & UP_RDY == 0) ;
365*9974Ssam 		if (up->upds & UPDS_ERR || up->upcs1 & UP_TRE) {
366*9974Ssam 			up->upwc = twc -512;
367*9974Ssam 			return (0);
368*9974Ssam 		}
369*9974Ssam 	}
370*9974Ssam 	if (twc != 0) {
371*9974Ssam 		up->upwc = twc;
372*9974Ssam 	}
373*9974Ssam 	return (1);
374*9974Ssam }
375*9974Ssam 
376*9974Ssam #ifndef NOBADSECT
377*9974Ssam 
378*9974Ssam /*
379*9974Ssam  * Search the bad sector table looking for
380*9974Ssam  * the specified sector.  Return index if found.
381*9974Ssam  * Return -1 if not found.
382*9974Ssam  */
383*9974Ssam 
384*9974Ssam isbad(bt, st, blno)
385*9974Ssam 	register struct dkbad *bt;
386*9974Ssam 	register struct upst *st;
387*9974Ssam {
388*9974Ssam 	register int i;
389*9974Ssam 	register long blk, bblk;
390*9974Ssam 	int trk, sec;
391*9974Ssam 
392*9974Ssam 	sec = blno % st->nspc;
393*9974Ssam 	trk = sec / st->nsect;
394*9974Ssam 	sec %= st->nsect;
395*9974Ssam 	blk = ((long)(blno/st->nspc) << 16) + (trk << 8) + sec;
396*9974Ssam 	for (i = 0; i < 126; i++) {
397*9974Ssam 		bblk = ((long)bt->bt_bad[i].bt_cyl << 16) + bt->bt_bad[i].bt_trksec;
398*9974Ssam 		if (blk == bblk)
399*9974Ssam 			return (i);
400*9974Ssam 		if (blk < bblk || bblk < 0)
401*9974Ssam 			break;
402*9974Ssam 	}
403*9974Ssam 	return (-1);
404*9974Ssam }
405*9974Ssam #endif
406*9974Ssam 
407*9974Ssam upstart(io, bn)
408*9974Ssam register struct iob *io;
409*9974Ssam daddr_t bn;
410*9974Ssam {
411*9974Ssam 	register struct updevice *upaddr =
412*9974Ssam 			(struct updevice *)ubamem(io->i_unit, ubastd[0]);
413*9974Ssam 	register struct upst *st = &upst[up_type[io->i_unit]];
414*9974Ssam 	int sn, tn;
415*9974Ssam 
416*9974Ssam 	sn = bn%st->nspc;
417*9974Ssam 	tn = sn/st->nsect;
418*9974Ssam 	sn %= st->nsect;
419*9974Ssam 	upaddr->updc = bn/st->nspc;
420*9974Ssam 	upaddr->upda = (tn << 8) + sn;
421*9974Ssam 	switch (up_ctl[io->i_unit]) {
422*9974Ssam 	case NORMAL:
423*9974Ssam 		if (io->i_flgs & IO_READ)
424*9974Ssam 			upaddr->upcs1 = UP_RCOM|UP_GO;
425*9974Ssam 		else
426*9974Ssam 			upaddr->upcs1 = UP_WCOM|UP_GO;
427*9974Ssam 		break;
428*9974Ssam 	case HDRIO:
429*9974Ssam 		if (io->i_flgs & IO_READ)
430*9974Ssam 			upaddr->upcs1 = UP_RHDR|UP_GO;
431*9974Ssam 		else
432*9974Ssam 			upaddr->upcs1 = UP_WHDR|UP_GO;
433*9974Ssam 		break;
434*9974Ssam 	case WCHECK:
435*9974Ssam 		/* don't care if read or write, write check is writecheck
436*9974Ssam 		 * anyhow  */
437*9974Ssam 		upaddr->upcs1 = UP_WCDATA|UP_GO;
438*9974Ssam 		break;
439*9974Ssam 	case WHCHECK:
440*9974Ssam 		/* don't care if read or write, write check is writecheck
441*9974Ssam 		 * anyhow  */
442*9974Ssam 		upaddr->upcs1 = UP_WCHDR|UP_GO;
443*9974Ssam 		break;
444*9974Ssam 	default:
445*9974Ssam 		io->i_error = IOERR_CMD;
446*9974Ssam 		return(1);
447*9974Ssam 	}
448*9974Ssam 	return(0) ;
449*9974Ssam }
450*9974Ssam 
451