xref: /csrg-svn/sys/vax/uba/up.c (revision 264)
1*264Sbill int	printsw;
2*264Sbill int	slow1 = 1;
3*264Sbill int	slow2 = 1;
4*264Sbill int	slow3 = 1;
5*264Sbill int	slow4 = 1;
6*264Sbill /*	10/14/12	3.1	06/16/80	*/
7*264Sbill 
8*264Sbill /*
9*264Sbill  * Emulex UNIBUS disk driver with overlapped seeks and ECC recovery.
10*264Sbill  *
11*264Sbill  * This driver has been tested on a SC-11B Controller, configured
12*264Sbill  * with the following internal switch settings:
13*264Sbill  *	SW1-1	5/19 surfaces	(off, 19 surfaces on Ampex 9300)
14*264Sbill  *	SW1-2	chksum enable	(off, checksum disabled)
15*264Sbill  *	SW1-3	volume select	(off, 815 cylinders)
16*264Sbill  *	SW1-4	sector select	(on, 32 sectors)
17*264Sbill  *	SW1-5	unused		(off)
18*264Sbill  *	SW1-6	port select	(on, single port)
19*264Sbill  *	SW1-7	npr delay	(off, disable)
20*264Sbill  *	SW1-8	ecc test mode	(off, disable)
21*264Sbill  * and top mounted switches:
22*264Sbill  *	SW2-1	extend opcodes	(off=open, disable)
23*264Sbill  *	SW2-2	extend diag	(off=open, disable)
24*264Sbill  *	SW2-3	4 wd dma burst	(off=open, disable)
25*264Sbill  *	SW2-4	unused		(off=open)
26*264Sbill  *
27*264Sbill  * The controller transfers data much more rapidly with SW2-3 set,
28*264Sbill  * but we have previously experienced problems with it set this way.
29*264Sbill  * We intend to try this again in the near future.
30*264Sbill  *
31*264Sbill  *	wnj	June 14, 1980
32*264Sbill  */
33*264Sbill 
34*264Sbill #include "../h/param.h"
35*264Sbill #include "../h/systm.h"
36*264Sbill #include "../h/buf.h"
37*264Sbill #include "../h/conf.h"
38*264Sbill #include "../h/dir.h"
39*264Sbill #include "../h/user.h"
40*264Sbill #include "../h/map.h"
41*264Sbill #include "../h/mba.h"
42*264Sbill #include "../h/mtpr.h"
43*264Sbill #include "../h/pte.h"
44*264Sbill #include "../h/uba.h"
45*264Sbill #include "../h/vm.h"
46*264Sbill 
47*264Sbill /*
48*264Sbill  * Define number of drives, and range of sampling information to be used.
49*264Sbill  *
50*264Sbill  * Normally, DK_N .. DK_N+NUP-1 gather individual drive stats,
51*264Sbill  * and DK_N+NUP gathers controller transferring stats.
52*264Sbill  *
53*264Sbill  * If DK_N+NUP > DK_NMAX, then transfer stats are divided per drive.
54*264Sbill  * If DK_NMAX is yet smaller, some drives are not monitored.
55*264Sbill  */
56*264Sbill #define	DK_N	1
57*264Sbill #define	DK_NMAX	2
58*264Sbill 
59*264Sbill #define	ushort	unsigned short
60*264Sbill 
61*264Sbill struct	device
62*264Sbill {
63*264Sbill 	ushort	upcs1;		/* control and status register 1 */
64*264Sbill 	short	upwc;		/* word count register */
65*264Sbill 	ushort	upba;		/* UNIBUS address register */
66*264Sbill 	ushort	upda;		/* desired address register */
67*264Sbill 	ushort	upcs2;		/* control and status register 2 */
68*264Sbill 	ushort	upds;		/* drive Status */
69*264Sbill 	ushort	uper1;		/* error register 1 */
70*264Sbill 	ushort	upas;		/* attention summary */
71*264Sbill 	ushort	upla;		/* look ahead */
72*264Sbill 	ushort	updb;		/* data buffer */
73*264Sbill 	ushort	upmr;		/* maintenance */
74*264Sbill 	ushort	updt;		/* drive type */
75*264Sbill 	ushort	upsn;		/* serial number */
76*264Sbill 	ushort	upof;		/* offset register */
77*264Sbill 	ushort	updc;		/* desired cylinder address register */
78*264Sbill 	ushort	upcc;		/* current cylinder */
79*264Sbill 	ushort	uper2;		/* error register 2 */
80*264Sbill 	ushort	uper3;		/* error register 3 */
81*264Sbill 	ushort	upec1;		/* burst error bit position */
82*264Sbill 	ushort	upec2;		/* burst error bit pattern */
83*264Sbill };
84*264Sbill 
85*264Sbill #define	UPADDR	((struct device *)(UBA0_DEV + 0176700))
86*264Sbill 
87*264Sbill #define	NUP	2		/* Number of drives this installation */
88*264Sbill 
89*264Sbill #define	NSECT	32
90*264Sbill #define	NTRAC	19
91*264Sbill 
92*264Sbill /*
93*264Sbill  * Constants controlling on-cylinder SEARCH usage.
94*264Sbill  *
95*264Sbill  * We assume that it takes SDIST sectors of time to set up a transfer.
96*264Sbill  * If a drive is on-cylinder, and between SDIST and SDIST+RDIST sectors
97*264Sbill  * from the first sector to be transferred, then we just perform the
98*264Sbill  * transfer.  SDIST represents interrupt latency, RDIST the amount
99*264Sbill  * of rotation which is tolerable to avoid another interrupt.
100*264Sbill  */
101*264Sbill #define	SDIST	4		/* 4 sectors ~~= 2 msec */
102*264Sbill #define	RDIST	6		/* 6 sectors ~~= 3 msec */
103*264Sbill 
104*264Sbill /*
105*264Sbill  * To fill a 300M drive:
106*264Sbill  *	A is designed to be used as a root.
107*264Sbill  *	B is suitable for a swap area.
108*264Sbill  *	H is the primary storage area.
109*264Sbill  * On systems with RP06'es, we normally use only 291346 blocks of the H
110*264Sbill  * area, and use DEF or G to cover the rest of the drive.  The C system
111*264Sbill  * covers the whole drive and can be used for pack-pack copying.
112*264Sbill  */
113*264Sbill struct	size
114*264Sbill {
115*264Sbill 	daddr_t	nblocks;
116*264Sbill 	int	cyloff;
117*264Sbill } up_sizes[8] = {
118*264Sbill 	15884,	0,		/* A=cyl 0 thru 26 */
119*264Sbill 	33440,	27,		/* B=cyl 27 thru 81 */
120*264Sbill 	494912,	0,		/* C=cyl 0 thru 814 */
121*264Sbill 	15884,	562,		/* D=cyl 562 thru 588 */
122*264Sbill 	55936,	589,		/* E=cyl 589 thru 680 */
123*264Sbill 	81472,	681,		/* F=cyl 681 thru 814 */
124*264Sbill 	153824,	562,		/* G=cyl 562 thru 814 */
125*264Sbill 	445664,	82,		/* H=cyl 82 thru 814 */
126*264Sbill /* Later, and more safely for H area...
127*264Sbill 	291346,	82,		/* H=cyl 82 thru 561 */
128*264Sbill };
129*264Sbill 
130*264Sbill /*
131*264Sbill  * The following defines are used in offset positioning
132*264Sbill  * when trying to recover disk errors, with the constants being
133*264Sbill  * +/- microinches.  Note that header compare inhibit (HCI) is not
134*264Sbill  * tried (this makes sense only during read, in any case.)
135*264Sbill  *
136*264Sbill  * ARE ALL THESE IMPLEMENTED ON 9300?
137*264Sbill  */
138*264Sbill #define	P400	020
139*264Sbill #define	M400	0220
140*264Sbill #define	P800	040
141*264Sbill #define	M800	0240
142*264Sbill #define	P1200	060
143*264Sbill #define	M1200	0260
144*264Sbill #define	HCI	020000
145*264Sbill 
146*264Sbill int	up_offset[16] =
147*264Sbill {
148*264Sbill 	P400, M400, P400, M400,
149*264Sbill 	P800, M800, P800, M800,
150*264Sbill 	P1200, M1200, P1200, M1200,
151*264Sbill 	0, 0, 0, 0,
152*264Sbill };
153*264Sbill 
154*264Sbill /*
155*264Sbill  * Each drive has a table uputab[i].  On this table are sorted the
156*264Sbill  * pending requests implementing an elevator algorithm (see dsort.c.)
157*264Sbill  * In the upustart() routine, each drive is independently advanced
158*264Sbill  * until it is on the desired cylinder for the next transfer and near
159*264Sbill  * the desired sector.  The drive is then chained onto the uptab
160*264Sbill  * table, and the transfer is initiated by the upstart() routine.
161*264Sbill  * When the transfer is completed the driver reinvokes the upustart()
162*264Sbill  * routine to set up the next transfer.
163*264Sbill  */
164*264Sbill struct	buf	uptab;
165*264Sbill struct	buf	uputab[NUP];
166*264Sbill 
167*264Sbill struct	buf	rupbuf;			/* Buffer for raw i/o */
168*264Sbill 
169*264Sbill /* Drive commands, placed in upcs1 */
170*264Sbill #define	GO	01		/* Go bit, set in all commands */
171*264Sbill #define	PRESET	020		/* Preset drive at init or after errors */
172*264Sbill #define	OFFSET	014		/* Offset heads to try to recover error */
173*264Sbill #define	RTC	016		/* Return to center-line after OFFSET */
174*264Sbill #define	SEARCH	030		/* Search for cylinder+sector */
175*264Sbill #define	RECAL	06		/* Recalibrate, needed after seek error */
176*264Sbill #define	DCLR	010		/* Drive clear, after error */
177*264Sbill #define	WCOM	060		/* Write */
178*264Sbill #define	RCOM	070		/* Read */
179*264Sbill 
180*264Sbill /* Other bits of upcs1 */
181*264Sbill #define	IE	0100		/* Controller wide interrupt enable */
182*264Sbill #define	TRE	040000		/* Transfer error */
183*264Sbill 
184*264Sbill /* Drive status bits of upds */
185*264Sbill #define	PIP	020000		/* Positioning in progress */
186*264Sbill #define	ERR	040000		/* Error has occurred, DCLR necessary */
187*264Sbill #define	VV	0100		/* Volume is valid, set by PRESET */
188*264Sbill #define	DPR	0400		/* Drive has been preset */
189*264Sbill #define	MOL	010000		/* Drive is online, heads loaded, etc */
190*264Sbill #define	DRY	0200		/* Drive ready */
191*264Sbill 
192*264Sbill /* Bits of uper1 */
193*264Sbill #define	DCK	0100000		/* Ecc error occurred */
194*264Sbill #define	ECH	0100		/* Ecc error was unrecoverable */
195*264Sbill #define	WLE	04000		/* Attempt to write read-only drive */
196*264Sbill 
197*264Sbill /* Bits of upof; the offset bits above are also in this register */
198*264Sbill #define	FMT22	010000		/* 16 bits/word, must be always set */
199*264Sbill 
200*264Sbill #define	b_cylin b_resid
201*264Sbill 
202*264Sbill int	up_ubinfo;		/* Information about UBA usage saved here */
203*264Sbill /*
204*264Sbill  * The EMULEX controller balks if accessed quickly after
205*264Sbill  * certain operations.  The exact timing has not yet been
206*264Sbill  * determined, but delays are known to be needed when changing
207*264Sbill  * the selected drive (by writing in upcs2), and thought to be
208*264Sbill  * needed after operations like PRESET and DCLR.  The following
209*264Sbill  * variables control the delay, DELAY(n) is approximately n usec.
210*264Sbill  */
211*264Sbill int	idelay = 500;		/* Delay after PRESET or DCLR */
212*264Sbill int	sdelay = 500;		/* Delay after selecting drive in upcs2 */
213*264Sbill 
214*264Sbill #define	DELAY(N)		{ register int d; d = N; while (--d > 0); }
215*264Sbill 
216*264Sbill int	nwaitcs2;		/* How many sdelay loops ? */
217*264Sbill int	neasycs2;		/* How many sdelay loops not needed ? */
218*264Sbill 
219*264Sbill #ifdef INTRLVE
220*264Sbill daddr_t dkblock();
221*264Sbill #endif
222*264Sbill 
223*264Sbill /*
224*264Sbill  * Queue an i/o request for a drive, checking first that it is in range.
225*264Sbill  *
226*264Sbill  * A unit start is issued if the drive is inactive, causing
227*264Sbill  * a SEARCH for the correct cylinder/sector.  If the drive is
228*264Sbill  * already nearly on the money and the controller is not transferring
229*264Sbill  * we kick it to start the transfer.
230*264Sbill  */
231*264Sbill upstrategy(bp)
232*264Sbill register struct buf *bp;
233*264Sbill {
234*264Sbill 	register struct buf *dp;
235*264Sbill 	register unit, xunit;
236*264Sbill 	long sz, bn;
237*264Sbill 
238*264Sbill 	xunit = minor(bp->b_dev) & 077;
239*264Sbill 	sz = bp->b_bcount;
240*264Sbill 	sz = (sz+511) >> 9;		/* transfer size in 512 byte sectors */
241*264Sbill 	unit = dkunit(bp);
242*264Sbill 	if (unit >= NUP ||
243*264Sbill 	    bp->b_blkno < 0 ||
244*264Sbill 	    (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) {
245*264Sbill 		bp->b_flags |= B_ERROR;
246*264Sbill 		iodone(bp);
247*264Sbill 		return;
248*264Sbill 	}
249*264Sbill 	bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff;
250*264Sbill 	dp = &uputab[unit];
251*264Sbill 	(void) spl5();
252*264Sbill 	disksort(dp, bp);
253*264Sbill 	if (dp->b_active == 0) {
254*264Sbill 		upustart(unit);
255*264Sbill 		if (uptab.b_actf && uptab.b_active == 0)
256*264Sbill 			upstart();
257*264Sbill 	}
258*264Sbill 	(void) spl0();
259*264Sbill }
260*264Sbill 
261*264Sbill /*
262*264Sbill  * Start activity on specified drive; called when drive is inactive
263*264Sbill  * and new transfer request arrives and also when upas indicates that
264*264Sbill  * a SEARCH command is complete.
265*264Sbill  */
266*264Sbill upustart(unit)
267*264Sbill register unit;
268*264Sbill {
269*264Sbill 	register struct buf *bp, *dp;
270*264Sbill 	register struct device *upaddr = UPADDR;
271*264Sbill 	daddr_t bn;
272*264Sbill 	int sn, cn, csn;
273*264Sbill 
274*264Sbill 	if (printsw&1) printf("upustart\n");
275*264Sbill 	if (slow1) DELAY(idelay);
276*264Sbill 	upaddr->upas = 1<<unit;
277*264Sbill 	if (slow4) DELAY(idelay);
278*264Sbill 	upaddr->upcs1 = IE;
279*264Sbill 	if (slow4) DELAY(idelay);
280*264Sbill 	if (unit >= NUP) {
281*264Sbill 		printf("stray upustart\n");		/* can't happen */
282*264Sbill 		return;
283*264Sbill 	}
284*264Sbill 
285*264Sbill 	if (unit+DK_N <= DK_NMAX)
286*264Sbill 		dk_busy &= ~(1<<(unit+DK_N));
287*264Sbill 	dp = &uputab[unit];
288*264Sbill 	if((bp=dp->b_actf) == NULL)
289*264Sbill 		return;
290*264Sbill 	if (slow1) DELAY(idelay);
291*264Sbill 	if ((upaddr->upcs2 & 07) != unit) {
292*264Sbill 		upaddr->upcs2 = unit;
293*264Sbill 		DELAY(sdelay);
294*264Sbill 		nwaitcs2++;
295*264Sbill 	} else
296*264Sbill 		neasycs2++;
297*264Sbill 	if (slow2) DELAY(idelay);
298*264Sbill 	if((upaddr->upds & VV) == 0) {
299*264Sbill 		upaddr->upcs1 = IE|PRESET|GO;
300*264Sbill 		DELAY(idelay);
301*264Sbill 		upaddr->upof = FMT22;
302*264Sbill 	}
303*264Sbill 	/*
304*264Sbill 	 * Don't SEARCH twice on same drive; avoids looping.
305*264Sbill 	 */
306*264Sbill 	if(dp->b_active)
307*264Sbill 		goto done;
308*264Sbill 	dp->b_active++;
309*264Sbill 	if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL))
310*264Sbill 		goto done;
311*264Sbill 
312*264Sbill 	bn = dkblock(bp);
313*264Sbill 	cn = bp->b_cylin;
314*264Sbill 	sn = bn%(NSECT*NTRAC);
315*264Sbill 	sn = (sn+NSECT-SDIST)%NSECT;
316*264Sbill 
317*264Sbill 	if(cn - upaddr->updc)
318*264Sbill 		goto search;
319*264Sbill 	csn = (upaddr->upla>>6) - sn - 1;
320*264Sbill 	if(csn < 0)
321*264Sbill 		csn += NSECT;
322*264Sbill 	if(csn > NSECT-RDIST)
323*264Sbill 		goto done;
324*264Sbill 
325*264Sbill search:
326*264Sbill 	upaddr->updc = cn;
327*264Sbill 	upaddr->upda = sn;
328*264Sbill 	upaddr->upcs1 = IE|SEARCH|GO;
329*264Sbill 	unit += DK_N;
330*264Sbill 	if (unit <= DK_NMAX) {
331*264Sbill 		dk_busy |= 1<<unit;
332*264Sbill 		dk_numb[unit]++;
333*264Sbill 	}
334*264Sbill 	return;
335*264Sbill 
336*264Sbill done:
337*264Sbill 	dp->b_forw = NULL;
338*264Sbill 	if(uptab.b_actf == NULL)
339*264Sbill 		uptab.b_actf = dp;
340*264Sbill 	else
341*264Sbill 		uptab.b_actl->b_forw = dp;
342*264Sbill 	uptab.b_actl = dp;
343*264Sbill }
344*264Sbill 
345*264Sbill /*
346*264Sbill  * Start a transfer; call from top level at spl5() or on interrupt.
347*264Sbill  *
348*264Sbill  * Pick a drive off the queue of ready drives and perform
349*264Sbill  * the first transfer in its queue.
350*264Sbill  */
351*264Sbill upstart()
352*264Sbill {
353*264Sbill 	register struct buf *bp, *dp;
354*264Sbill 	register unit;
355*264Sbill 	register struct device *upaddr;
356*264Sbill 	daddr_t bn;
357*264Sbill 	int dn, sn, tn, cn;
358*264Sbill 
359*264Sbill 	if (printsw&2) printf("upstart\n");
360*264Sbill loop:
361*264Sbill 	if ((dp = uptab.b_actf) == NULL)
362*264Sbill 		return;
363*264Sbill 	if ((bp = dp->b_actf) == NULL) {
364*264Sbill 		uptab.b_actf = dp->b_forw;
365*264Sbill 		goto loop;
366*264Sbill 	}
367*264Sbill 	uptab.b_active++;
368*264Sbill 	unit = minor(bp->b_dev) & 077;
369*264Sbill 	dn = dkunit(bp);
370*264Sbill 	bn = dkblock(bp);
371*264Sbill 	cn = up_sizes[unit&07].cyloff;
372*264Sbill 	cn += bn/(NSECT*NTRAC);
373*264Sbill 	sn = bn%(NSECT*NTRAC);
374*264Sbill 	tn = sn/NSECT;
375*264Sbill 	sn = sn%NSECT;
376*264Sbill 
377*264Sbill 	upaddr = UPADDR;
378*264Sbill 	if (slow3) DELAY(idelay);
379*264Sbill 	if ((upaddr->upcs2 & 07) != dn) {
380*264Sbill 		upaddr->upcs2 = dn;
381*264Sbill 		DELAY(sdelay);
382*264Sbill 		nwaitcs2++;
383*264Sbill 	} else
384*264Sbill 		neasycs2++;
385*264Sbill 	up_ubinfo = ubasetup(bp, 1);
386*264Sbill 	if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) {
387*264Sbill 		uptab.b_active = 0;
388*264Sbill 		uptab.b_errcnt = 0;
389*264Sbill 		dp->b_actf = bp->av_forw;
390*264Sbill 		bp->b_flags |= B_ERROR;
391*264Sbill 		iodone(bp);
392*264Sbill 		ubafree(up_ubinfo), up_ubinfo = 0;
393*264Sbill 		goto loop;
394*264Sbill 	}
395*264Sbill 	if(uptab.b_errcnt >= 16) {
396*264Sbill 		upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22;
397*264Sbill 		upaddr->upcs1 = OFFSET|GO;
398*264Sbill 		DELAY(idelay);
399*264Sbill 		while(upaddr->upds & PIP)
400*264Sbill 			DELAY(25);
401*264Sbill 	}
402*264Sbill 	upaddr->updc = cn;
403*264Sbill 	upaddr->upda = (tn << 8) + sn;
404*264Sbill 	upaddr->upba = up_ubinfo;
405*264Sbill 	upaddr->upwc = -bp->b_bcount / sizeof (short);
406*264Sbill 	if (bp->b_flags & B_READ)
407*264Sbill 		upaddr->upcs1 = IE|GO|RCOM;
408*264Sbill 	else
409*264Sbill 		upaddr->upcs1 = IE|GO|WCOM;
410*264Sbill 
411*264Sbill 	unit = dn+DK_N;
412*264Sbill 	if (NUP+DK_N == DK_NMAX)
413*264Sbill 		unit = NUP+DK_N;
414*264Sbill 	if (unit <= DK_NMAX) {
415*264Sbill 		dk_busy |= 1<<unit;
416*264Sbill 		dk_numb[unit]++;
417*264Sbill 		dk_wds[unit] += bp->b_bcount>>6;
418*264Sbill 	}
419*264Sbill }
420*264Sbill 
421*264Sbill /*
422*264Sbill  * Handle a device interrupt.
423*264Sbill  *
424*264Sbill  * If the transferring drive needs attention, service it
425*264Sbill  * retrying on error or beginning next transfer.
426*264Sbill  * Service all other ready drives, calling ustart to transfer
427*264Sbill  * their blocks to the ready queue in uptab, and then restart
428*264Sbill  * the controller if there is anything to do.
429*264Sbill  */
430*264Sbill upintr()
431*264Sbill {
432*264Sbill 	register struct buf *bp, *dp;
433*264Sbill 	register unit;
434*264Sbill 	register struct device *upaddr = UPADDR;
435*264Sbill 	int as = upaddr->upas & 0377;
436*264Sbill 
437*264Sbill 	if (printsw&4) printf("upintr\n");
438*264Sbill 	if(uptab.b_active) {
439*264Sbill 		dp = uptab.b_actf;
440*264Sbill 		bp = dp->b_actf;
441*264Sbill 		unit = dkunit(bp);
442*264Sbill 		if (DK_N+NUP == DK_NMAX)
443*264Sbill 			dk_busy &= ~(1<<(DK_N+NUP));
444*264Sbill 		else if (DK_N+unit <= DK_NMAX)
445*264Sbill 			dk_busy &= ~(1<<(DK_N+unit));
446*264Sbill 		if (upaddr->upcs1 & TRE) {
447*264Sbill 			if ((upaddr->upcs2 & 07) != unit) {
448*264Sbill 				upaddr->upcs2 = unit;
449*264Sbill 				DELAY(sdelay);
450*264Sbill 				nwaitcs2++;
451*264Sbill 			} else
452*264Sbill 				neasycs2++;
453*264Sbill 			while((upaddr->upds & DRY) == 0)
454*264Sbill 				DELAY(25);
455*264Sbill 			if(++uptab.b_errcnt > 28 || upaddr->uper1&WLE)
456*264Sbill 				bp->b_flags |= B_ERROR;
457*264Sbill 			else
458*264Sbill 				uptab.b_active = 0;
459*264Sbill 			if(uptab.b_errcnt > 27)
460*264Sbill 				deverror(bp, upaddr->upcs2, upaddr->uper1);
461*264Sbill 			if ((upaddr->uper1&(DCK|ECH)) == DCK) {
462*264Sbill 				if (upecc(upaddr, bp))
463*264Sbill 					return;
464*264Sbill 			}
465*264Sbill 			upaddr->upcs1 = TRE|IE|DCLR|GO;
466*264Sbill 			DELAY(idelay);
467*264Sbill 			if((uptab.b_errcnt&07) == 4) {
468*264Sbill 				upaddr->upcs1 = RECAL|GO|IE;
469*264Sbill 				DELAY(idelay);
470*264Sbill 				while(upaddr->upds & PIP)
471*264Sbill 					DELAY(25);
472*264Sbill 			}
473*264Sbill 		}
474*264Sbill 		if(uptab.b_active) {
475*264Sbill 			if(uptab.b_errcnt) {
476*264Sbill 				upaddr->upcs1 = RTC|GO;
477*264Sbill 				DELAY(idelay);
478*264Sbill 				while(upaddr->upds & PIP)
479*264Sbill 					DELAY(25);
480*264Sbill 			}
481*264Sbill 			uptab.b_active = 0;
482*264Sbill 			uptab.b_errcnt = 0;
483*264Sbill 			uptab.b_actf = dp->b_forw;
484*264Sbill 			dp->b_active = 0;
485*264Sbill 			dp->b_errcnt = 0;
486*264Sbill 			dp->b_actf = bp->av_forw;
487*264Sbill 			bp->b_resid = (-upaddr->upwc * 2);
488*264Sbill 			iodone(bp);
489*264Sbill 			if(dp->b_actf)
490*264Sbill 				upustart(unit);
491*264Sbill 		}
492*264Sbill 		as &= ~(1<<unit);
493*264Sbill 		ubafree(up_ubinfo), up_ubinfo = 0;
494*264Sbill 	} else {
495*264Sbill 		if (upaddr->upcs1 & TRE) {
496*264Sbill 			upaddr->upcs1 = TRE;
497*264Sbill 			DELAY(idelay);
498*264Sbill 		}
499*264Sbill 		if (slow4) DELAY(idelay);
500*264Sbill 		if(as == 0)
501*264Sbill 			upaddr->upcs1 = IE;
502*264Sbill 		if (slow4) DELAY(idelay);
503*264Sbill 	}
504*264Sbill 	for(unit=0; unit<NUP; unit++)
505*264Sbill 		if(as & (1<<unit))
506*264Sbill 			upustart(unit);
507*264Sbill 	upstart();
508*264Sbill }
509*264Sbill 
510*264Sbill upread(dev)
511*264Sbill {
512*264Sbill 
513*264Sbill 	physio(upstrategy, &rupbuf, dev, B_READ, minphys);
514*264Sbill }
515*264Sbill 
516*264Sbill upwrite(dev)
517*264Sbill {
518*264Sbill 
519*264Sbill 	physio(upstrategy, &rupbuf, dev, B_WRITE, minphys);
520*264Sbill }
521*264Sbill 
522*264Sbill upecc(up, bp)
523*264Sbill register struct device *up;
524*264Sbill register struct buf *bp;
525*264Sbill {
526*264Sbill 	struct uba_regs *ubp = (struct uba_regs *)UBA0;
527*264Sbill 	register int i, off;
528*264Sbill 	caddr_t addr;
529*264Sbill 	int reg, bit, byte, npf, mask, o;
530*264Sbill 	extern char buffers[NBUF][BSIZE];
531*264Sbill 	int bn, cn, tn, sn;
532*264Sbill 
533*264Sbill 	if (printsw&8) printf("upecc\n");
534*264Sbill 	/*
535*264Sbill 	 * Npf is number of page frames (= disk blocks) completed before ecc.
536*264Sbill 	 */
537*264Sbill 	npf = btop((UPADDR->upwc * sizeof(short)) + bp->b_bcount) - 1;
538*264Sbill 	reg = btop(up_ubinfo&0xffff) + npf;
539*264Sbill 	o = (int)bp->b_un.b_addr & PGOFSET;
540*264Sbill 	printf("%D ", bp->b_blkno+npf);
541*264Sbill 	prdev("ECC", bp->b_dev);
542*264Sbill 	mask = up->upec2;
543*264Sbill 	if (mask == 0) {
544*264Sbill 		up->upof = FMT22;
545*264Sbill 		DELAY(idelay);
546*264Sbill 		return (0);
547*264Sbill 	}
548*264Sbill 	i = up->upec1 - 1;
549*264Sbill 	bit = i&017;
550*264Sbill 	i = (i&~017)>>3;
551*264Sbill 	byte = i + o;
552*264Sbill 	if (byte & 1) {
553*264Sbill 		byte--;
554*264Sbill 		bit += 8;
555*264Sbill 	}
556*264Sbill 	i += (int)ptob(reg);
557*264Sbill 	for (off = 0; off <= 32; off += 16) {
558*264Sbill 		if (i <= bp->b_bcount) {
559*264Sbill 			addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+
560*264Sbill 			    (byte & PGOFSET);
561*264Sbill 			putmemw(addr, getmemw(addr)^(mask<<bit));
562*264Sbill 		}
563*264Sbill 		byte += sizeof (short);
564*264Sbill 		i += sizeof (short);
565*264Sbill 		bit -= 16;
566*264Sbill 	}
567*264Sbill 	uptab.b_active++;
568*264Sbill 	if (up->upwc == 0)
569*264Sbill 		return (0);
570*264Sbill 	up->upcs1 = DCLR|GO;
571*264Sbill 	DELAY(idelay);
572*264Sbill 	bn = dkblock(bp);
573*264Sbill 	cn = bp->b_cylin;
574*264Sbill 	sn = bn%(NSECT*NTRAC);
575*264Sbill 	tn = sn/NSECT;
576*264Sbill 	sn %= NSECT;
577*264Sbill 	sn += npf + 1;
578*264Sbill 	cn += sn/NSECT;
579*264Sbill 	sn %= NSECT;
580*264Sbill 	up->updc = cn;
581*264Sbill 	up->upda = ((i/NSECT)<<8) + (i%NSECT);
582*264Sbill 	up->upba = (int)ptob(reg+1)|((int)bp->b_un.b_addr&PGOFSET);
583*264Sbill 	up->upcs1 = IE|GO|RCOM;
584*264Sbill 	return (1);
585*264Sbill }
586