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