diff --git a/sys/i386/isa/wd.c b/sys/i386/isa/wd.c index 170300039359..79a136ba55d3 100644 --- a/sys/i386/isa/wd.c +++ b/sys/i386/isa/wd.c @@ -1,5 +1,3 @@ -static int wdtest = 0; - /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. @@ -35,8 +33,29 @@ static int wdtest = 0; * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * from: @(#)wd.c 7.2 (Berkeley) 5/9/91 - * $Id$ + * from:@(#)wd.c 7.2 (Berkeley) 5/9/91 + * + * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE + * -------------------- ----- ---------------------- + * CURRENT PATCH LEVEL: 6 00155 + * -------------------- ----- ---------------------- + * + * 17 Sep 92 Frank Maclachlan Fixed I/O error reporting on raw device + * 31 Jul 92 Christoph Robitschko Fixed second disk recognition, + * bzero of malloced memory for warm + * boot problem. + * 19 Aug 92 Frank Maclachlan Fixed bug when first sector of a + * multisector read is in bad144 table. + * 17 Jan 93 B. Evans & A.Chernov Fixed bugs from previous patches, + * driver initialization, and cylinder + * boundary conditions. + * 28 Mar 93 Charles Hannum Add missing splx calls. + * 20 Apr 93 Terry Lee Always report disk errors + * 20 Apr 93 Brett Lymn Change infinite while loops to + * timeouts + * 17 May 93 Rodney W. Grimes Fixed all 1000000 to use WDCTIMEOUT, + * and increased to 1000000*10 for new + * intr-0.1 code. */ /* TODO:peel out buffer at low ipl, speed improvement */ @@ -48,7 +67,6 @@ static int wdtest = 0; #include "param.h" #include "dkbad.h" #include "systm.h" -#include "kernel.h" #include "conf.h" #include "file.h" #include "stat.h" @@ -72,7 +90,6 @@ static int wdtest = 0; #endif #define RETRIES 5 /* number of retries before giving up */ -#define RECOVERYTIME 500000 /* usec for controller to recover after err */ #define MAXTRANSFER 32 /* max size of transfer in page clusters */ #define wdnoreloc(dev) (minor(dev) & 0x80) /* ignore partition table */ @@ -105,19 +122,19 @@ struct disk { u_char dk_status; /* copy of status reg. */ u_char dk_error; /* copy of error reg. */ short dk_port; /* i/o port base */ - + u_long dk_copenpart; /* character units open on this drive */ u_long dk_bopenpart; /* block units open on this drive */ u_long dk_openpart; /* all units open on this drive */ short dk_wlabel; /* label writable? */ short dk_flags; /* drive characteistics found */ #define DKFL_DOSPART 0x00001 /* has DOS partition table */ +#define DKFL_QUIET 0x00002 /* report errors back, but don't complain */ #define DKFL_SINGLE 0x00004 /* sector at a time mode */ #define DKFL_ERROR 0x00008 /* processing a disk error */ #define DKFL_BSDLABEL 0x00010 /* has a BSD disk label */ #define DKFL_BADSECT 0x00020 /* has a bad144 badsector table */ #define DKFL_WRITEPROT 0x00040 /* manual unit write protect */ -#define DKFL_LABELLING 0x00080 /* readdisklabel() in progress */ struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */ struct disklabel dk_dd; /* device configuration data */ struct dos_partition @@ -125,42 +142,33 @@ struct disk { struct dkbad dk_bad; /* bad sector table */ }; -static struct disk *wddrives[_NWD]; /* table of units */ -static struct buf wdtab; -static struct buf wdutab[_NWD]; /* head of queue per drive */ -#ifdef notyet -static struct buf rwdbuf[_NWD]; /* buffers for raw IO */ +struct disk *wddrives[_NWD]; /* table of units */ +struct buf wdtab; +struct buf wdutab[_NWD]; /* head of queue per drive */ +struct buf rwdbuf[_NWD]; /* buffers for raw IO */ +long wdxfer[_NWD]; /* count of transfers */ +#ifdef WDDEBUG +int wddebug; #endif -static long wdxfer[_NWD]; /* count of transfers */ - -static int wdprobe(struct isa_device *dvp); -static int wdattach(struct isa_device *dvp); -static void wdustart(struct disk *du); -static void wdstart(void); -static int wdcontrol(struct buf *bp); -static int wdcommand(struct disk *du, u_int cylinder, u_int head, - u_int sector, u_int count, u_int command); -static int wdsetctlr(struct disk *du); -static int wdwsetctlr(struct disk *du); -static int wdgetctlr(struct disk *du); -static void wderror(struct buf *bp, struct disk *du, char *mesg); -static int wdreset(struct disk *du); -static void wdsleep(char *wmesg); -static int wdunwedge(struct disk *du); -static int wdwait(struct disk *du, u_char bits_wanted); struct isa_driver wddriver = { wdprobe, wdattach, "wd", }; +static void wdustart(struct disk *); +static void wdstart(); +static int wdcommand(struct disk *, int); +static int wdcontrol(struct buf *); +static int wdsetctlr(dev_t, struct disk *); +static int wdgetctlr(int, struct disk *); + /* * Probe for controller. */ -static int +int wdprobe(struct isa_device *dvp) { int unit = dvp->id_unit; - int u; struct disk *du; int wdc; @@ -175,31 +183,24 @@ wdprobe(struct isa_device *dvp) } wdc = du->dk_port = dvp->id_iobase; - - /* - * Skip probing and resetting the controller if it has already - * been successfully done. - * XXX - should finish probing for drives (using wdgetctlr) or - * only probe for controllers. - */ - for (u = 0; u < _NWD; u++) - if (u != unit && wddrives[u] != NULL - && wddrives[u]->dk_port == wdc) - return (IO_WDCSIZE); - + /* check if we have registers that work */ outb(wdc+wd_cyl_lo, 0xa5) ; /* wd_cyl_lo is read/write */ if(inb(wdc+wd_cyl_lo) != 0xa5) goto nodevice; - if (wdreset(du) != 0 && (DELAY(RECOVERYTIME), wdreset(du)) != 0) - goto nodevice; + /* reset the device */ + outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS)); + DELAY(1000); + outb(wdc+wd_ctlr, WDCTL_IDS); + DELAY(1000); /* execute a controller only command */ - if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0 - || wdwait(du, 0) != 0) + if (wdcommand(du, WDCC_DIAGNOSE) < 0) goto nodevice; + (void) inb(wdc+wd_error); /* XXX! */ + outb(wdc+wd_ctlr, WDCTL_4BIT); return (IO_WDCSIZE); nodevice: @@ -211,10 +212,11 @@ wdprobe(struct isa_device *dvp) /* * Attach each drive if possible. */ -static int +int wdattach(struct isa_device *dvp) { int unit; +/* int unit = dvp->id_unit;*/ for (unit=0; unit< _NWD; unit++) { struct disk *du; @@ -227,9 +229,9 @@ wdattach(struct isa_device *dvp) } /* print out description of drive, suppressing multiple blanks*/ - if (wdgetctlr(du) == 0) { + if(wdgetctlr(unit, du) == 0) { int i, blank; - + char c; printf("wd%d: unit %d type ", unit, unit); for (i = blank = 0 ; i < sizeof(du->dk_params.wdp_model); i++) { char c = du->dk_params.wdp_model[i]; @@ -246,10 +248,7 @@ wdattach(struct isa_device *dvp) printf("%c", c); } printf("\n"); - } - else { - free(du, M_TEMP); - wddrives[unit] = 0; + du->dk_unit = unit; } } return(1); @@ -260,11 +259,14 @@ wdattach(struct isa_device *dvp) * to complete. Multi-page transfers are supported. All I/O requests must * be a multiple of a sector in length. */ -void +int wdstrategy(register struct buf *bp) { register struct buf *dp; + struct disklabel *lp; + register struct partition *p; struct disk *du; /* Disk unit to do the IO. */ + long maxsz, sz; int unit = wdunit(bp->b_dev); int s; @@ -295,21 +297,15 @@ wdstrategy(register struct buf *bp) /* otherwise, process transfer request */ } +q: /* queue transfer on drive, activate drive and controller if idle */ dp = &wdutab[unit]; s = splbio(); disksort(dp, bp); if (dp->b_active == 0) wdustart(du); /* start drive */ - - /* Pick up changes made by readdisklabel(). */ - if (du->dk_flags & DKFL_LABELLING && du->dk_state > RECAL) { - wdsleep("wdlab"); - du->dk_state = WANTOPEN; - } - if (wdtab.b_active == 0) - wdstart(); /* start controller */ + wdstart(s); /* start controller */ splx(s); return; @@ -335,7 +331,7 @@ wdustart(register struct disk *du) /* anything to start? */ bp = dp->b_actf; if (bp == NULL) - return; + return; /* link onto controller queue */ dp->b_forw = NULL; @@ -358,16 +354,16 @@ wdustart(register struct disk *du) */ static void -wdstart(void) +wdstart() { register struct disk *du; /* disk unit for IO */ register struct buf *bp; struct disklabel *lp; struct buf *dp; register struct bt_bad *bt_ptr; - long blknum, cylin, head, sector; - long secpertrk, secpercyl, addr; - int unit, wdc; + long blknum, pagcnt, cylin, head, sector; + long secpertrk, secpercyl, addr, i, timeout; + int unit, s, wdc; loop: /* is there a drive for the controller to do a transfer with? */ @@ -386,24 +382,19 @@ wdstart(void) /* obtain controller and drive information */ unit = wdunit(bp->b_dev); du = wddrives[unit]; - wdc = du->dk_port; /* if not really a transfer, do control operations specially */ if (du->dk_state < OPEN) { - if (du->dk_state != WANTOPEN) - printf("wd%d: wdstart: weird dk_state %d\n", - du->dk_unit, du->dk_state); - if (wdcontrol(bp) != 0) - printf("wd%d: wdstart: wdcontrol returned nonzero, state = %d\n", - du->dk_unit, du->dk_state); + (void) wdcontrol(bp); return; } /* calculate transfer details */ blknum = bp->b_blkno + du->dk_skip; +/*if(wddebug)printf("bn%d ", blknum);*/ #ifdef WDDEBUG if (du->dk_skip == 0) - printf("wd%d: wdstart: %s %d@%d; map ", unit, + printf("\nwdstart %d: %s %d@%d; map ", unit, (bp->b_flags & B_READ) ? "read" : "write", bp->b_bcount, blknum); else @@ -428,8 +419,14 @@ wdstart(void) */ if ((du->dk_flags & (DKFL_SINGLE|DKFL_BADSECT)) /* 19 Aug 92*/ == (DKFL_SINGLE|DKFL_BADSECT)) - for (bt_ptr = du->dk_bad.bt_bad; bt_ptr->bt_cyl != 0xffff; - bt_ptr++) { + /* XXX + * BAD144END was done to clean up some old bad code that was + * attempting to compare a u_short to -1. This makes the compilers + * happy and clearly shows what is going on. + * rgrimes 93/06/17 + */ +#define BAD144END (u_short)(-1) + for (bt_ptr = du->dk_bad.bt_bad; bt_ptr->bt_cyl != BAD144END; bt_ptr++) { if (bt_ptr->bt_cyl > cylin) /* Sorted list, and we passed our cylinder. quit. */ break; @@ -445,74 +442,97 @@ wdstart(void) printf("--- badblock code -> Old = %d; ", blknum); #endif - - /* - * XXX the offset of the bad sector table ought - * to be stored in the in-core copy of the table. - */ -#define BAD144_PART 2 /* XXX scattered magic numbers */ -#define BSD_PART 0 /* XXX should be 2 but bad144.c uses 0 */ - if (lp->d_partitions[BSD_PART].p_offset != 0) - blknum = lp->d_partitions[BAD144_PART].p_offset - + lp->d_partitions[BAD144_PART].p_size; - else - blknum = lp->d_secperunit; - blknum -= lp->d_nsectors + (bt_ptr - du->dk_bad.bt_bad) - + 1; - + blknum = lp->d_secperunit - lp->d_nsectors + - (bt_ptr - du->dk_bad.bt_bad) - 1; cylin = blknum / secpercyl; head = (blknum % secpercyl) / secpertrk; sector = blknum % secpertrk; #ifdef WDDEBUG - printf("new = %d\n", blknum); + printf("new = %d\n", blknum); #endif break; } } +/*if(wddebug)pg("c%d h%d s%d ", cylin, head, sector);*/ + sector += 1; /* sectors begin with 1, not 0 */ wdtab.b_active = 1; /* mark controller active */ + wdc = du->dk_port; +RETRY: /* if starting a multisector transfer, or doing single transfers */ if (du->dk_skip == 0 || (du->dk_flags & DKFL_SINGLE)) { - int command; - u_int count; - if (wdtab.b_errcnt && (bp->b_flags & B_READ) == 0) du->dk_bc += DEV_BSIZE; -#ifdef B_FORMAT - if (bp->b_flags & B_FORMAT) { - command = WDCC_FORMAT; - count = lp->d_nsectors; - sector = lp->d_gap3 - 1; /* + 1 later */ - } else -#endif + /* controller idle? */ + timeout = 0; + while (inb(wdc+wd_status) & WDCS_BUSY) { - if (du->dk_flags & DKFL_SINGLE) - count = 1; - else - count = howmany(du->dk_bc, DEV_BSIZE); - command = (bp->b_flags & B_READ) ? WDCC_READ : WDCC_WRITE; - } - - /* - * XXX this loop may never terminate. The code to handle - * counting down of retries and eventually failing the i/o is - * in wdintr() and we can't get there from here. - */ - if (wdtest != 0) { - if (--wdtest == 0) { - wdtest = 100; - printf("dummy wdunwedge\n"); - wdunwedge(du); + if (++timeout > WDCTIMEOUT) + { + printf("wd.c: Controller busy too long!\n"); + /* reset the device */ + outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS)); + DELAY(1000); + outb(wdc+wd_ctlr, WDCTL_IDS); + DELAY(1000); + (void) inb(wdc+wd_error); /* XXX! */ + outb(wdc+wd_ctlr, WDCTL_4BIT); + break; } } - while (wdcommand(du, cylin, head, sector, count, command) != 0) - { - wderror(bp, du, - "wdstart: timeout waiting to send command"); - wdunwedge(du); + + /* stuff the task file */ + outb(wdc+wd_precomp, lp->d_precompcyl / 4); +#ifdef B_FORMAT + if (bp->b_flags & B_FORMAT) { + outb(wdc+wd_sector, lp->d_gap3); + outb(wdc+wd_seccnt, lp->d_nsectors); + } else { +#endif + if (du->dk_flags & DKFL_SINGLE) + outb(wdc+wd_seccnt, 1); + else + outb(wdc+wd_seccnt, howmany(du->dk_bc, DEV_BSIZE)); + outb(wdc+wd_sector, sector); + +#ifdef B_FORMAT } +#endif + + outb(wdc+wd_cyl_lo, cylin); + outb(wdc+wd_cyl_hi, cylin >> 8); + + /* set up the SDH register (select drive) */ + outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf)); + + /* wait for drive to become ready */ + timeout = 0; + while ((inb(wdc+wd_status) & WDCS_READY) == 0) + { + if (++timeout > WDCTIMEOUT) + { + printf("wd.c: Drive busy too long!\n"); + /* reset the device */ + outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS)); + DELAY(1000); + outb(wdc+wd_ctlr, WDCTL_IDS); + DELAY(1000); + (void) inb(wdc+wd_error); /* XXX! */ + outb(wdc+wd_ctlr, WDCTL_4BIT); + goto RETRY; + } + } + + /* initiate command! */ +#ifdef B_FORMAT + if (bp->b_flags & B_FORMAT) + outb(wdc+wd_command, WDCC_FORMAT); + else +#endif + outb(wdc+wd_command, + (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE); #ifdef WDDEBUG printf("sector %d cylin %d head %d addr %x sts %x\n", sector, cylin, head, addr, inb(wdc+wd_altsts)); @@ -523,18 +543,21 @@ wdstart(void) if (bp->b_flags & B_READ) return; /* ready to send data? */ - if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ) != 0) { - wderror(bp, du, "wdstart: timeout waiting for DRQ"); - /* - * XXX what do we do now? If we've just issued the command, - * then we can treat this failure the same as a command - * failure. But if we are continuing a multi-sector write, - * the command was issued ages ago, so we can't simply - * restart it. - * - * XXX we waste a lot of time unnecessarily translating - * block numbers to cylin/head/sector for continued i/o's. - */ + timeout = 0; + while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) + { + if (++timeout > WDCTIMEOUT) + { + printf("wd.c: Drive not ready for too long!\n"); + /* reset the device */ + outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS)); + DELAY(1000); + outb(wdc+wd_ctlr, WDCTL_IDS); + DELAY(1000); + (void) inb(wdc+wd_error); /* XXX! */ + outb(wdc+wd_ctlr, WDCTL_4BIT); + goto RETRY; + } } /* then send it! */ @@ -549,14 +572,17 @@ wdstart(void) * continue with the next chunk if so. */ void -wdintr(int unit) +wdintr(struct intrframe wdif) { register struct disk *du; register struct buf *bp, *dp; - int wdc; + int status, wdc; + char partch ; if (!wdtab.b_active) { - printf("wd%d: extra interrupt\n", unit); +#ifdef nyet + printf("wd: extra interrupt\n"); +#endif return; } @@ -565,24 +591,26 @@ wdintr(int unit) du = wddrives[wdunit(bp->b_dev)]; wdc = du->dk_port; - if (wdwait(du, 0) < 0) { - wderror(bp, du, "wdintr: timeout waiting for status"); - du->dk_status |= WDCS_ERR; /* XXX */ - } +#ifdef WDDEBUG + printf("I "); +#endif + + while ((status = inb(wdc+wd_status)) & WDCS_BUSY) ; /* is it not a transfer, but a control operation? */ if (du->dk_state < OPEN) { - wdtab.b_active = 0; if (wdcontrol(bp)) wdstart(); return; } /* have we an error? */ - if (du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) { -oops: + if (status & (WDCS_ERR | WDCS_ECCCOR)) { + + du->dk_status = status; + du->dk_error = inb(wdc + wd_error); #ifdef WDDEBUG - wderror(bp, du, "wdintr"); + printf("status %x error %x\n", status, du->dk_error); #endif if((du->dk_flags & DKFL_SINGLE) == 0) { du->dk_flags |= DKFL_ERROR; @@ -595,19 +623,28 @@ wdintr(int unit) goto done; } #endif - + /* error or error correction? */ - if (du->dk_status & WDCS_ERR) { + if (status & WDCS_ERR) { if (++wdtab.b_errcnt < RETRIES) { wdtab.b_active = 0; } else { - wderror(bp, du, "hard error"); + diskerr(bp, "wd", "hard error", LOG_PRINTF, + du->dk_skip, &du->dk_dd); +#ifdef WDDEBUG + printf( "status %b error %b\n", + status, WDCS_BITS, + inb(wdc+wd_error), WDERR_BITS); +#endif bp->b_error = EIO; /* 17 Sep 92*/ bp->b_flags |= B_ERROR; /* flag the error */ } - } else - wderror(bp, du, "soft ecc"); + } else { + diskerr(bp, "wd", "soft ecc", 0, + du->dk_skip, &du->dk_dd); + } } +outt: /* * If this was a successful read operation, fetch the data. @@ -618,13 +655,8 @@ wdintr(int unit) chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short)); /* ready to receive data? */ - if ((du->dk_status & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ)) - != (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ)) - wderror(bp, du, "wdintr: read intr arrived early"); - if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ) != 0) { - wderror(bp, du, "wdintr: read error detected late"); - goto oops; - } + while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) + ; /* suck in data */ insw (wdc+wd_data, @@ -636,17 +668,16 @@ wdintr(int unit) } wdxfer[du->dk_unit]++; -outt: if (wdtab.b_active) { if ((bp->b_flags & B_ERROR) == 0) { du->dk_skip++; /* Add to successful sectors. */ if (wdtab.b_errcnt) - wderror(bp, du, "soft error"); + diskerr(bp, "wd", "soft error", 0, + du->dk_skip, &du->dk_dd); wdtab.b_errcnt = 0; /* see if more to transfer */ if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) { - wdtab.b_active = 0; wdstart(); return; /* next chunk is started */ } else if ((du->dk_flags & (DKFL_SINGLE|DKFL_ERROR)) @@ -654,15 +685,12 @@ wdintr(int unit) du->dk_skip = 0; du->dk_flags &= ~DKFL_ERROR; du->dk_flags |= DKFL_SINGLE; - wdtab.b_active = 0; wdstart(); return; /* redo xfer sector by sector */ } } -#ifdef B_FORMAT -done: ; -#endif +done: /* done with this transfer, with or without error */ du->dk_flags &= ~DKFL_SINGLE; wdtab.b_actf = dp->b_forw; @@ -696,8 +724,9 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p) register struct disk *du; int part = wdpart(dev), mask = 1 << part; struct partition *pp; + struct dkbad *db; + int i, error = 0; char *msg; - struct disklabel save_label; unit = wdunit(dev); if (unit >= _NWD) return (ENXIO) ; @@ -705,59 +734,48 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p) du = wddrives[unit]; if (du == 0) return (ENXIO) ; - while (du->dk_flags & DKFL_LABELLING) - tsleep((caddr_t)&du->dk_flags, PZERO - 1, "wdopen", 1); if ((du->dk_flags & DKFL_BSDLABEL) == 0) { - /* - * wdtab.b_active != 0 implies wdutab[unit].b_actf == NULL (?) - * so the following guards most things (until the next i/o). - * It doesn't guard against a new i/o starting and being - * affected by the label being changed. Sigh. - */ - wdsleep("wdopn1"); - - du->dk_flags |= DKFL_LABELLING | DKFL_WRITEPROT; - du->dk_state = WANTOPEN; + du->dk_flags |= DKFL_WRITEPROT; wdutab[unit].b_actf = NULL; /* - * Read label using WDRAW partition. - * - * If the drive has an MBR, then the current geometry (from - * wdgetctlr()) is used to read it; then the BIOS/DOS - * geometry is inferred and used to read the label off the - * 'c' partition. Otherwise the label is read using the - * current geometry. The label gives the final geometry. - * If bad sector handling is enabled, then this geometry - * is used to read the bad sector table. The geometry - * changes occur inside readdisklabel() and are propagated - * to the driver by resetting the state machine. + * Use the default sizes until we've read the label, + * or longer if there isn't one there. */ - save_label = du->dk_dd; -#define WDSTRATEGY ((int (*)(struct buf *)) wdstrategy) /* XXX */ - msg = readdisklabel(makewddev(major(dev), unit, WDRAW), - WDSTRATEGY, &du->dk_dd, - du->dk_dospartitions, &du->dk_bad, - (struct buf **)NULL); - du->dk_flags &= ~DKFL_LABELLING; - if (msg != NULL) { - du->dk_dd = save_label; + bzero(&du->dk_dd, sizeof(du->dk_dd)); +#undef d_type /* fix goddamn segments.h! XXX */ + du->dk_dd.d_type = DTYPE_ST506; + du->dk_dd.d_ncylinders = 1024; + du->dk_dd.d_secsize = DEV_BSIZE; + du->dk_dd.d_ntracks = 8; + du->dk_dd.d_nsectors = 17; + du->dk_dd.d_secpercyl = 17*8; + du->dk_state = WANTOPEN; + du->dk_unit = unit; + + /* read label using "c" partition */ + if (msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW), + wdstrategy, &du->dk_dd, du->dk_dospartitions, + &du->dk_bad, 0)) { log(LOG_WARNING, "wd%d: cannot find label (%s)\n", - unit, msg); + unit, msg); if (part != WDRAW) - return (EINVAL); /* XXX needs translation */ + error = EINVAL; /* XXX needs translation */ + goto done; } else { + + wdsetctlr(dev, du); du->dk_flags |= DKFL_BSDLABEL; du->dk_flags &= ~DKFL_WRITEPROT; if (du->dk_dd.d_flags & D_BADSECT) du->dk_flags |= DKFL_BADSECT; } - /* Pick up changes made by readdisklabel(). */ - wdsleep("wdopn2"); - du->dk_state = WANTOPEN; - } +done: + if (error) + return(error); + } /* * Warn if a partion is opened * that overlaps another partition which is open @@ -775,6 +793,8 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p) if (pp->p_offset + pp->p_size <= start || pp->p_offset >= end) continue; + /*if (pp - du->dk_dd.d_partitions == RAWPART) + continue; */ if (pp - du->dk_dd.d_partitions == WDRAW) continue; if (du->dk_openpart & (1 << (pp - @@ -811,33 +831,55 @@ static int wdcontrol(register struct buf *bp) { register struct disk *du; + register unit; + unsigned char stat; + int s, cnt; + extern int bootdev; + int cyl, trk, sec, i, wdc; + struct wdparams foo; du = wddrives[wdunit(bp->b_dev)]; - + unit = du->dk_unit; + wdc = du->dk_port; + switch (du->dk_state) { - case WANTOPEN: -tryagainrecal: + + tryagainrecal: + case WANTOPEN: /* set SDH, step rate, do restore */ +#ifdef WDDEBUG + printf("wd%d: recal ", unit); +#endif + s = splbio(); /* not called from intr level ... */ + wdgetctlr(unit, du); + + outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); wdtab.b_active = 1; - if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0) { - wderror(bp, du, "wdcontrol: wdcommand failed"); - goto maybe_retry; - } - du->dk_state = RECAL; + + /* wait for drive and controller to become ready */ + for (i = WDCTIMEOUT; (inb(wdc+wd_status) & (WDCS_READY|WDCS_BUSY)) + != WDCS_READY && i-- != 0; ) + ; + outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); + du->dk_state++; + splx(s); return(0); + case RECAL: - if (du->dk_status & WDCS_ERR || wdsetctlr(du) != 0) { - wderror(bp, du, "wdcontrol: recal failed"); -maybe_retry: - if (du->dk_status & WDCS_ERR) - wdunwedge(du); + if ((stat = inb(wdc+wd_status)) & WDCS_ERR) { + printf("wd%d: recal", du->dk_unit); + printf(": status %b error %b\n", stat, WDCS_BITS, + inb(wdc+wd_error), WDERR_BITS); if (++wdtab.b_errcnt < RETRIES) { du->dk_state = WANTOPEN; goto tryagainrecal; } bp->b_error = ENXIO; /* XXX needs translation */ - bp->b_flags |= B_ERROR; - return(1); + goto badopen; } + + /* some controllers require this ... */ + wdsetctlr(bp->b_dev, du); + wdtab.b_errcnt = 0; du->dk_state = OPEN; /* @@ -845,114 +887,127 @@ wdcontrol(register struct buf *bp) * by normal means. */ return(1); + + default: + panic("wdcontrol"); } - panic("wdcontrol"); - return (1); + /* NOTREACHED */ + +badopen: + printf(": status %b error %b\n", stat, WDCS_BITS, + inb(wdc + wd_error), WDERR_BITS); + bp->b_flags |= B_ERROR; + return(1); } /* - * Wait uninterruptibly until controller is not busy, then send it a command. - * The wait usually terminates immediately because we waited for the previous - * command to terminate. + * send a command and wait uninterruptibly until controller is finished. + * return -1 if controller busy for too long, otherwise + * return status. intended for brief controller commands at critical points. + * assumes interrupts are blocked. */ static int -wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector, - u_int count, u_int command) -{ - u_int wdc; +wdcommand(struct disk *du, int cmd) { + int timeout = WDCTIMEOUT, stat, wdc; - if (wdwait(du, 0) < 0) - return (1); + /* controller ready for command? */ wdc = du->dk_port; - outb(wdc + wd_precomp, du->dk_dd.d_precompcyl / 4); - outb(wdc + wd_cyl_lo, cylinder); - outb(wdc + wd_cyl_hi, cylinder >> 8); - outb(wdc + wd_sdh, WDSD_IBM | (du->dk_unit << 4) | head); - outb(wdc + wd_sector, sector + 1); - outb(wdc + wd_seccnt, count); - outb(du->dk_port + wd_command, command); - return (0); + while (((stat = inb(wdc + wd_status)) & WDCS_BUSY) && timeout > 0) + timeout--; + if (timeout <= 0) + return(-1); + + /* send command, await results */ + outb(wdc+wd_command, cmd); + while (((stat = inb(wdc+wd_status)) & WDCS_BUSY) && timeout > 0) + timeout--; + if (timeout <= 0) + return(-1); + if (cmd != WDCC_READP) + return (stat); + + /* is controller ready to return data? */ + while (((stat = inb(wdc+wd_status)) & (WDCS_ERR|WDCS_DRQ)) == 0 && + timeout > 0) + timeout--; + if (timeout <= 0) + return(-1); + + return (stat); } /* * issue IDC to drive to tell it just what geometry it is to be. */ static int -wdsetctlr(struct disk *du) -{ -#ifdef WDDEBUG - printf("wd%d: wdsetctlr C %lu H %lu S %lu\n", du->dk_unit, - du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, - du->dk_dd.d_nsectors); -#endif - if (wdcommand(du, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks - 1, 0, - du->dk_dd.d_nsectors, WDCC_IDC) != 0 - || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) != 0) { - wderror((struct buf *)NULL, du, "wdsetctlr failed"); - return (1); - } - return (0); -} +wdsetctlr(dev_t dev, struct disk *du) { + int stat, x, wdc; -/* - * Wait until driver is inactive, then set up controller. - */ -static int -wdwsetctlr(struct disk *du) -{ - int stat; - int x; +/*printf("C%dH%dS%d ", du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, + du->dk_dd.d_nsectors);*/ - wdsleep("wdwset"); x = splbio(); - stat = wdsetctlr(du); + wdc = du->dk_port; + outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders+1); + outb(wdc+wd_cyl_hi, (du->dk_dd.d_ncylinders+1)>>8); + outb(wdc+wd_sdh, WDSD_IBM | (wdunit(dev) << 4) + du->dk_dd.d_ntracks-1); + outb(wdc+wd_seccnt, du->dk_dd.d_nsectors); + stat = wdcommand(du, WDCC_IDC); + + if (stat < 0) { + splx(x); + return(stat); + } + if (stat & WDCS_ERR) + printf("wdsetctlr: status %b error %b\n", + stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS); splx(x); - return (stat); + return(stat); } /* * issue READP to drive to ask it what it is. */ static int -wdgetctlr(struct disk *du) { - int i; +wdgetctlr(int u, struct disk *du) { + int stat, x, i, wdc; char tb[DEV_BSIZE]; struct wdparams *wp; - if (wdcommand(du, 0, 0, 0, 0, WDCC_READP) != 0 - || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ) != 0) { + x = splbio(); /* not called from intr level ... */ + wdc = du->dk_port; + outb(wdc+wd_sdh, WDSD_IBM | (u << 4)); + stat = wdcommand(du, WDCC_READP); - /* Old drives don't support WDCC_READP. Try a seek to 0. */ - if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0 - || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) != 0) - return (1); + if (stat < 0) { + splx(x); + return(stat); + } + /* + * If WDCC_READP fails then we might have an old ST506 type drive + * so we try a seek to 0; if that passes then the + * drive is there but it's OLD AND KRUSTY + */ + if (stat & WDCS_ERR) { + stat = wdcommand(du, WDCC_RESTORE | WD_STEP); + if (stat & WDCS_ERR) { + stat = inb(wdc+wd_error); + splx(x); + return(stat); + } - /* Fake minimal drive geometry for reading the MBR or label. */ - du->dk_dd.d_secsize = DEV_BSIZE; - du->dk_dd.d_nsectors = 17; - du->dk_dd.d_ntracks = 1; - du->dk_dd.d_ncylinders = 1; - du->dk_dd.d_secpercyl = 17; - - /* - * Fake some more of the label for printing by disklabel(1) - * in case there is no real label. - */ - du->dk_dd.d_type = DTYPE_ST506; - du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; - strncpy(du->dk_dd.d_typename, "Fake geometry", + strncpy(du->dk_dd.d_typename, "ST506", sizeof du->dk_dd.d_typename); - - /* Fake the model name for printing by wdattach(). */ strncpy(du->dk_params.wdp_model, "Unknown Type", sizeof du->dk_params.wdp_model); - - return (0); + du->dk_dd.d_type = DTYPE_ST506; + splx(x); + return(0); } /* obtain parameters */ wp = &du->dk_params; - insw(du->dk_port + wd_data, tb, sizeof(tb)/sizeof(short)); + insw(wdc+wd_data, tb, sizeof(tb)/sizeof(short)); bcopy(tb, wp, sizeof(struct wdparams)); /* shuffle string byte order */ @@ -961,21 +1016,18 @@ wdgetctlr(struct disk *du) { p = (u_short *) (wp->wdp_model + i); *p = ntohs(*p); } -#ifdef WDDEBUG - printf( - "\nwdgetctlr wd%d: gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", - du->dk_unit, wp->wdp_config, - wp->wdp_fixedcyl + wp->wdp_removcyl, wp->wdp_heads, - wp->wdp_sectors, wp->wdp_cntype, wp->wdp_cnsbsz, - wp->wdp_model); -#endif +/*printf("gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", wp->wdp_config, +wp->wdp_fixedcyl+wp->wdp_removcyl, wp->wdp_heads, wp->wdp_sectors, +wp->wdp_cntype, wp->wdp_cnsbsz, wp->wdp_model);*/ /* update disklabel given drive information */ - du->dk_dd.d_secsize = DEV_BSIZE; du->dk_dd.d_ncylinders = wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/; du->dk_dd.d_ntracks = wp->wdp_heads; du->dk_dd.d_nsectors = wp->wdp_sectors; du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; + du->dk_dd.d_partitions[1].p_size = du->dk_dd.d_secpercyl * + wp->wdp_sectors; + du->dk_dd.d_partitions[1].p_offset = 0; /* dubious ... */ bcopy("ESDI/IDE", du->dk_dd.d_typename, 9); bcopy(wp->wdp_model+20, du->dk_dd.d_packname, 14-1); @@ -983,6 +1035,8 @@ wdgetctlr(struct disk *du) { du->dk_dd.d_type = DTYPE_ESDI; du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; + /* XXX sometimes possibly needed */ + (void) inb(wdc+wd_status); return (0); } @@ -1015,10 +1069,8 @@ wdioctl(dev_t dev, int cmd, caddr_t addr, int flag) int unit = wdunit(dev); register struct disk *du; int error = 0; -#ifdef notyet struct uio auio; struct iovec aiov; -#endif du = wddrives[unit]; @@ -1036,9 +1088,9 @@ wdioctl(dev_t dev, int cmd, caddr_t addr, int flag) break; case DIOCGPART: - ((struct partinfo *)addr)->disklab = &du->dk_dd; - ((struct partinfo *)addr)->part = - &du->dk_dd.d_partitions[wdpart(dev)]; + ((struct partinfo *)addr)->disklab = &du->dk_dd; + ((struct partinfo *)addr)->part = + &du->dk_dd.d_partitions[wdpart(dev)]; break; case DIOCSDINFO: @@ -1051,7 +1103,7 @@ wdioctl(dev_t dev, int cmd, caddr_t addr, int flag) du->dk_dospartitions); if (error == 0) { du->dk_flags |= DKFL_BSDLABEL; - wdwsetctlr(du); /* XXX - check */ + wdsetctlr(dev, du); } break; @@ -1073,13 +1125,13 @@ wdioctl(dev_t dev, int cmd, caddr_t addr, int flag) int wlab; du->dk_flags |= DKFL_BSDLABEL; - wdwsetctlr(du); /* XXX - check */ + wdsetctlr(dev, du); /* simulate opening partition 0 so write succeeds */ du->dk_openpart |= (1 << 0); /* XXX */ wlab = du->dk_wlabel; du->dk_wlabel = 1; - error = writedisklabel(dev, WDSTRATEGY, + error = writedisklabel(dev, wdstrategy, &du->dk_dd, du->dk_dospartitions); du->dk_openpart = du->dk_copenpart | du->dk_bopenpart; du->dk_wlabel = wlab; @@ -1142,8 +1194,7 @@ wdsize(dev_t dev) return(-1); du = wddrives[unit]; - val = 0; - if (du == 0 || du->dk_state == CLOSED) + if (du == 0 || du->dk_state == 0) val = wdopen (makewddev(major(dev), unit, WDRAW), FREAD, S_IFBLK, 0); if (du == 0 || val != 0 || du->dk_flags & DKFL_WRITEPROT) return (-1); @@ -1157,16 +1208,11 @@ int wddump(dev_t dev) /* dump core after a system crash */ { register struct disk *du; /* disk unit to do the IO */ -#ifdef notyet register struct bt_bad *bt_ptr; -#endif long num; /* number of sectors to write */ int unit, part, wdc; - long blkoff, blknum; -#ifdef notdef - long blkcnt; -#endif - long cylin, head, sector; + long blkoff, blknum, blkcnt; + long cylin, head, sector, stat; long secpertrk, secpercyl, nblocks, i; char *addr; extern int Maxmem; @@ -1210,10 +1256,8 @@ wddump(dev_t dev) /* dump core after a system crash */ if ((dumplo < 0) || ((dumplo + num) > nblocks)) return(EINVAL); -#if 0 - wdtab.b_active = 1; /* mark controller active for if we + /*wdtab.b_active = 1; /* mark controller active for if we panic during the dump */ -#endif wddoingadump = 1 ; i = 100000 ; while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0)) ; outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); @@ -1221,8 +1265,8 @@ wddump(dev_t dev) /* dump core after a system crash */ while (inb(wdc+wd_status) & WDCS_BUSY) ; /* some compaq controllers require this ... */ - wdsetctlr(du); - + wdsetctlr(dev, du); + blknum = dumplo + blkoff; while (num > 0) { #ifdef notdef @@ -1257,22 +1301,15 @@ wddump(dev_t dev) /* dump core after a system crash */ * last track which is used for the bad block list), * and works backwards to the front of the disk. */ - /* XXX as usual */ - if (lp->d_partitions[BSD_PART].p_offset != 0) - blknum = lp->d_partitions[BAD144_PART] - .p_offset - + lp->d_partitions[BAD144_PART] - .p_size; - else - blknum = lp->d_secperunit; - blknum -= du->dk_dd.d_nsectors - + (bt_ptr - du->dk_bad.bt_bad) + 1; + blknum = (du->dk_dd.d_secperunit) + - du->dk_dd.d_nsectors + - (bt_ptr - du->dk_bad.bt_bad) - 1; cylin = blknum / secpercyl; head = (blknum % secpercyl) / secpertrk; sector = blknum % secpertrk; break; } - } + #endif sector++; /* origin 1 */ @@ -1292,7 +1329,7 @@ wddump(dev_t dev) /* dump core after a system crash */ inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr) ; #endif outb(wdc+wd_command, WDCC_WRITE); - + /* Ready to send data? */ while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ; if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ; @@ -1310,7 +1347,7 @@ wddump(dev_t dev) /* dump core after a system crash */ /* error check the xfer */ if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ; - if ((unsigned)addr % (1024*1024) == 0) printf("%ld ", num/2048); + if ((unsigned)addr % (1024*1024) == 0) printf("%d ", num/2048) ; /* update block count */ num--; blknum++ ; @@ -1322,126 +1359,4 @@ wddump(dev_t dev) /* dump core after a system crash */ } return(0); } - -static void -wderror(struct buf *bp, struct disk *du, char *mesg) -{ - if (bp == NULL) - printf("wd%d: %s:", du->dk_unit, mesg); - else - diskerr(bp, "wd", mesg, LOG_PRINTF, du->dk_skip, &du->dk_dd); - printf(" status %b error %b\n", - du->dk_status, WDCS_BITS, du->dk_error, WDERR_BITS); -} - -/* - * Reset the controller. - */ -static int -wdreset(struct disk *du) -{ - int wdc; - - wdc = du->dk_port; - (void)wdwait(du, 0); - outb(wdc + wd_ctlr, WDCTL_IDS | WDCTL_RST); - DELAY(10 * 1000); - outb(wdc + wd_ctlr, WDCTL_IDS); - if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) != 0 - || (du->dk_error = inb(wdc + wd_error)) != 0x01) - return (1); - outb(wdc + wd_ctlr, WDCTL_4BIT); - return (0); -} - -/* - * Sleep until driver is inactive. - * This is used only for avoiding rare race conditions, so it is unimportant - * that the sleep may be far too short or too long. - */ -static void -wdsleep(char *wmesg) -{ - while (wdtab.b_active) - tsleep((caddr_t)&wdtab.b_active, PZERO - 1, wmesg, 1); -} - -/* - * Reset the controller after it has become wedged. This is different from - * wdreset() so that wdreset() can be used in the probe and so that this - * can restore the geometry . - */ -static int -wdunwedge(struct disk *du) -{ - struct disk *du1; - int unit; - - /* Schedule other drives for recalibration. */ - for (unit = 0; unit < _NWD; unit++) - if ((du1 = wddrives[unit]) != NULL && du1 != du - && du1->dk_state > WANTOPEN) - du1->dk_state = WANTOPEN; - - DELAY(RECOVERYTIME); - if (wdreset(du) == 0) { - /* - * XXX - recalibrate current drive now because some callers - * aren't prepared to have its state change. - */ - if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) == 0 - && wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) == 0 - && wdsetctlr(du) == 0) - return (0); - } - wderror((struct buf *)NULL, du, "wdunwedge failed"); - return (1); -} - -/* - * Wait uninterruptibly until controller is not busy and either certain - * status bits are set or an error has occurred. - * The wait is usually short unless it is for the controller to process - * an entire critical command. - * Return 1 for (possibly stale) controller errors, -1 for timeout errors, - * or 0 for no errors. - * Return controller status in du->dk_status and, if there was a controller - * error, return the error code in du->dk_error. - */ -static int min_retries; -static int -wdwait(struct disk *du, u_char bits_wanted) -{ - int retries; - int wdc; - u_char status; -#define POLLING 1000 -#define TIMEOUT 2000 /* WDCC_DIAGNOSE can take > 300 msec */ - - wdc = du->dk_port; - retries = POLLING + TIMEOUT; - do { -if (min_retries > retries || min_retries == 0) - min_retries = retries; - du->dk_status = status = inb(wdc + wd_status); - if (!(status & WDCS_BUSY)) { - if (status & WDCS_ERR) { - du->dk_error = inb(wdc + wd_error); - return (1); - } - if ((status & bits_wanted) == bits_wanted) - return (0); - } - if (retries < TIMEOUT) - /* - * Switch to a polling rate of about 1 KHz so that - * the timeout is almost machine-independent. The - * controller is taking a long time to respond, so - * an extra msec won't matter. - */ - DELAY(1000); - } while (--retries != 0); - return (-1); -} - -#endif /* NWD > 0 */ +#endif diff --git a/sys/i386/isa/wdreg.h b/sys/i386/isa/wdreg.h index 24d189739a23..d9389157039b 100644 --- a/sys/i386/isa/wdreg.h +++ b/sys/i386/isa/wdreg.h @@ -129,15 +129,15 @@ struct wdparams { /* * wd driver entry points */ -void wdstrategy(struct buf *bp); -void wdintr(int unit); -int wdopen(dev_t dev, int flags, int fmt, struct proc *p); +int wdprobe(struct isa_device *); +int wdattach(struct isa_device *); +int wdstrategy(struct buf *); +void wdintr(struct intrframe); +int wdopen(dev_t, int, int, struct proc *); int wdclose(dev_t dev, int flags, int fmt); -int wdioctl(dev_t dev, int cmd, caddr_t addr, int flag); -#ifdef B_FORMAT -int wdformat(struct buf *bp); -#endif -int wdsize(dev_t dev); -int wddump(dev_t dev); +int wdioctl(dev_t, int, caddr_t, int); +/* int wdformat(struct buf *bp); */ +int wdsize(dev_t); +int wddump(dev_t); #endif KERNEL