xref: /original-bsd/sys/tahoe/vba/hd.c (revision 210ce081)
1 /*
2  * Copyright (c) 1988 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Harris Corp.
7  *
8  * %sccs.include.redist.c%
9  *
10  *	@(#)hd.c	7.11 (Berkeley) 06/28/90
11  */
12 
13 #include "hd.h"
14 
15 #if NHD > 0
16 #include "param.h"
17 #include "buf.h"
18 #include "conf.h"
19 #include "dkstat.h"
20 #include "disklabel.h"
21 #include "file.h"
22 #include "systm.h"
23 #include "vmmac.h"
24 #include "time.h"
25 #include "proc.h"
26 #include "uio.h"
27 #include "syslog.h"
28 #include "kernel.h"
29 #include "ioctl.h"
30 #include "stat.h"
31 #include "errno.h"
32 
33 #include "../tahoe/cpu.h"
34 #include "../tahoe/mtpr.h"
35 
36 #include "../tahoevba/vbavar.h"
37 #include "../tahoevba/hdreg.h"
38 
39 #define	b_cylin	b_resid
40 
41 #define	hdunit(dev)		(minor(dev)>>3)
42 #define	hdpart(dev)		(minor(dev)&0x07)
43 #define	hdminor(unit, part)	(((unit)<<3)|(part))
44 
45 struct vba_ctlr *hdcminfo[NHDC];
46 struct vba_device *hddinfo[NHD];
47 int hdcprobe(), hdslave(), hdattach(), hddgo(), hdstrategy();
48 long hdstd[] = { 0 };
49 struct vba_driver hdcdriver =
50     { hdcprobe, hdslave, hdattach, hddgo, hdstd, "hd", hddinfo, "hdc", hdcminfo };
51 
52 /*
53  * Per-controller state.
54  */
55 struct hdcsoftc {
56 	u_short	hdc_flags;
57 #define	HDC_INIT	0x01	/* controller initialized */
58 #define	HDC_STARTED	0x02	/* start command issued */
59 #define	HDC_LOCKED	0x04	/* locked for direct controller access */
60 #define	HDC_WAIT	0x08	/* someone needs direct controller access */
61 	u_short	hdc_wticks;		/* timeout */
62 	struct master_mcb *hdc_mcbp;	/* address of controller mcb */
63 	struct registers *hdc_reg;	/* base address of i/o regs */
64 	struct vb_buf hdc_rbuf;		/* vba resources */
65 	struct master_mcb hdc_mcb;	/* controller mcb */
66 } hdcsoftc[NHDC];
67 
68 #define	HDCMAXTIME	20		/* max time for operation, sec. */
69 #define	HDCINTERRUPT	0xf0		/* interrupt vector */
70 
71 /*
72  * Per-drive state; probably everything should be "hd_", not "dk_",
73  * but it's not worth it, and dk is a better mnemonic for disk anyway.
74  */
75 struct dksoftc {
76 #ifdef COMPAT_42
77 	u_short	dk_def_cyl;	/* definition track cylinder address */
78 #endif
79 	int	dk_state;	/* open fsm */
80 	u_short	dk_bshift;	/* shift for * (DEV_BSIZE / sectorsize) XXX */
81 	int	dk_wlabel;	/* if label sector is writeable */
82 	u_long	dk_copenpart;	/* character units open on this drive */
83 	u_long	dk_bopenpart;	/* block units open on this drive */
84 	u_long	dk_openpart;	/* all units open on this drive */
85 	int	dk_unit;	/* unit# */
86 	int	dk_ctlr;	/* controller# */
87 	int	dk_format;	/* if format program is using disk */
88 	struct buf dk_utab;		/* i/o queue header */
89 	struct disklabel dk_label;	/* disklabel for this disk */
90 	struct mcb dk_mcb;		/* disk mcb */
91 } dksoftc[NHD];
92 
93 /*
94  * Drive states.  Used during steps of open/initialization.
95  * States < OPEN (> 0) are transient, during an open operation.
96  * OPENRAW is used for unlabeled disks, to allow format operations.
97  */
98 #define	CLOSED		0		/* disk is closed */
99 #define	WANTOPEN	1		/* open requested, not started */
100 #define	WANTOPENRAW	2		/* open requested, no label */
101 #define	RDLABEL		3		/* reading pack label */
102 #define	OPEN		4		/* intialized and ready */
103 #define	OPENRAW		5		/* open, no label */
104 
105 int hdcwstart, hdcwatch();
106 
107 /* see if the controller is really there, if so, init it. */
108 /* ARGSUSED */
109 hdcprobe(reg, vm)
110 	caddr_t reg;
111 	/* register */ struct vba_ctlr *vm;
112 {
113 	register int br, cvec;		/* must be r12, r11 */
114 	register struct hdcsoftc *hdc;
115 	static struct module_id id;
116 	struct pte *dummypte;
117 	caddr_t putl;
118 
119 	/* initialize the hdc controller structure. */
120 	hdc = &hdcsoftc[vm->um_ctlr];
121 	if (!vbmemalloc(1, reg, &dummypte, &putl)) {
122 		printf("hdc%d: vbmemalloc failed.\n", vm->um_ctlr);
123 		return(0);
124 	}
125 	hdc->hdc_reg = (struct registers *)putl;
126 
127 	/*
128 	 * try and ping the MID register; side effect of wbadaddr is to read
129 	 * the module id; the controller is bad if it's not an hdc, the hdc's
130 	 * writeable control store is not loaded, or the hdc failed the
131 	 * functional integrity test;
132 	 */
133 	if (wbadaddr(&hdc->hdc_reg->module_id, 4,
134 	    vtoph((struct process *)NULL, &id)))
135 		return(0);
136 	DELAY(10000);
137 	mtpr(PADC, 0);
138 	if (id.module_id != (u_char)HDC_MID) {
139 		printf("hdc%d: bad module id; id = %x.\n",
140 		    vm->um_ctlr, id.module_id);
141 		return(0);
142 	}
143 	if (id.code_rev == (u_char)0xff) {
144 		printf("hdc%d: micro-code not loaded.\n", vm->um_ctlr);
145 		return(0);
146 	}
147 	if (id.fit != (u_char)0xff) {
148 		printf("hdc%d: FIT test failed.\n", vm->um_ctlr);
149 		return(0);
150 	}
151 
152 	/* reset that pup; flag as inited */
153 	hdc->hdc_reg->soft_reset = 0;
154 	DELAY(1000000);
155 	hdc->hdc_flags |= HDC_INIT;
156 
157 	/* allocate page tables and i/o buffer. */
158 	if (!vbainit(&hdc->hdc_rbuf, MAXPHYS, VB_32BIT|VB_SCATTER)) {
159 		printf("hdc%d: vbainit failed\n", vm->um_ctlr);
160 		return (0);
161 	}
162 
163 	/* set pointer to master control block */
164 	hdc->hdc_mcbp =
165 	    (struct master_mcb *)vtoph((struct proc *)NULL, &hdc->hdc_mcb);
166 
167 	br = 0x17, cvec = HDCINTERRUPT + vm->um_ctlr;		/* XXX */
168 	return(sizeof(struct registers));
169 }
170 
171 /* ARGSUSED */
172 hdslave(vi, vdaddr)
173 	struct vba_device *vi;
174 	struct vddevice *vdaddr;
175 {
176 	register struct mcb *mcb;
177 	register struct disklabel *lp;
178 	register struct dksoftc *dk;
179 	static struct status status;
180 
181 	dk = &dksoftc[vi->ui_unit];
182 	dk->dk_unit = vi->ui_unit;
183 	dk->dk_ctlr = vi->ui_ctlr;
184 
185 	mcb = &dk->dk_mcb;
186 	mcb->command = HCMD_STATUS;
187 	mcb->chain[0].wcount = sizeof(struct status) / sizeof(long);
188 	mcb->chain[0].memadr  = (u_long)vtoph((struct process *)0, &status);
189 	if (hdimcb(dk)) {
190 		printf(" (no status)\n");
191 		return(0);
192 	}
193 
194 	/*
195 	 * Report the drive down if anything in the drive status looks bad.
196 	 * If the drive is offline and it is not on cylinder, then the drive
197 	 * is not there.  If there is a fault condition, the hdc will try to
198 	 * clear it when we read the disklabel information.
199 	 */
200 	if (!(status.drs&DRS_ONLINE)) {
201 		if (status.drs&DRS_ON_CYLINDER)
202 			printf(" (not online)\n");
203 		return(0);
204 	}
205 	if (status.drs&DRS_FAULT)
206 		printf(" (clearing fault)");
207 
208 	lp = &dk->dk_label;
209 #ifdef RAW_SIZE
210 	lp->d_secsize = status.bytes_per_sec;
211 #else
212 	lp->d_secsize = 512;
213 #endif
214 	lp->d_nsectors = status.max_sector + 1;
215 	lp->d_ntracks = status.max_head + 1;
216 	lp->d_ncylinders = status.max_cyl + 1;
217 	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
218 	lp->d_npartitions = 1;
219 	lp->d_partitions[0].p_offset = 0;
220 	lp->d_partitions[0].p_size = LABELSECTOR + 1;
221 	lp->d_rpm = status.rpm;
222 	lp->d_typename[0] = 'h';
223 	lp->d_typename[1] = 'd';
224 	lp->d_typename[2] = '\0';
225 #ifdef COMPAT_42
226 	dk->dk_def_cyl = status.def_cyl;
227 #endif
228 	return(1);
229 }
230 
231 hdattach(vi)
232 	register struct vba_device *vi;
233 {
234 	register struct dksoftc *dk;
235 	register struct disklabel *lp;
236 	register int unit;
237 
238 	unit = vi->ui_unit;
239 	if (hdinit(hdminor(unit, 0), 0)) {
240 		printf(": unknown drive type");
241 		return;
242 	}
243 	dk = &dksoftc[unit];
244 	lp = &dk->dk_label;
245 	hd_setsecsize(dk, lp);
246 	if (dk->dk_state == OPEN)
247 		printf(": %s <secsize %d, ntrak %d, ncyl %d, nsec %d>",
248 		    lp->d_typename, lp->d_secsize, lp->d_ntracks,
249 		    lp->d_ncylinders, lp->d_nsectors);
250 
251 	/*
252 	 * (60 / rpm) / (sectors per track * (bytes per sector / 2))
253 	 */
254 	if (vi->ui_dk >= 0)
255 		dk_wpms[vi->ui_dk] =
256 		    (lp->d_rpm * lp->d_nsectors * lp->d_secsize) / 120;
257 #ifdef notyet
258 	addswap(makedev(HDMAJOR, hdminor(unit, 0)), lp);
259 #endif
260 }
261 
262 hdopen(dev, flags, fmt)
263 	dev_t dev;
264 	int flags, fmt;
265 {
266 	register struct disklabel *lp;
267 	register struct dksoftc *dk;
268 	register struct partition *pp;
269 	register int unit;
270 	struct vba_device *vi;
271 	int s, error, part = hdpart(dev), mask = 1 << part;
272 	daddr_t start, end;
273 
274 	unit = hdunit(dev);
275 	if (unit >= NHD || (vi = hddinfo[unit]) == 0 || vi->ui_alive == 0)
276 		return(ENXIO);
277 	dk = &dksoftc[unit];
278 	lp = &dk->dk_label;
279 	s = spl7();
280 	while (dk->dk_state != OPEN && dk->dk_state != OPENRAW &&
281 	    dk->dk_state != CLOSED)
282 		if (error = tsleep((caddr_t)dk, (PZERO+1) | PCATCH,
283 		    devopn, 0)) {
284 			splx(s);
285 			return (error);
286 		}
287 	splx(s);
288 	if (dk->dk_state != OPEN && dk->dk_state != OPENRAW)
289 		if (error = hdinit(dev, flags))
290 			return(error);
291 
292 	if (hdcwstart == 0) {
293 		timeout(hdcwatch, (caddr_t)0, hz);
294 		hdcwstart++;
295 	}
296 	/*
297 	 * Warn if a partion is opened that overlaps another partition
298 	 * which is open unless one is the "raw" partition (whole disk).
299 	 */
300 #define	RAWPART		8		/* 'x' partition */	/* XXX */
301 	if ((dk->dk_openpart & mask) == 0 && part != RAWPART) {
302 		pp = &lp->d_partitions[part];
303 		start = pp->p_offset;
304 		end = pp->p_offset + pp->p_size;
305 		for (pp = lp->d_partitions;
306 		     pp < &lp->d_partitions[lp->d_npartitions]; pp++) {
307 			if (pp->p_offset + pp->p_size <= start ||
308 			    pp->p_offset >= end)
309 				continue;
310 			if (pp - lp->d_partitions == RAWPART)
311 				continue;
312 			if (dk->dk_openpart & (1 << (pp - lp->d_partitions)))
313 				log(LOG_WARNING,
314 				    "hd%d%c: overlaps open partition (%c)\n",
315 				    unit, part + 'a',
316 				    pp - lp->d_partitions + 'a');
317 		}
318 	}
319 	if (part >= lp->d_npartitions)
320 		return(ENXIO);
321 	dk->dk_openpart |= mask;
322 	switch (fmt) {
323 	case S_IFCHR:
324 		dk->dk_copenpart |= mask;
325 		break;
326 	case S_IFBLK:
327 		dk->dk_bopenpart |= mask;
328 		break;
329 	}
330 	return(0);
331 }
332 
333 /* ARGSUSED */
334 hdclose(dev, flags, fmt)
335 	dev_t dev;
336 	int flags, fmt;
337 {
338 	register struct dksoftc *dk;
339 	int mask;
340 
341 	dk = &dksoftc[hdunit(dev)];
342 	mask = 1 << hdpart(dev);
343 	switch (fmt) {
344 	case S_IFCHR:
345 		dk->dk_copenpart &= ~mask;
346 		break;
347 	case S_IFBLK:
348 		dk->dk_bopenpart &= ~mask;
349 		break;
350 	}
351 	if (((dk->dk_copenpart | dk->dk_bopenpart) & mask) == 0)
352 		dk->dk_openpart &= ~mask;
353 	/*
354 	 * Should wait for i/o to complete on this partition
355 	 * even if others are open, but wait for work on blkflush().
356 	 */
357 	if (dk->dk_openpart == 0) {
358 		int s = spl7();
359 		while (dk->dk_utab.b_actf)
360 			sleep((caddr_t)dk, PZERO-1);
361 		splx(s);
362 		dk->dk_state = CLOSED;
363 		dk->dk_wlabel = 0;
364 	}
365 	return(0);
366 }
367 
368 hdinit(dev, flags)
369 	dev_t dev;
370 	int flags;
371 {
372 	register struct dksoftc *dk;
373 	register struct disklabel *lp;
374 	struct vba_device *vi;
375 	int error, unit;
376 	char *msg, *readdisklabel();
377 	extern int cold;
378 
379 	vi = hddinfo[unit = hdunit(dev)];
380 	dk = &dksoftc[unit];
381 	dk->dk_unit = vi->ui_slave;
382 	dk->dk_ctlr = vi->ui_ctlr;
383 
384 	if (flags & O_NDELAY) {
385 		dk->dk_state = OPENRAW;
386 		return(0);
387 	}
388 
389 	error = 0;
390 	lp = &dk->dk_label;
391 	dk->dk_state = RDLABEL;
392 	if (msg = readdisklabel(dev, hdstrategy, lp)) {
393 		if (cold) {
394 			printf(": %s\n", msg);
395 			dk->dk_state = CLOSED;
396 		} else {
397 			log(LOG_ERR, "hd%d: %s\n", unit, msg);
398 			dk->dk_state = OPENRAW;
399 		}
400 #ifdef COMPAT_42
401 		hdclock(vi->ui_ctlr);
402 		if (!(error = hdreadgeometry(dk)))
403 			dk->dk_state = OPEN;
404 		hdcunlock(vi->ui_ctlr);
405 #endif
406 	} else
407 		dk->dk_state = OPEN;
408 	wakeup((caddr_t)dk);
409 	return(error);
410 }
411 
412 hd_setsecsize(dk, lp)
413 	register struct dksoftc *dk;
414 	struct disklabel *lp;
415 {
416 	register int mul;
417 
418 	/*
419 	 * Calculate scaling shift for mapping
420 	 * DEV_BSIZE blocks to drive sectors.
421 	 */
422 	mul = DEV_BSIZE / lp->d_secsize;
423 	dk->dk_bshift = 0;
424 	while ((mul >>= 1) > 0)
425 		dk->dk_bshift++;
426 }
427 
428 /* ARGSUSED */
429 hddgo(vm)
430 	struct vba_device *vm;
431 {}
432 
433 extern int name_ext;
434 hdstrategy(bp)
435 	register struct buf *bp;
436 {
437 	register struct vba_device *vi;
438 	register struct disklabel *lp;
439 	register struct dksoftc *dk;
440 	struct buf *dp;
441 	register int unit;
442 	daddr_t sn, sz, maxsz;
443 	int part, s;
444 
445 	vi = hddinfo[unit = hdunit(bp->b_dev)];
446 	if (unit >= NHD || vi == 0 || vi->ui_alive == 0) {
447 		bp->b_error = ENXIO;
448 		goto bad;
449 	}
450 	dk = &dksoftc[unit];
451 	if (dk->dk_state < OPEN)
452 		goto q;
453 	if (dk->dk_state != OPEN && (bp->b_flags & B_READ) == 0) {
454 		bp->b_error = EROFS;
455 		goto bad;
456 	}
457 	part = hdpart(bp->b_dev);
458 	if ((dk->dk_openpart & (1 << part)) == 0) {
459 		bp->b_error = ENODEV;
460 		goto bad;
461 	}
462 	lp = &dk->dk_label;
463 	sz = (bp->b_bcount + lp->d_secsize - 1) / lp->d_secsize;
464 	maxsz = lp->d_partitions[part].p_size;
465 	sn = bp->b_blkno << dk->dk_bshift;
466 	if (sn + lp->d_partitions[part].p_offset <= LABELSECTOR &&
467 #if LABELSECTOR != 0
468 	    sn + lp->d_partitions[part].p_offset + sz > LABELSECTOR &&
469 #endif
470 	    (bp->b_flags & B_READ) == 0 && dk->dk_wlabel == 0) {
471 		bp->b_error = EROFS;
472 		goto bad;
473 	}
474 	if (sn < 0 || sn + sz > maxsz) {
475 		if (sn == maxsz) {
476 			bp->b_resid = bp->b_bcount;
477 			goto done;
478 		}
479 		sz = maxsz - sn;
480 		if (sz <= 0) {
481 			bp->b_error = EINVAL;
482 			goto bad;
483 		}
484 		bp->b_bcount = sz * lp->d_secsize;
485 	}
486 	bp->b_cylin = (sn + lp->d_partitions[part].p_offset) / lp->d_secpercyl;
487 
488 q:	s = spl7();
489 	dp = &dk->dk_utab;
490 	disksort(dp, bp);
491 	if (!dp->b_active) {
492 		(void)hdustart(vi);
493 		if (!vi->ui_mi->um_tab.b_active)
494 			hdcstart(vi->ui_mi);
495 	}
496 	splx(s);
497 	return;
498 bad:
499 	bp->b_flags |= B_ERROR;
500 done:
501 	biodone(bp);
502 }
503 
504 hdustart(vi)
505 	register struct vba_device *vi;
506 {
507 	register struct buf *bp, *dp;
508 	register struct vba_ctlr *vm;
509 	register struct dksoftc *dk;
510 
511 	dk = &dksoftc[vi->ui_unit];
512 	dp = &dk->dk_utab;
513 
514 	/* if queue empty, nothing to do.  impossible? */
515 	if (dp->b_actf == NULL)
516 		return;
517 
518 	/* place on controller transfer queue */
519 	vm = vi->ui_mi;
520 	if (vm->um_tab.b_actf == NULL)
521 		vm->um_tab.b_actf = dp;
522 	else
523 		vm->um_tab.b_actl->b_forw = dp;
524 	vm->um_tab.b_actl = dp;
525 	dp->b_forw = NULL;
526 	dp->b_active++;
527 }
528 
529 hdcstart(vm)
530 	register struct vba_ctlr *vm;
531 {
532 	register struct buf *bp;
533 	register struct dksoftc *dk;
534 	register struct disklabel *lp;
535 	register struct master_mcb *master;
536 	register struct mcb *mcb;
537 	struct vba_device *vi;
538 	struct hdcsoftc *hdc;
539 	struct buf *dp;
540 	int sn;
541 
542 	/* pull a request off the controller queue */
543 	for (;;) {
544 		if ((dp = vm->um_tab.b_actf) == NULL)
545 			return;
546 		if (bp = dp->b_actf)
547 			break;
548 		vm->um_tab.b_actf = dp->b_forw;
549 	}
550 
551 	/* mark controller active */
552 	vm->um_tab.b_active++;
553 
554 	vi = hddinfo[hdunit(bp->b_dev)];
555 	dk = &dksoftc[vi->ui_unit];
556 	lp = &dk->dk_label;
557 	sn = bp->b_blkno << dk->dk_bshift;
558 
559 	/* fill in mcb */
560 	mcb = &dk->dk_mcb;
561 	mcb->forw_phaddr = 0;
562 	/* mcb->priority = 0; */
563 	mcb->interrupt = 1;
564 	mcb->command = (bp->b_flags & B_READ) ? HCMD_READ:HCMD_WRITE;
565 	mcb->cyl = bp->b_cylin;
566 /* assumes partition starts on cylinder boundary */
567 	mcb->head = (sn / lp->d_nsectors) % lp->d_ntracks;
568 	mcb->sector = sn % lp->d_nsectors;
569 	mcb->drive = vi->ui_slave;
570 	/* mcb->context = 0;		/* what do we want on interrupt? */
571 
572 	hdc = &hdcsoftc[vm->um_ctlr];
573 	if (!hd_sgsetup(bp, &hdc->hdc_rbuf, mcb->chain)) {
574 		mcb->chain[0].wcount = (bp->b_bcount+3) >> 2;
575 		mcb->chain[0].memadr =
576 		    vbasetup(bp, &hdc->hdc_rbuf, (int)lp->d_secsize);
577 	}
578 
579 	if (vi->ui_dk >= 0) {
580 		dk_busy |= 1<<vi->ui_dk;
581 		dk_xfer[vi->ui_dk]++;
582 		dk_wds[vi->ui_dk] += bp->b_bcount>>6;
583 	}
584 
585 	master = &hdc->hdc_mcb;
586 	master->mcw = MCL_QUEUED;
587 	master->interrupt = HDCINTERRUPT + vm->um_ctlr;
588 	master->forw_phaddr = (u_long)vtoph((struct proc *)NULL, mcb);
589 	hdc->hdc_reg->master_mcb = (u_long)hdc->hdc_mcbp;
590 }
591 
592 /*
593  * Wait for controller to finish current operation
594  * so that direct controller accesses can be done.
595  */
596 hdclock(ctlr)
597 	int ctlr;
598 {
599 	register struct vba_ctlr *vm = hdcminfo[ctlr];
600 	register struct hdcsoftc *hdc;
601 	int s;
602 
603 	hdc = &hdcsoftc[ctlr];
604 	s = spl7();
605 	while (vm->um_tab.b_active || hdc->hdc_flags & HDC_LOCKED) {
606 		hdc->hdc_flags |= HDC_WAIT;
607 		sleep((caddr_t)hdc, PRIBIO);
608 	}
609 	hdc->hdc_flags |= HDC_LOCKED;
610 	splx(s);
611 }
612 
613 /*
614  * Continue normal operations after pausing for
615  * munging the controller directly.
616  */
617 hdcunlock(ctlr)
618 	int ctlr;
619 {
620 	register struct vba_ctlr *vm;
621 	register struct hdcsoftc *hdc = &hdcsoftc[ctlr];
622 
623 	hdc->hdc_flags &= ~HDC_LOCKED;
624 	if (hdc->hdc_flags & HDC_WAIT) {
625 		hdc->hdc_flags &= ~HDC_WAIT;
626 		wakeup((caddr_t)hdc);
627 	} else {
628 		vm = hdcminfo[ctlr];
629 		if (vm->um_tab.b_actf)
630 			hdcstart(vm);
631 	}
632 }
633 
634 hdintr(ctlr)
635 	int ctlr;
636 {
637 	register struct buf *bp, *dp;
638 	register struct vba_ctlr *vm;
639 	register struct vba_device *vi;
640 	register struct hdcsoftc *hdc;
641 	register struct mcb *mcb;
642 	struct master_mcb *master;
643 	register int status;
644 	int timedout;
645 	struct dksoftc *dk;
646 
647 	hdc = &hdcsoftc[ctlr];
648 	master = &hdc->hdc_mcb;
649 	uncache(&master->mcs);
650 	uncache(&master->context);
651 
652 	vm = hdcminfo[ctlr];
653 	if (!vm->um_tab.b_active || !(master->mcs&MCS_DONE)) {
654 		printf("hd%d: stray interrupt\n", ctlr);
655 		return;
656 	}
657 
658 	dp = vm->um_tab.b_actf;
659 	bp = dp->b_actf;
660 	vi = hddinfo[hdunit(bp->b_dev)];
661 	dk = &dksoftc[vi->ui_unit];
662 	if (vi->ui_dk >= 0)
663 		dk_busy &= ~(1<<vi->ui_dk);
664 	timedout = (hdc->hdc_wticks >= HDCMAXTIME);
665 
666 	mcb = &dk->dk_mcb;
667 
668 	if (master->mcs & (MCS_SOFTERROR | MCS_FATALERROR) || timedout)
669 		hdcerror(ctlr, *(u_long *)master->xstatus);
670 	else
671 		hdc->hdc_wticks = 0;
672 	if (vm->um_tab.b_active) {
673 		vm->um_tab.b_active = 0;
674 		vm->um_tab.b_actf = dp->b_forw;
675 		dp->b_active = 0;
676 		dp->b_errcnt = 0;
677 		dp->b_actf = bp->av_forw;
678 		bp->b_resid = 0;
679 		vbadone(bp, &hdc->hdc_rbuf);
680 		biodone(bp);
681 		/* start up now, if more work to do */
682 		if (dp->b_actf)
683 			hdustart(vi);
684 		else if (dk->dk_openpart == 0)
685 			wakeup((caddr_t)dk);
686 	}
687 	/* if there are devices ready to transfer, start the controller. */
688 	if (hdc->hdc_flags & HDC_WAIT) {
689 		hdc->hdc_flags &= ~HDC_WAIT;
690 		wakeup((caddr_t)hdc);
691 	} else if (vm->um_tab.b_actf)
692 		hdcstart(vm);
693 }
694 
695 hdioctl(dev, cmd, data, flag)
696 	dev_t dev;
697 	int cmd, flag;
698 	caddr_t data;
699 {
700 	register int unit;
701 	register struct dksoftc *dk;
702 	register struct disklabel *lp;
703 	int error;
704 
705 	unit = hdunit(dev);
706 	dk = &dksoftc[unit];
707 	lp = &dk->dk_label;
708 	error = 0;
709 	switch (cmd) {
710 	case DIOCGDINFO:
711 		*(struct disklabel *)data = *lp;
712 		break;
713 	case DIOCGPART:
714 		((struct partinfo *)data)->disklab = lp;
715 		((struct partinfo *)data)->part =
716 		    &lp->d_partitions[hdpart(dev)];
717 		break;
718 	case DIOCSDINFO:
719 		if ((flag & FWRITE) == 0)
720 			error = EBADF;
721 		else
722 			error = setdisklabel(lp, (struct disklabel *)data,
723 			    (dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart);
724 		if (error == 0 && dk->dk_state == OPENRAW)
725 			dk->dk_state = OPEN;
726 		break;
727 	case DIOCWLABEL:
728 		if ((flag & FWRITE) == 0)
729 			error = EBADF;
730 		else
731 			dk->dk_wlabel = *(int *)data;
732 		break;
733 	case DIOCWDINFO:
734 		if ((flag & FWRITE) == 0)
735 			error = EBADF;
736 		else if ((error = setdisklabel(lp, (struct disklabel *)data,
737 		    (dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart)) == 0) {
738 			int wlab;
739 
740 			if (error == 0 && dk->dk_state == OPENRAW)
741 				dk->dk_state = OPEN;
742 			/* simulate opening partition 0 so write succeeds */
743 			dk->dk_openpart |= (1 << 0);		/* XXX */
744 			wlab = dk->dk_wlabel;
745 			dk->dk_wlabel = 1;
746 			error = writedisklabel(dev, hdstrategy, lp);
747 			dk->dk_openpart = dk->dk_copenpart | dk->dk_bopenpart;
748 			dk->dk_wlabel = wlab;
749 		}
750 		break;
751 	default:
752 		error = ENOTTY;
753 		break;
754 	}
755 	return (error);
756 }
757 
758 /*
759  * Watch for lost interrupts.
760  */
761 hdcwatch()
762 {
763 	register struct hdcsoftc *hdc;
764 	register struct vba_ctlr **vmp;
765 	register int ctlr;
766 	int s;
767 
768 	timeout(hdcwatch, (caddr_t)0, hz);
769 	for (vmp = hdcminfo, hdc = hdcsoftc, ctlr = 0; ctlr < NHDC;
770 	    ++ctlr, ++vmp, ++hdc) {
771 		if (*vmp == 0 || (*vmp)->um_alive == 0)
772 			continue;
773 		s = spl7();
774 		if ((*vmp)->um_tab.b_active &&
775 		    hdc->hdc_wticks++ >= HDCMAXTIME) {
776 			printf("hd%d: lost interrupt\n", ctlr);
777 			hdintr(ctlr);
778 		}
779 		splx(s);
780 	}
781 }
782 
783 hddump(dev)
784 	dev_t dev;
785 {
786 	return(ENXIO);
787 }
788 
789 hdsize(dev)
790 	dev_t dev;
791 {
792 	register int unit = hdunit(dev);
793 	register struct dksoftc *dk;
794 	struct vba_device *vi;
795 	struct disklabel *lp;
796 
797 	if (unit >= NHD || (vi = hddinfo[unit]) == 0 || vi->ui_alive == 0 ||
798 	    (dk = &dksoftc[unit])->dk_state != OPEN)
799 		return (-1);
800 	lp = &dk->dk_label;
801 	return ((int)lp->d_partitions[hdpart(dev)].p_size >> dk->dk_bshift);
802 }
803 
804 hdimcb(dk)
805 	register struct dksoftc *dk;
806 {
807 	register struct master_mcb *master;
808 	register struct mcb *mcb;
809 	register struct hdcsoftc *hdc;
810 	int timeout;
811 
812 	/* fill in mcb */
813 	mcb = &dk->dk_mcb;
814 	mcb->interrupt = 0;
815 	mcb->forw_phaddr = 0;
816 	mcb->drive = dk->dk_unit;
817 
818 	hdc = &hdcsoftc[dk->dk_ctlr];
819 	master = &hdc->hdc_mcb;
820 
821 	/* fill in master mcb */
822 	master->mcw = MCL_IMMEDIATE;
823 	master->forw_phaddr = (u_long)vtoph((struct proc *)NULL, mcb);
824 	master->mcs = 0;
825 
826 	/* kick controller and wait */
827 	hdc->hdc_reg->master_mcb = (u_long)hdc->hdc_mcbp;
828 	for (timeout = 15000; timeout; --timeout) {
829 		DELAY(1000);
830 		mtpr(PADC, 0);
831 		if (master->mcs&MCS_FATALERROR) {
832 			printf("hdc%d: fatal error\n", dk->dk_ctlr);
833 			hdcerror(dk->dk_ctlr, *(u_long *)master->xstatus);
834 			return(1);
835 		}
836 		if (master->mcs&MCS_DONE)
837 			return(0);
838 	}
839 	printf("hdc%d: timed out\n", dk->dk_ctlr);
840 	return(1);
841 }
842 
843 hdcerror(ctlr, code)
844 	int ctlr;
845 	u_long code;
846 {
847 	printf("hd%d: error %lx\n", ctlr, code);
848 }
849 
850 #ifdef COMPAT_42
851 hdreadgeometry(dk)
852 	struct dksoftc *dk;
853 {
854 	static geometry_sector geometry;
855 	register struct mcb *mcb;
856 	register struct disklabel *lp;
857 	geometry_block *geo;
858 	int cnt;
859 
860 	/*
861 	 * Read the geometry block (at head = 0 sector = 0 of the drive
862 	 * definition cylinder), validate it (must have the correct version
863 	 * number, header, and checksum).
864 	 */
865 	mcb = &dk->dk_mcb;
866 	mcb->command = HCMD_READ;
867 	mcb->cyl = dk->dk_def_cyl;
868 	mcb->head = 0;
869 	mcb->sector = 0;
870 	mcb->chain[0].wcount = sizeof(geometry_sector) / sizeof(long);
871 	mcb->chain[0].memadr  = (u_long)vtoph((struct process *)0, &geometry);
872 	/* mcb->chain[0].memadr = (long)&geometry; */
873 	if (hdimcb(dk)) {
874  		printf("hd%d: can't read default geometry.\n", dk->dk_unit);
875 		return(1);
876 	}
877 	geo = &geometry.geometry_block;
878  	if (geo->version > 64000  ||  geo->version < 0) {
879  		printf("hd%d: bad default geometry version#.\n", dk->dk_unit);
880 		return(1);
881 	}
882  	if (bcmp(&geo->id[0], GB_ID, GB_ID_LEN)) {
883  		printf("hd%d: bad default geometry header.\n", dk->dk_unit);
884 		return(1);
885 	}
886 	GB_CHECKSUM(geo, cnt);
887 	if (geometry.checksum != cnt) {
888 		printf("hd%d: bad default geometry checksum.\n", dk->dk_unit);
889 		return(1);
890 	}
891 	lp = &dk->dk_label;
892 
893 	/* 1K block in Harris geometry; convert to sectors for disklabels */
894 	for (cnt = 0; cnt < GB_MAXPART; cnt++) {
895 		lp->d_partitions[cnt].p_offset =
896 		    geo->partition[cnt].start * (1024 / lp->d_secsize);
897 		lp->d_partitions[cnt].p_size =
898 		    geo->partition[cnt].length * (1024 / lp->d_secsize);
899 	}
900 	lp->d_npartitions = GB_MAXPART;
901 	return(0);
902 }
903 #endif /* COMPAT_42 */
904 #endif /* NHD */
905