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