/* * Charles H. Dickman 2001, 2002 * * All rights reserved. * * This code is available for all to use, so long as the * rights as enunciated by the University of California, Berkeley * below are also attributed to me. * In short, do with this as you will, but give credit where * credit is due. */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* TODO:peel out buffer at low ipl, speed improvement, rewrite to clean code from garbage artifacts */ #include "wd.h" #if NWD > 0 #include "param.h" #include "../machine/seg.h" #include "errno.h" #include "systm.h" #include "conf.h" #include "file.h" #include "stat.h" #include "ioctl.h" #include "disk.h" #include "disklabel.h" #include "buf.h" #include "wdreg.h" #include "syslog.h" #ifdef DEV_BSIZE #undef DEV_BSIZE #undef DEV_BSHIFT #endif #define DEV_BSIZE 512 #define DEV_BSHIFT 9 #define MAX_XFR 8192 /* maximum bytes to transfer */ #define wdctlr(dev) ((minor(dev) & 0300) >> 6) #define wdunit(dev) ((minor(dev) & 070) >> 3) #define wdpart(dev) ((minor(dev) & 007)) #ifdef b_cylin #undef b_cylin #endif #define b_cylin b_resid /* cylinder number for doing IO to */ /* shares an entry in the buf struct */ /* * The structure of a disk drive. */ struct wd_disk { daddr_t wd_nblks; short wd_bc; /* byte count left */ short wd_skip; /* blocks already transferred */ char wd_unit; /* physical unit number */ u_char wd_stat; /* copy of status reg. */ u_char wd_err; /* copy of error reg. */ u_char wd_reg[8]; /* copy of disk registers */ long wd_intfail; /* count of the number of interrupt enables */ short wd_open; /* open/closed refcnt */ struct dkdevice wd_dk; /* kernel resident part of label */ }; /* * Some shorthand for accessing the in-kernel label structure. */ #define wd_bopen wd_dk.dk_bopenmask #define wd_copen wd_dk.dk_copenmask #define wd_open wd_dk.dk_openmask #define wd_flags wd_dk.dk_flags #define wd_label wd_dk.dk_label #define wd_parts wd_dk.dk_parts struct wd_disk wddrives[NWD] = {0}; /* table of units */ struct buf wdtab = {0}; struct buf wdutab[NWD] = {0}; /* head of queue per drive */ long wdxfer[NWD] = {0}; /* count of transfers */ int wdprobe(), wdattach(), wdintr(); static int wdc; /* base IOpage address of adapter */ static int wdv; /* interrupt vector */ int cyloffset; #define WDASM #define NO_WDMACRO #define NO_WDINLINE #define WDIDDUMP #ifdef WDMACRO #define inb(p) (*(char *)p) #define inw(p) (*(short *)p) #else char inb(p) char *p; { return *p; } short inw(p) int *p; { return *p; } #endif outb(p,val) char *p; char val; { /* printf("wd: outb: %o -> %o\n", val, (int)p); */ *p = val; } outw(p,val) short *p; short val; { /* printf("wd: outw: %o -> %o\n", val, (int)p); */ *p = val; } insw(src, dst, cnt) register short *src; register short *dst; register int cnt; { #ifndef WDASM extern int wdc; while (cnt--) { if ((inb(wdc+wd_altsts) & WDCS_DRQ) == 0) printf("wd: insw: no DRQ count = %d\n", 255 - cnt); *dst++ = *src; } #else if (cnt == 0) return; /* nothing to move */ asm("mov 4(r5),r1"); asm("mov 6(r5),r2"); asm("mov 10(r5),r3"); asm("1:"); asm("mov (r1),(r2)+"); asm("sob r3,1b"); #endif } outsw(dst, src, cnt) register short *src; register short *dst; register int cnt; { #ifndef WDASM while (cnt--) *dst = *src++; #else if (cnt == 0) return; /* nothing to move */ asm("mov 6(r5),r1"); asm("mov 4(r5),r2"); asm("mov 10(r5),r3"); asm("1:"); asm("mov (r1)+,(r2)"); asm("sob r3,1b"); #endif } #ifdef WDINLINE #define wdwbusy() while ((inb(wdc+wd_altsts) & WDCS_BUSY) != 0) #define wdwready() while ((inb(wdc+wd_altsts) & WDCS_READY) == 0) #define wdwdrq() while ((inb(wdc+wd_altsts) & WDCS_DRQ) == 0) #else int wdwbusy() { extern int wdc; u_int d = 30000; while ((inb(wdc+wd_altsts) & WDCS_BUSY) != 0) if (d-- == 0) return 0; return 1; } int wdwready() { extern int wdc; u_int d = 30000; while ((inb(wdc+wd_altsts) & (WDCS_READY | WDCS_BUSY)) != WDCS_READY) if (d-- == 0) return 0; return 1; } int wdwdrq() { extern int wdc; char st; u_int d = 30000; while (1) { st = inb(wdc+wd_altsts); if ((st & (WDCS_BUSY | WDCS_READY | WDCS_DRQ)) == (WDCS_READY | WDCS_DRQ)) break; if ((st & (WDCS_BUSY | WDCS_ERR)) == WDCS_ERR) break; if (d-- == 0) return 0; } return (st); } #endif #define min(v1,v2) ((v1)<(v2) ? (v1) : (v2)) wdrdump() { int i; printf("wd dev register dump\n"); printf("wd_csr = %b\n", fioword(IOBASE + wd_csr), WDA_BITS); printf("wd_altsts = %b\n", fioword(IOBASE + wd_altsts), WDCS_BITS); printf("wd_error = %b\n", fioword(IOBASE + wd_error), WDERR_BITS); for (i = IOBASE; i < IOBASE + 040; i += 2) printf("%o = %o\n", i, fioword(i)); } wdbufdump(bp) struct buf *bp; { printf("wdbufdump: buf structure contents dump\n"); printf("bp = %o\n", bp); printf("b_flags = %b\n", bp->b_flags, "\010\020RAMREMAP\017UBAREMAP\ \016LOCKED\015BAD\014INVAL\013TAPE\012DELWRI\011ASYNC\010AGE\ \007WANTED\006MAP\005PHYS\004BUSY\003ERROR\002DONE\001READ"); printf("b_forw = %o, b_back = %o, av_forw = %o, av_back = %o\n", bp->b_forw, bp->b_back, bp->av_forw, bp->av_back); printf("b_bcount = %u, b_error = %o\n", bp->b_bcount, bp->b_error); printf("b_dev = %o, wdctlr = %d, wdunit = %d, wdpart = %d\n", bp->b_dev, wdctlr(bp->b_dev), wdunit(bp->b_dev), wdpart(bp->b_dev)); printf("b_xmem = %o, b_addr = %o, physaddr = %o\n", bp->b_xmem, bp->b_un.b_addr, (long)bp->b_xmem<<16 | (long)bp->b_un.b_addr); printf("bftopaddr(bp) = %o\n", bftopaddr(bp)); printf("b_blkno = %ld\n", (long)bp->b_blkno); } /* * Called from autoconfig and raroot() to set the vector for a controller. * It is an error to attempt to set the vector more than once except for * the first controller which may have had the vector set from raroot(). * In this case the error is ignored and the vector left unchanged. */ wdVec(ctlr, vector) int ctlr; int vector; { extern int wdc; if (ctlr != 0) return(-1); if (wdv != 0) { return(0); } wdv = vector; outw(wdc+wd_vector, vector); outw(wdc+wd_csr, WDA_IE); return(0); } wdroot(csr) int csr; { if (!csr) /* XXX */ csr = 0171000; /* XXX */ wdattach(csr, 0); wdVec(0, 0510); } /* * attach each drive if possible. */ wdattach(addr, unit) char *addr; int unit; { wdc = (int)addr; outb(wdc+wd_ctlr,12); /* reset */ DELAY(1000); outb(wdc+wd_ctlr,8); return(1); } #if 0 wdprobe(addr,vector) u_int *addr; int vector; { return (1); /* XXX assume all is well */ } #endif /* * Strings in the sector returned by the identify command are * in words stored in bigendian order. Returns a string with * the order corrected and trailing spaces removed. */ char * wdidstr(str, idsec, n) char *str; int *idsec; int n; { int i; for (i = 0; i < n; i += 2) { str[i] = idsec[i >> 1] >> 8; str[i + 1] = idsec[i >> 1]; } str[i] = '\0'; for (i -= 1; i >= 0; i--) if (str[i] == ' ') str[i] = '\0'; return (str); } #ifdef WDIDDUMP wdiddump(du, idsec) register struct wd_disk *du; int *idsec; { char str[64]; log(LOG_NOTICE, "wd%d: ", du->wd_unit); wdidstr(str, &idsec[27], 40); log(LOG_NOTICE, "%s ", (str[0]) ? str : "undefined"); if (idsec[49] & 0400) log(LOG_NOTICE, "size=%ld\n", ((long)idsec[61] << 16) + idsec[60]); else log(LOG_NOTICE, "LBA mode NOT supported!\n"); } #else wdiddump(du, idsec) register struct wd_disk *du; int *idsec; { char str[64]; printf("wd%d id results:\n", du->wd_unit); wdidstr(str, &idsec[27], 40); printf("Drive Model: %s\n", (str[0]) ? str : "undefined"); wdidstr(str, &idsec[23], 8); printf("Firmware Rev: %s\n", (str[0]) ? str : "undefined"); wdidstr(str, &idsec[10], 20); printf("Serial Number: %s\n", (str[0]) ? str : "undefined"); if (idsec[49] & 0400) { printf("total LBA addressable sectors: %ld\n", ((long)idsec[61] << 16) + idsec[60]); } else printf("ACHTUNG! LBA mode NOT supported.\n"); printf("CHS configuration: %d cyl, %d heads, %d sectors\n", idsec[1], idsec[3], idsec[6]); printf("total CHS addressable sectors: %ld\n", (long)idsec[1]*idsec[3]*idsec[6]); } #endif wdidentify(du, idsec) register struct wd_disk *du; int *idsec; { int st, ie; /* have drive execute identity command */ wdwbusy(); outb(wdc+wd_sdh, du->wd_unit << 4); wdwready(); /* disable interrupts from drive */ ie = inw(wdc+wd_csr); outw(wdc+wd_csr, ie & ~WDA_IE); outb(wdc+wd_command, WDCC_IDENTIFY); wdwbusy(); if ((st = inb(wdc+wd_altsts)) & WDCS_ERR) { log(LOG_NOTICE, "wd%d err: identify command: st = %o, er = %o\n", du->wd_unit, st, inb(wdc+wd_error)); } else { wdwdrq(); insw(wdc+wd_data, idsec, 256); } /* eat interrupt and restore interrupt enable state */ st = inb(wdc+wd_status); outw(wdc+wd_csr, ie); return (0); } void wddfltlbl(du, lp) register struct wd_disk *du; register struct disklabel *lp; { int idsec[256]; struct partition *pi = &lp->d_partitions[0]; bzero(lp, sizeof (*lp)); /* * NOTE: partition 0 ('a') is used to read the label. Therefore 'a' must * start at the beginning of the disk! If there is no label or the label * is corrupted then a label containing a geometry sufficient to read/write * sector 1 (LABELSECTOR) is created. */ lp->d_type = DTYPE_ST506; lp->d_secsize = 512; /* XXX */ lp->d_nsectors = LABELSECTOR + 1; /* # sectors/track */ lp->d_ntracks = 1; /* # tracks/cylinder */ lp->d_secpercyl = LABELSECTOR + 1; /* # sectors/cylinder */ lp->d_ncylinders = 1; /* # cylinders */ lp->d_npartitions = 1; /* 1 partition = 'a' */ lp->d_secperunit = lp->d_secpercyl*lp->d_ncylinders; if (wdidentify(du, idsec) == 0) { wdiddump(du, idsec); lp->d_nsectors = idsec[6]; lp->d_ntracks = idsec[3]; lp->d_secpercyl = idsec[6]*idsec[3]; lp->d_ncylinders = idsec[1]; lp->d_secperunit = ((long)idsec[61] << 16) + idsec[60]; } /* * Need to put the information where the driver expects it. This is normally * done after reading the label. Since we're creating a fake label we have to * copy the invented geometry information to the right place. */ du->wd_nblks = pi->p_size = lp->d_secperunit; pi->p_fstype = FS_V71K; pi->p_frag = 1; pi->p_fsize = 1024; bcopy(pi, du->wd_parts, sizeof (lp->d_partitions)); } void wdgetinfo(du, dev) register struct wd_disk *du; dev_t dev; { struct disklabel locallabel; char *msg; register struct disklabel *lp = &locallabel; extern wdstrategy(); wddfltlbl(du, lp); msg = readdisklabel((dev & ~7) | 0, wdstrategy, lp); /* 'a' */ if (msg != 0) { log(LOG_NOTICE, "wd%da using labelonly geometry: %s\n", wdunit(dev), msg); wddfltlbl(du, lp); } mapseg5(du->wd_label, LABELDESC); bcopy(lp, (struct disklabel *)SEG5, sizeof (struct disklabel)); normalseg5(); bcopy(lp->d_partitions, du->wd_parts, sizeof (lp->d_partitions)); return; } /* Read/write routine for a buffer. Finds the proper unit, range checks * arguments, and schedules the transfer. Does not wait for the transfer * to complete. Multi-page transfers are supported. All I/O requests must * be a multiple of a sector in length. */ wdstrategy(bp) register struct buf *bp; /* IO operation to perform */ { register struct buf *dp; register struct wd_disk *du; /* Disk unit to do the IO. */ register struct partition *p; int unit; int s; unit = wdunit(bp->b_dev); du = &wddrives[unit]; if (unit >= NWD || !wdc) { bp->b_error = ENXIO; goto bad; } /* * Determine the size of the transfer, and make sure it is * within the boundaries of the partition. */ s = partition_check(bp, &du->wd_dk); if (s < 0) goto bad; if (s == 0) goto done; p = &du->wd_parts[wdpart(bp->b_dev)]; bp->b_cylin = ((bp->b_blkno + p->p_offset) >> 12); dp = &wdutab[unit]; s = splbio(); disksort(dp, bp); if (dp->b_active == 0) wdustart(du); /* start drive if idle */ if (wdtab.b_active == 0) wdstart(); /* start IO if controller idle */ splx(s); return; bad: bp->b_flags |= B_ERROR; done: iodone(bp); return; } /* Routine to queue a read or write command to the controller. The request is * linked into the active list for the controller. If the controller is idle, * the transfer is started. */ wdustart(du) register struct wd_disk *du; { register struct buf *bp, *dp; dp = &wdutab[du->wd_unit]; if (dp->b_active) return; bp = dp->b_actf; if (bp == NULL) return; dp->b_forw = NULL; if (wdtab.b_actf == NULL) /* link unit into active list */ wdtab.b_actf = dp; else wdtab.b_actl->b_forw = dp; wdtab.b_actl = dp; dp->b_active++; /* mark the drive as busy */ } /* * Controller startup routine. This does the calculation, and starts * a single-sector read or write operation. Called to start a transfer, * or from the interrupt routine to continue a multi-sector transfer. * * RESTRICTIONS: * 1. The transfer length must be an exact multiple of the sector size. */ wdstart() { register struct wd_disk *du; /* disk unit for IO */ register struct buf *bp; struct buf *dp; daddr_t blknum; int addr, i; int unit, s; segm saveregs; int paddr; int stat; loop: dp = wdtab.b_actf; if (dp == NULL) return; bp = dp->b_actf; if (bp == NULL) { wdtab.b_actf = dp->b_forw; goto loop; } unit = wdunit(bp->b_dev); du = &wddrives[unit]; if (wdtab.b_active == 0) { wdtab.b_active++; /* mark controller active */ blknum = (unsigned long) bp->b_blkno + du->wd_skip + du->wd_parts[wdpart(bp->b_dev)].p_offset; du->wd_bc = min(bp->b_bcount, MAX_XFR); bp->b_resid = bp->b_bcount - du->wd_bc; if(!(stat = wdwbusy())) { log(LOG_NOTICE, "wd: timeout waiting for BSY\n"); error: bp->b_resid = bp->b_bcount; bp->b_flags |= B_ERROR; biodone(bp); return; } outb(wdc+wd_sdh, du->wd_reg[4] = WD_LBA | (unit << 4) | (int)(blknum >> 24) & 017); if(!(stat = wdwready())) { log(LOG_NOTICE, "wd: timeout waiting for DRDY\n"); goto error; }; outb(wdc+wd_seccnt, du->wd_reg[0] = (du->wd_bc + DEV_BSIZE - 1) >> DEV_BSHIFT); outb(wdc+wd_sector, du->wd_reg[1] = blknum); outb(wdc+wd_cyl_lo, du->wd_reg[2] = (blknum >> 8)); outb(wdc+wd_cyl_hi, du->wd_reg[3] = (blknum >> 16)); du->wd_reg[5] = 0; outb(wdc+wd_command, du->wd_reg[5] = (bp->b_flags & B_READ) ? WDCC_READ : WDCC_WRITE); } if ((inw(wdc+wd_csr) & 0100) == 0) { du->wd_intfail++; outw(wdc+wd_csr, 0100); } /* If this is a read operation, just go away until it's done. */ if (bp->b_flags & B_READ) return; /* Ready to send data? */ if(!(stat = wdwdrq())) { log(LOG_NOTICE, "wd: timeout waiting for DRQ\n"); goto error; } if (stat & WDCS_ERR) { log(LOG_NOTICE, "wd: write error st = %o er = %o\n", inb(wdc+wd_altsts), inb(wdc+wd_error)); wdbufdump(bp); goto error; } /* ASSUMES CONTIGUOUS MEMORY */ saveseg5(saveregs); addr = (int)mapin(bp); *KDSD5 = 077406; /* XXXXX buffer is set to 8k */ outsw (wdc+wd_data, addr+du->wd_skip*DEV_BSIZE, DEV_BSIZE/2); mapout(bp); restorseg5(saveregs); du->wd_bc -= DEV_BSIZE; } /* * these are globally defined so they can be found * by the debugger easily in the case of a system crash */ daddr_t wd_errbn; unsigned char wd_errstat; /* Interrupt routine for the controller. Acknowledge the interrupt, check for * errors on the current operation, mark it done if necessary, and start * the next request. Also check for a partially done transfer, and * continue with the next chunk if so. */ wdintr(unit) { register struct wd_disk *du; register struct buf *bp, *dp; int status; char partch; segm saveregs; int paddr; int addr; /* Shouldn't need this, but it may be a slow controller. */ if(!wdwbusy()) { log(LOG_NOTICE, "wd: timeout waiting for BSY\n"); bp->b_flags |= B_ERROR; goto done; } status = inb(wdc+wd_status); /* clear drive interrupt */ if (!wdtab.b_active) { log(LOG_NOTICE, "wd: extra interrupt\n"); return; } dp = wdtab.b_actf; bp = dp->b_actf; du = &wddrives[wdunit(bp->b_dev)]; partch = wdpart(bp->b_dev) + 'a'; if (status & WDCS_ERR) { wd_errbn = bp->b_blkno + du->wd_skip + du->wd_parts[wdpart(bp->b_dev)].p_offset; printf("wd%d%c: ", du->wd_unit, partch); printf("hard %s error ", (bp->b_flags & B_READ)? "read":"write"); printf("bn %ld\n", wd_errbn); wdrdump(); bp->b_flags |= B_ERROR; /* flag the error */ } /* * If this was a successful read operation, fetch the data. */ if ((bp->b_flags & (B_READ | B_ERROR)) == B_READ) { int chk, dummy; chk = min(DEV_BSIZE/2, du->wd_bc/2); /* Ready to send data? */ if(!wdwdrq()) { log(LOG_NOTICE, "wd: timeout waiting for DRQ\n"); bp->b_flags |= B_ERROR; goto done; } saveseg5(saveregs); addr = (int)mapin(bp); *KDSD5 = 077406; /* XXXXXXX setting buffer size to 8k */ insw(wdc+wd_data, addr + du->wd_skip*DEV_BSIZE, chk); restorseg5(saveregs); du->wd_bc -= 2*chk; while (chk++ < DEV_BSIZE/2) dummy = inw(wdc+wd_data); } wdxfer[du->wd_unit]++; if ((bp->b_flags & B_ERROR) == 0) { du->wd_skip++; /* Add to successful sectors. */ /* see if more to transfer */ if (du->wd_bc > 0) { wdstart(); return; /* next chunk is started */ } } /* done with this transfer, with or without error */ done: wdtab.b_actf = dp->b_forw; wdtab.b_errcnt = 0; du->wd_skip = 0; dp->b_active = 0; dp->b_actf = bp->av_forw; dp->b_errcnt = 0; bp->b_resid += du->wd_bc; biodone(bp); wdtab.b_active = 0; if (dp->b_actf) wdustart(du); /* requeue disk if more io to do */ if (wdtab.b_actf) wdstart(); /* start IO on next drive */ } /* * Initialize a drive. */ wdopen(dev, flags, fmt) dev_t dev; int flags, fmt; { register unsigned int unit; register struct buf *bp; register struct wd_disk *du; int part = wdpart(dev), mask = 1 << part; struct partition *pp; int i, error = 0; unit = wdunit(dev); if (unit >= NWD) return (ENXIO); du = &wddrives[unit]; du->wd_unit = unit; /* Wait for pending opens/closes */ while (du->wd_flags & (DKF_OPENING | DKF_CLOSING)) sleep(du, PRIBIO); if (du->wd_label == 0) du->wd_label = disklabelalloc(); /* * On first open get label and partition info. * We may block reading the label so be careful to stop * any other opens. */ if (du->wd_open == 0) { du->wd_flags |= DKF_OPENING; wdgetinfo(du, dev); du->wd_flags &= ~DKF_OPENING; wakeup(du); } /* * Need to make sure the partition is not out of bounds. This requires * mapping in the external label. This only happens when a partition * is opened (at mount time) and isn't an efficiency problem. */ mapseg5(du->wd_label, LABELDESC); i = ((struct disklabel *)SEG5)->d_npartitions; normalseg5(); if (part >= i) return (ENXIO); dkoverlapchk(du->wd_open, dev, du->wd_label, "wd"); du->wd_open |= mask; switch (fmt) { case S_IFCHR: du->wd_copen |= mask; break; case S_IFBLK: du->wd_bopen |= mask; break; } return (0); } /* ARGSUSED */ wdclose(dev, flags, mode) dev_t dev; int flags, mode; { register struct wd_disk *du; int mask, s; int unit = wdunit(dev); du = &wddrives[unit]; if (du->wd_intfail) log(LOG_NOTICE, "wd%d: intfail = %ld\n", unit, du->wd_intfail); mask = 1 << wdpart(dev); if (mode == S_IFCHR) du->wd_copen &= ~mask; else if (mode == S_IFBLK) du->wd_bopen &= ~mask; else return(EINVAL); du->wd_open = du->wd_copen | du->wd_bopen; if (du->wd_open == 0) { du->wd_flags |= DKF_CLOSING; s = splbio(); while (wdutab[unit].b_actf) { du->wd_flags |= DKF_WANTED; sleep(&wdutab[unit], PRIBIO); } splx(s); du->wd_flags &= ~(DKF_CLOSING | DKF_WANTED); wakeup(du); } return(0); } wdioctl(dev,cmd,addr,flag) dev_t dev; int cmd; caddr_t addr; int flag; { int error = 0; struct dkdevice *disk; disk = &wddrives[wdunit(dev)].wd_dk; error = ioctldisklabel(dev, cmd, addr, flag, disk, wdstrategy); return (error); } wdsize(dev) dev_t dev; { int unit = wdunit(dev); int part = wdpart(dev); struct wd_disk *du = &wddrives[unit]; int val; if (unit >= NWD) return(-1); if (du->wd_open == 0) { val = wdopen (dev, 0, 0); if (val < 0) { printf("wdsize: wd%d%c: error opening device\n", unit, 'a'+part); return (-1); } } val = (u_long)du->wd_parts[part].p_size; wdclose(dev, 0, 0); printf("wdsize: wd%d%c: size = %d\n", unit, 'a'+part, val); return (val); } extern char *vmmap; /* poor name! */ #if 0 wddump(dev) /* dump core after a system crash */ dev_t dev; { register struct wd_disk *du; /* disk unit to do the IO */ register struct bt_bad *bt_ptr; long num; /* number of sectors to write */ int unit, part; long cyloff, blknum, blkcnt; long cylin, head, sector, stat; long secpertrk, secpercyl, nblocks, i; char *addr; extern int Maxmem; static wddoingadump = 0 ; extern CMAP1; extern char CADDR1[]; #ifdef ARGO outb(0x461,0); /* disable failsafe timer */ #endif addr = (char *) 0; /* starting address */ /* size of memory to dump */ num = Maxmem; unit = wdunit(dev); /* eventually support floppies? */ part = wdpart(dev); /* file system */ /* check for acceptable drive number */ if (unit >= NWD) return(ENXIO); du = &wddrives[unit]; /* was it ever initialized ? */ if (du->wd_state < OPEN) return (ENXIO) ; /* Convert to disk sectors */ num = (u_long) num * NBPG / du->wd_dd.d_secsize; /* check if controller active */ /*if (wdtab.b_active) return(EFAULT); */ if (wddoingadump) return(EFAULT); secpertrk = du->wd_dd.d_nsectors; secpercyl = du->wd_dd.d_secpercyl; nblocks = du->wd_dd.d_partitions[part].p_size; cyloff = du->wd_dd.d_partitions[part].p_offset / secpercyl; /*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/ /* check transfer bounds against partition size */ if ((dumplo < 0) || ((dumplo + num) > nblocks)) return(EINVAL); /*wdtab.b_active = 1; /* mark controller active for if we panic during the dump */ wddoingadump = 1 ; i = 100000 ; while ((inb(wdc+wd_altsts) & WDCS_BUSY) && (i-- > 0)) ; outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); while (inb(wdc+wd_altsts) & WDCS_BUSY) ; /* some compaq controllers require this ... */ wdsetctlr(dev, du); blknum = dumplo; while (num > 0) { #ifdef notdef if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER; if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl) blkcnt = secpercyl - (blknum % secpercyl); /* keep transfer within current cylinder */ #endif pmap_enter(kernel_pmap, vmmap, addr, VM_PROT_READ, TRUE); /* compute disk address */ cylin = blknum / secpercyl; head = (blknum % secpercyl) / secpertrk; sector = blknum % secpertrk; cylin += cyloff; #ifdef notyet /* * See if the current block is in the bad block list. * (If we have one.) */ for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) { if (bt_ptr->bt_cyl > cylin) /* Sorted list, and we passed our cylinder. quit. */ break; if (bt_ptr->bt_cyl == cylin && bt_ptr->bt_trksec == (head << 8) + sector) { /* * Found bad block. Calculate new block addr. * This starts at the end of the disk (skip the * last track which is used for the bad block list), * and works backwards to the front of the disk. */ blknum = (du->wd_dd.d_secperunit) - du->wd_dd.d_nsectors - (bt_ptr - dkbad[unit].bt_bad) - 1; cylin = blknum / secpercyl; head = (blknum % secpercyl) / secpertrk; sector = blknum % secpertrk; break; } #endif sector++; /* origin 1 */ /* select drive. */ outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf)); wdwready(); /* transfer some blocks */ outb(wdc+wd_sector, sector); outb(wdc+wd_seccnt,1); outb(wdc+wd_cyl_lo, cylin); outb(wdc+wd_cyl_hi, cylin >> 8); #ifdef notdef /* lets just talk about this first...*/ pg ("sdh 0%o sector %d cyl %d addr 0x%x", inb(wdc+wd_sdh), inb(wdc+wd_sector), inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr) ; #endif #ifdef ODYSSEUS if(cylin < 46 || cylin > 91)pg("oops"); #endif #ifdef PRIAM if(cylin < 40 || cylin > 79)pg("oops"); #endif outb(wdc+wd_command, WDCC_WRITE); /* Ready to send data? */ wdwdrq(); if (inb(wdc+wd_altsts) & WDCS_ERR) return(EIO) ; outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256); (int) addr += 512; if (inb(wdc+wd_altsts) & WDCS_ERR) return(EIO) ; /* Check data request (should be done). */ if (inb(wdc+wd_altsts) & WDCS_DRQ) return(EIO) ; /* wait for completion */ for ( i = 1000000 ; inb(wdc+wd_altsts) & WDCS_BUSY ; i--) { if (i < 0) return (EIO) ; } /* error check the xfer */ if (inb(wdc+wd_altsts) & WDCS_ERR) return(EIO) ; /* update block count */ num--; blknum++ ; if (num % 100 == 0) printf(".") ; } return(0); } #else wddump(dev) dev_t dev; { printf("wddump: nothing here \n"); } #endif #endif