xref: /csrg-svn/sys/vax/stand/up.c (revision 11085)
1*11085Ssam /*	up.c	4.6	83/02/16	*/
29974Ssam 
310023Ssam /*
410023Ssam  * UNIBUS peripheral standalone driver
510023Ssam  * with ECC correction and bad block forwarding.
610023Ssam  * Also supports header operation and write
710023Ssam  * check for data and/or header.
810023Ssam  */
910352Shelge 
109974Ssam #include "../h/param.h"
119974Ssam #include "../h/inode.h"
129974Ssam #include "../h/fs.h"
139974Ssam #include "../h/dkbad.h"
149974Ssam #include "../h/vmmac.h"
159974Ssam 
169974Ssam #include "../vax/pte.h"
179974Ssam #include "../vaxuba/upreg.h"
189974Ssam #include "../vaxuba/ubareg.h"
199974Ssam 
2010023Ssam #include "saio.h"
219974Ssam #include "savax.h"
229974Ssam 
2310352Shelge #define MAXBADDESC	126	/* max number of bad sectors recorded */
2410352Shelge #define SECTSIZ		512	/* sector size in bytes */
2510352Shelge #define HDRSIZ		4	/* number of bytes in sector header */
2610352Shelge #define MAXECC		5	/* max number of bad bits accepted in
2710352Shelge 				 * a soft ecc error when F_ECCLM is set */
2810352Shelge #define	NUPTYPES	3
2910352Shelge 
3010023Ssam u_short	ubastd[] = { 0776700 };
319974Ssam 
329974Ssam char	up_gottype[MAXNUBA*8] = { 0 };
339974Ssam char	up_type[MAXNUBA*8] = { 0 };
349974Ssam short	up_off[] = { 0, 27, 68, -1, -1, -1, -1, 82 };
359974Ssam short	fj_off[] = { 0, 50, 0, -1, -1, -1, -1, 155 };
369974Ssam /* this is called upam instead of am because hp.c has a similar array */
379974Ssam short	upam_off[] = { 0, 32, 0, 668, 723, 778, 668, 98 };
3810023Ssam 
3910352Shelge struct st upst[NUPTYPES] = {
4010023Ssam 	32,	19,	32*19,	823,	up_off,		/* 9300/equiv */
4110023Ssam 	32,	10,	32*10,	823,	fj_off,		/* Fuji 160 */
4210023Ssam 	32,	16,	32*16,	1024,	upam_off,	/* Capricorn */
439974Ssam };
4410023Ssam 
459974Ssam u_char	up_offset[16] = {
469974Ssam 	UPOF_P400, UPOF_M400, UPOF_P400, UPOF_M400,
479974Ssam 	UPOF_P800, UPOF_M800, UPOF_P800, UPOF_M800,
489974Ssam 	UPOF_P1200, UPOF_M1200, UPOF_P1200, UPOF_M1200,
499974Ssam 	0, 0, 0, 0
509974Ssam };
519974Ssam 
5210023Ssam struct  dkbad upbad[MAXNUBA*8];		/* bad sector table */
5310352Shelge int 	sectsiz;			/* real sector size */
5410023Ssam 
559974Ssam upopen(io)
569974Ssam 	register struct iob *io;
579974Ssam {
5810352Shelge 	register unit = io->i_unit;
5910023Ssam 	register struct updevice *upaddr;
6010352Shelge 	register struct st *st = &upst[up_type[unit]];
619974Ssam 
6210023Ssam 	if (io->i_boff < 0 || io->i_boff > 7 || st->off[io->i_boff] == -1)
6310023Ssam 		_stop("up bad unit");
6410352Shelge 	upaddr = (struct updevice *)ubamem(unit, ubastd[0]);
65*11085Ssam 	while ((upaddr->upcs1 & UP_DVA) == 0)
669974Ssam 		;
6710352Shelge 	if (up_gottype[unit] == 0) {
6810023Ssam 		register int i;
6910023Ssam 		struct iob tio;
7010023Ssam 
719974Ssam 		upaddr->uphr = UPHR_MAXTRAK;
7210023Ssam 		for (st = upst; st < &upst[NUPTYPES]; st++)
7310023Ssam 			if (upaddr->uphr == st->ntrak - 1) {
7410352Shelge 				up_type[unit] = st - upst;
7510023Ssam 				break;
7610023Ssam 			}
7710023Ssam 		if (st == &upst[NUPTYPES]) {
7810352Shelge 			printf("up%d: uphr=%x\n", unit, upaddr->uphr);
7910023Ssam 			_stop("unknown drive type");
8010023Ssam 		}
819974Ssam 		upaddr->upcs2 = UPCS2_CLR;
829974Ssam #ifdef DEBUG
8310352Shelge 		printf("Unittype=%d\n",up_type[unit]);
849974Ssam #endif
859974Ssam 
8610023Ssam 		/*
8710023Ssam 		 * Read in the bad sector table:
8810023Ssam 		 *	copy the contents of the io structure
8910023Ssam 		 *	to tio for use during the bb pointer
9010023Ssam 		 *	read operation.
9110023Ssam 		 */
9210023Ssam 		tio = *io;
9310023Ssam 		tio.i_bn = st->nspc * st->ncyl - st->nsect;
949974Ssam 		tio.i_ma = (char *)&upbad[tio.i_unit];
9510638Shelge 		tio.i_cc = sizeof (struct dkbad);
9610023Ssam 		tio.i_flgs |= F_RDDATA;
9710023Ssam 		for (i = 0; i < 5; i++) {
9810638Shelge 			if (upstrategy(&tio, READ) == sizeof (struct dkbad))
9910023Ssam 				break;
1009974Ssam 			tio.i_bn += 2;
1019974Ssam 		}
1029974Ssam 		if (i == 5) {
10310023Ssam 			printf("Unable to read bad sector table\n");
10410352Shelge 			for (i = 0; i < MAXBADDESC; i++) {
10510352Shelge 				upbad[unit].bt_bad[i].bt_cyl = -1;
10610352Shelge 				upbad[unit].bt_bad[i].bt_trksec = -1;
1079974Ssam 			}
1089974Ssam 		}
10910352Shelge 		up_gottype[unit] = 1;
1109974Ssam 	}
1119974Ssam 	io->i_boff = st->off[io->i_boff] * st->nspc;
11210023Ssam 	io->i_flgs &= ~F_TYPEMASK;
1139974Ssam }
1149974Ssam 
1159974Ssam upstrategy(io, func)
1169974Ssam 	register struct iob *io;
1179974Ssam {
11810352Shelge 	int cn, tn, sn;
11910352Shelge 	register unit = io->i_unit;
1209974Ssam 	daddr_t bn;
1219974Ssam 	int recal, info, waitdry;
1229974Ssam 	register struct updevice *upaddr =
12310352Shelge 	    (struct updevice *)ubamem(unit, ubastd[0]);
12410352Shelge 	register struct st *st = &upst[up_type[unit]];
1259974Ssam 
12610352Shelge 	sectsiz = SECTSIZ;
127*11085Ssam 	if (io->i_flgs & (F_HDR|F_HCHECK))
12810352Shelge 		sectsiz += HDRSIZ;
1299974Ssam 	upaddr->upcs2 = unit;
1309974Ssam 	if ((upaddr->upds & UPDS_VV) == 0) {
1319974Ssam 		upaddr->upcs1 = UP_DCLR|UP_GO;
1329974Ssam 		upaddr->upcs1 = UP_PRESET|UP_GO;
1339974Ssam 		upaddr->upof = UPOF_FMT22;
1349974Ssam 	}
135*11085Ssam 	if ((upaddr->upds & UPDS_DREADY) == 0)
1369974Ssam 		_stop("up not ready");
1379974Ssam 	info = ubasetup(io, 1);
1389974Ssam 	upaddr->upwc = -io->i_cc / sizeof (short);
1399974Ssam 	upaddr->upba = info;
140*11085Ssam 	recal = 0;
141*11085Ssam 	io->i_errcnt = 0;
142*11085Ssam 
14310638Shelge restart:
144*11085Ssam 	bn = io->i_bn + (io->i_cc + upaddr->upwc * sizeof(short)) / sectsiz;
14510023Ssam 	while((upaddr->upds & UPDS_DRY) == 0)
14610023Ssam 		;
14710352Shelge 	if (upstart(io, bn) != 0) {
14810352Shelge 		ubafree(io, info);
1499974Ssam 		return (-1);
15010352Shelge 	}
1519974Ssam 	do {
1529974Ssam 		DELAY(25);
1539974Ssam 	} while ((upaddr->upcs1 & UP_RDY) == 0);
154*11085Ssam 	/*
155*11085Ssam 	 * If transfer has completed, free UNIBUS
156*11085Ssam 	 * resources and return transfer size.
157*11085Ssam 	 */
158*11085Ssam 	if ((upaddr->upds&UPDS_ERR) == 0 && (upaddr->upcs1&UP_TRE) == 0) {
15910352Shelge 		ubafree(io, info);
160*11085Ssam 		return (io->i_cc);
16110352Shelge 	}
1629974Ssam #ifdef LOGALLERRS
1639974Ssam 	printf("uper: (c,t,s)=(%d,%d,%d) cs2=%b er1=%b er2=%b wc=%x\n",
164*11085Ssam 		upaddr->updc, upaddr->upda>>8, (upaddr->upda&0x1f-1),
165*11085Ssam 	    	upaddr->upcs2, UPCS2_BITS, upaddr->uper1,
166*11085Ssam 		UPER1_BITS, upaddr->uper2, UPER2_BITS,-upaddr->upwc);
1679974Ssam #endif
1689974Ssam 	waitdry = 0;
16910352Shelge 	while ((upaddr->upds & UPDS_DRY) == 0 && ++waitdry < sectsiz)
17010023Ssam 		DELAY(5);
171*11085Ssam 	if (upaddr->uper1&UPER1_WLE) {
172*11085Ssam 		/*
173*11085Ssam 		 * Give up on write locked devices immediately.
174*11085Ssam 		 */
175*11085Ssam 		printf("up%d: write locked\n", unit);
176*11085Ssam 		return (-1);
177*11085Ssam 	}
1789974Ssam 	if (++io->i_errcnt > 27) {
1799974Ssam 		/*
1809974Ssam 		 * After 28 retries (16 without offset, and
1819974Ssam 		 * 12 with offset positioning) give up.
1829974Ssam 		 */
18310023Ssam 		io->i_error = EHER;
18410023Ssam 		if (upaddr->upcs2 & UPCS2_WCE)
18510023Ssam 			io->i_error = EWCK;
18610352Shelge hard:
187*11085Ssam 		bn = io->i_bn +
188*11085Ssam 			(io->i_cc + upaddr->upwc * sizeof (short)) / sectsiz;
1899974Ssam 		cn = bn/st->nspc;
1909974Ssam 		sn = bn%st->nspc;
1919974Ssam 		tn = sn/st->nsect;
1929974Ssam 		sn = sn%st->nsect;
193*11085Ssam 		printf(
194*11085Ssam 		  "up error: (cyl,trk,sec)=(%d,%d,%d) cs2=%b er1=%b er2=%b\n",
195*11085Ssam 		   cn, tn, sn,
196*11085Ssam 		   upaddr->upcs2, UPCS2_BITS, upaddr->uper1,
197*11085Ssam 		   UPER1_BITS, upaddr->uper2, UPER2_BITS);
1989974Ssam 		upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO;
19910352Shelge 		io->i_errblk = bn;
200*11085Ssam 		return (io->i_cc + upaddr->upwc * sizeof(short));
201*11085Ssam 	}
202*11085Ssam 	if (upaddr->uper2 & UPER2_BSE) {
203*11085Ssam 		short wc = upaddr->upwc;
204*11085Ssam 		if ((io->i_flgs&F_NBSF) == 0 && upecc(io, BSE) == 0) {
205*11085Ssam 			if (wc != upaddr->upwc)
206*11085Ssam 				printf("wc %x upwc %x\n", wc, upaddr->upwc);
2079974Ssam 			goto success;
2089974Ssam 		}
209*11085Ssam 		io->i_error = EBSE;
210*11085Ssam 		goto hard;
2119974Ssam 	}
212*11085Ssam 	/*
213*11085Ssam 	 * Retriable error.
214*11085Ssam 	 * If a soft ecc, correct it
215*11085Ssam 	 * Otherwise fall through and retry the transfer
216*11085Ssam 	 */
217*11085Ssam 	if (upaddr->uper1 & UPER1_DCK) {
2189974Ssam 		/*
219*11085Ssam 		 * If a write check command is active, all
220*11085Ssam 		 * ecc errors give UPER1_ECH.
2219974Ssam 		 */
222*11085Ssam 		if ((upaddr->uper1 & UPER1_ECH) == 0 ||
223*11085Ssam 		    (upaddr->upcs2 & UPCS2_WCE)) {
224*11085Ssam 			if (upecc(io, ECC) == 0)
225*11085Ssam 				goto success;
226*11085Ssam 			io->i_error = EECC;
227*11085Ssam 			goto hard;
228*11085Ssam 		}
229*11085Ssam 	}
2309974Ssam 	/*
2319974Ssam 	 * Clear drive error and, every eight attempts,
2329974Ssam 	 * (starting with the fourth)
2339974Ssam 	 * recalibrate to clear the slate.
2349974Ssam 	 */
2359974Ssam 	upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO;
23610638Shelge 	if ((io->i_errcnt&07) == 4 ) {
2379974Ssam 		upaddr->upcs1 = UP_RECAL|UP_GO;
23810638Shelge 		recal = 1;
23910638Shelge 		goto restart;
2409974Ssam 	}
2419974Ssam 	/*
2429974Ssam 	 * Advance recalibration finite state machine
2439974Ssam 	 * if recalibrate in progress, through
2449974Ssam 	 *	RECAL
2459974Ssam 	 *	SEEK
2469974Ssam 	 *	OFFSET (optional)
2479974Ssam 	 *	RETRY
2489974Ssam 	 */
2499974Ssam 	switch (recal) {
2509974Ssam 
2519974Ssam 	case 1:
2529974Ssam 		upaddr->updc = cn;
2539974Ssam 		upaddr->upcs1 = UP_SEEK|UP_GO;
254*11085Ssam 		recal = 2;
25510638Shelge 		goto restart;
25610023Ssam 
2579974Ssam 	case 2:
2589974Ssam 		if (io->i_errcnt < 16 || (func & READ) == 0)
2599974Ssam 			goto donerecal;
2609974Ssam 		upaddr->upof = up_offset[io->i_errcnt & 017] | UPOF_FMT22;
2619974Ssam 		upaddr->upcs1 = UP_OFFSET|UP_GO;
262*11085Ssam 		recal = 3;
26310638Shelge 		goto restart;
26410023Ssam 
2659974Ssam 	donerecal:
2669974Ssam 	case 3:
2679974Ssam 		recal = 0;
2689974Ssam 		break;
2699974Ssam 	}
2709974Ssam 	/*
27110638Shelge 	 * If we were offset positioning,
27210638Shelge 	 * return to centerline.
2739974Ssam 	 */
27410638Shelge 	if (io->i_errcnt >= 16) {
27510638Shelge 		upaddr->upof = UPOF_FMT22;
27610638Shelge 		upaddr->upcs1 = UP_RTC|UP_GO;
27710638Shelge 		while ((upaddr->upds&UPDS_DRY) == 0)
27810638Shelge 			DELAY(25);
2799974Ssam 	}
28010638Shelge 	goto restart;
281*11085Ssam 
2829974Ssam success:
28310023Ssam 	if (upaddr->upwc != 0)
28410638Shelge 		goto restart;
2859974Ssam 	/*
2869974Ssam 	 * Release unibus
2879974Ssam 	 */
2889974Ssam 	ubafree(io, info);
2899974Ssam 	return (io->i_cc);
2909974Ssam }
2919974Ssam 
2929974Ssam /*
2939974Ssam  * Correct an ECC error, and restart the i/o to complete
2949974Ssam  * the transfer if necessary.  This is quite complicated because
2959974Ssam  * the transfer may be going to an odd memory address base and/or
2969974Ssam  * across a page boundary.
2979974Ssam  */
29810023Ssam upecc(io, flag)
2999974Ssam 	register struct iob *io;
3009974Ssam 	int flag;
3019974Ssam {
3029974Ssam 	register struct updevice *up =
3039974Ssam 		(struct updevice *)ubamem(io->i_unit, ubastd[0]);
30410352Shelge 	register struct st *st;
3059974Ssam 	register int i;
3069974Ssam 	caddr_t addr;
30710410Shelge 	int bn, twc, npf, mask, cn, tn, sn;
30810352Shelge 	daddr_t bbn;
3099974Ssam 
3109974Ssam 	/*
3119974Ssam 	 * Npf is the number of sectors transferred before the sector
3129974Ssam 	 * containing the ECC error, bn is the current block number
3139974Ssam 	 */
31410352Shelge 	twc = up->upwc;
31510352Shelge 	npf = ((twc * sizeof(short)) + io->i_cc)/sectsiz;
3169974Ssam #ifdef UPECCDEBUG
3179974Ssam 	printf("npf %d mask 0x%x pos %d wc 0x%x\n",npf,mask,up->upec1,-up->upwc);
3189974Ssam #endif
31910352Shelge 	bn = io->i_bn + npf ;
3209974Ssam 	st = &upst[up_type[io->i_unit]];
32110410Shelge 	cn = bn/st->nspc;
32210410Shelge 	sn = bn%st->nspc;
32310410Shelge 	tn = sn/st->nsect;
32410410Shelge 	sn = sn%st->nsect;
3259974Ssam 	/*
3269974Ssam 	 * action taken depends on the flag
3279974Ssam 	 */
3289974Ssam 	if (flag == ECC) {
32910352Shelge 		int bit, byte, ecccnt;
330*11085Ssam 
33110352Shelge 		ecccnt = 0;
3329974Ssam 		mask = up->upec2;
33310638Shelge 		printf("up%d: soft ecc sn%d\n", io->i_unit, bn);
3349974Ssam 		/*
3359974Ssam 		 * Compute the
3369974Ssam 		 * byte and bit position of the error.  The variable i
3379974Ssam 		 * is the byte offset in the transfer.
3389974Ssam 		 */
3399974Ssam 		i = up->upec1 - 1;		/* -1 makes 0 origin */
3409974Ssam 		bit = i&07;
3419974Ssam 		i = (i&~07)>>3;
3429974Ssam 		byte = i;
3439974Ssam 		up->upcs1 = UP_TRE|UP_DCLR|UP_GO;
3449974Ssam 		/*
3459974Ssam 		 * Correct while possible bits remain of mask.  Since mask
3469974Ssam 		 * contains 11 bits, we continue while the bit offset is > -11.
3479974Ssam 		 * Also watch out for end of this block and the end of the whole
3489974Ssam 		 * transfer.
3499974Ssam 		 */
35010352Shelge 		while (i < sectsiz && (npf*sectsiz)+i < io->i_cc && bit > -11) {
3519974Ssam 			/*
35210023Ssam 			 * addr = vax base addr + (number of sectors transferred
3539974Ssam 			 *	  before the error sector times the sector size)
3549974Ssam 			 *	  + byte number
3559974Ssam 			 */
356*11085Ssam 			addr = io->i_ma + (npf * sectsiz) + byte;
3579974Ssam #ifdef UPECCDEBUG
35810352Shelge 			printf("addr %x old: %x ",addr, (*addr&0xff));
3599974Ssam #endif
36010352Shelge 			if ((io->i_flgs & (F_CHECK|F_HCHECK)) == 0)
36110352Shelge 				*addr ^= (mask << bit);
3629974Ssam #ifdef UPECCDEBUG
36310352Shelge 			printf("new: %x\n", (*addr&0xff));
3649974Ssam #endif
3659974Ssam 			byte++;
3669974Ssam 			i++;
3679974Ssam 			bit -= 8;
368*11085Ssam 			if ((io->i_flgs&F_ECCLM) && ++ecccnt > MAXECC)
369*11085Ssam 				return (1);
3709974Ssam 		}
371*11085Ssam 		return (0);
372*11085Ssam 	}
373*11085Ssam 	if (flag == BSE) {
3749974Ssam 		/*
37510352Shelge 		 * if not in bad sector table, return 1 (= hard error)
3769974Ssam 		 */
37710352Shelge 		up->upcs1 = UP_TRE|UP_DCLR|UP_GO;
37810410Shelge 		if ((bbn = isbad(&upbad[io->i_unit], cn, tn, sn)) < 0)
379*11085Ssam 			return (1);
3809974Ssam 		bbn = st->ncyl * st->nspc -st->nsect - 1 - bbn;
38110352Shelge 		twc = up->upwc + sectsiz;
382*11085Ssam 		up->upwc = - (sectsiz / sizeof (short));
3839974Ssam #ifdef UPECCDEBUG
3849974Ssam 		printf("revector to block %d\n", bbn);
3859974Ssam #endif
3869974Ssam 		/*
3879974Ssam 	 	* Clear the drive & read the replacement sector.
3889974Ssam 	 	* If this is in the middle of a transfer, then set up the
3899974Ssam 	 	* controller registers in a normal fashion.
3909974Ssam 	 	* The ub-address need not be changed.
3919974Ssam 	 	*/
39210023Ssam 		while (up->upcs1 & UP_RDY == 0)
3939974Ssam 			;
39410023Ssam 		if (upstart(io, bbn) != 0)
39510352Shelge 			return (1);		/* error */
39610352Shelge 		io->i_errcnt = 0;		/* success */
3979974Ssam 		do {
3989974Ssam 			DELAY(25);
3999974Ssam 		} while ( up->upcs1 & UP_RDY == 0) ;
4009974Ssam 		if (up->upds & UPDS_ERR || up->upcs1 & UP_TRE) {
40110352Shelge 			up->upwc = twc -sectsiz;
40210352Shelge 			return (1);
4039974Ssam 		}
4049974Ssam 	}
40510638Shelge 	if (twc)
4069974Ssam 		up->upwc = twc;
40710352Shelge 	return (0);
4089974Ssam }
4099974Ssam 
4109974Ssam upstart(io, bn)
41110023Ssam 	register struct iob *io;
41210023Ssam 	daddr_t bn;
4139974Ssam {
4149974Ssam 	register struct updevice *upaddr =
41510023Ssam 		(struct updevice *)ubamem(io->i_unit, ubastd[0]);
41610352Shelge 	register struct st *st = &upst[up_type[io->i_unit]];
4179974Ssam 	int sn, tn;
4189974Ssam 
4199974Ssam 	sn = bn%st->nspc;
4209974Ssam 	tn = sn/st->nsect;
4219974Ssam 	sn %= st->nsect;
4229974Ssam 	upaddr->updc = bn/st->nspc;
4239974Ssam 	upaddr->upda = (tn << 8) + sn;
42410352Shelge 	switch (io->i_flgs & F_TYPEMASK) {
42510023Ssam 
42610023Ssam 	case F_RDDATA:
42710023Ssam 		upaddr->upcs1 = UP_RCOM|UP_GO;
4289974Ssam 		break;
42910023Ssam 
43010023Ssam 	case F_WRDATA:
43110023Ssam 		upaddr->upcs1 = UP_WCOM|UP_GO;
4329974Ssam 		break;
43310023Ssam 
43410023Ssam 	case F_HDR|F_RDDATA:
43510023Ssam 		upaddr->upcs1 = UP_RHDR|UP_GO;
43610023Ssam 		break;
43710023Ssam 
43810023Ssam 	case F_HDR|F_WRDATA:
43910023Ssam 		upaddr->upcs1 = UP_WHDR|UP_GO;
44010023Ssam 		break;
44110023Ssam 
44210023Ssam 	case F_CHECK|F_WRDATA:
44310023Ssam 	case F_CHECK|F_RDDATA:
4449974Ssam 		upaddr->upcs1 = UP_WCDATA|UP_GO;
4459974Ssam 		break;
44610023Ssam 
44710023Ssam 	case F_HCHECK|F_WRDATA:
44810023Ssam 	case F_HCHECK|F_RDDATA:
4499974Ssam 		upaddr->upcs1 = UP_WCHDR|UP_GO;
4509974Ssam 		break;
45110023Ssam 
4529974Ssam 	default:
45310023Ssam 		io->i_error = ECMD;
45410023Ssam 		io->i_flgs &= ~F_TYPEMASK;
45510023Ssam 		return (1);
4569974Ssam 	}
45710023Ssam 	return (0);
4589974Ssam }
4599974Ssam 
46010023Ssam /*ARGSUSED*/
46110023Ssam upioctl(io, cmd, arg)
46210023Ssam 	struct iob *io;
46310023Ssam 	int cmd;
46410023Ssam 	caddr_t arg;
46510023Ssam {
46610352Shelge 	struct st *st = &upst[up_type[io->i_unit]], *tmp;
46710352Shelge 
46810352Shelge 	switch(cmd) {
46910352Shelge 
47010352Shelge 	case SAIODEVDATA:
47110352Shelge 		tmp = (struct st *)arg;
47210352Shelge 		*tmp = *st;
473*11085Ssam 		return (0);
47410352Shelge 	}
475*11085Ssam 	return (ECMD);
47610023Ssam }
477