diff options
author | Jason Downs <downsj@cvs.openbsd.org> | 1996-06-08 09:12:48 +0000 |
---|---|---|
committer | Jason Downs <downsj@cvs.openbsd.org> | 1996-06-08 09:12:48 +0000 |
commit | fdc68389b9dfe6e5d35bc6cb6b180e9daf9e61e2 (patch) | |
tree | a4460d2ec87ff4da7cc88b4cae0ebcf64034dc98 /sys/dev/isa/wd.c | |
parent | 9a887c22a316f9b94faed82ec7af6c138ff71262 (diff) |
Initial support ATAPI CD-ROMs.
Based on Manuel Bouyer's drivers.
Diffstat (limited to 'sys/dev/isa/wd.c')
-rw-r--r-- | sys/dev/isa/wd.c | 1278 |
1 files changed, 220 insertions, 1058 deletions
diff --git a/sys/dev/isa/wd.c b/sys/dev/isa/wd.c index 82c2ac91ac5..d9061bd00eb 100644 --- a/sys/dev/isa/wd.c +++ b/sys/dev/isa/wd.c @@ -1,5 +1,4 @@ -/* $OpenBSD: wd.c,v 1.14 1996/06/01 09:35:36 deraadt Exp $ */ -/* $NetBSD: wd.c,v 1.150 1996/05/12 23:54:03 mycroft Exp $ */ +/* $NetBSD: wd.c,v 1.150 1996/05/12 23:54:03 mycroft Exp $ */ /* * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved. @@ -56,21 +55,8 @@ #include <machine/pio.h> #include <dev/isa/isavar.h> -#include <dev/isa/isadmavar.h> #include <dev/isa/wdreg.h> - -#define WAITTIME (8 * hz) /* time to wait for a completion - (long enough for disk spin-ups) */ -#define RECOVERYTIME (hz / 2) /* time to recover from an error */ - -#define WDCDELAY 100 -#define WDCNDELAY 100000 /* delay = 100us; so 10s for a controller state change */ -#if 0 -/* If you enable this, it will report any delays more than 100us * N long. */ -#define WDCNDELAY_DEBUG 10 -#endif - -#define WDIORETRIES 3 /* number of retries before giving up */ +#include <dev/isa/wdlink.h> #define WDUNIT(dev) DISKUNIT(dev) #define WDPART(dev) DISKPART(dev) @@ -78,82 +64,22 @@ #define WDLABELDEV(dev) (MAKEWDDEV(major(dev), WDUNIT(dev), RAW_PART)) +#ifdef WDDEBUG +#define WDDEBUG_PRINT(args) printf args +#else +#define WDDEBUG_PRINT(args) +#endif + struct wd_softc { struct device sc_dev; struct disk sc_dk; - - /* Information about the current transfer: */ - daddr_t sc_blkno; /* starting block number */ - int sc_bcount; /* byte count left */ - int sc_skip; /* bytes already transferred */ - int sc_nblks; /* number of blocks currently transferring */ - int sc_nbytes; /* number of bytes currently transferring */ - - /* Long-term state: */ - int sc_drive; /* physical unit number */ - int sc_state; /* control state */ -#define RECAL 0 /* recalibrate */ -#define RECAL_WAIT 1 /* done recalibrating */ -#define GEOMETRY 2 /* upload geometry */ -#define GEOMETRY_WAIT 3 /* done uploading geometry */ -#define MULTIMODE 4 /* set multiple mode */ -#define MULTIMODE_WAIT 5 /* done setting multiple mode */ -#define OPEN 6 /* done with open */ - int sc_mode; /* transfer mode */ -#define WDM_PIOSINGLE 0 /* single-sector PIO */ -#define WDM_PIOMULTI 1 /* multi-sector PIO */ -#define WDM_DMA 2 /* DMA */ - int sc_multiple; /* multiple for WDM_PIOMULTI */ - int sc_flags; /* drive characteistics found */ -#define WDF_LOCKED 0x01 -#define WDF_WANTED 0x02 -#define WDF_WLABEL 0x04 /* label is writable */ -#define WDF_LABELLING 0x08 /* writing label */ -/* XXX Nothing resets this yet, but disk change sensing will when ATAPI is - implemented. */ -#define WDF_LOADED 0x10 /* parameters loaded */ -#define WDF_32BIT 0x20 /* can do 32-bit transfer */ - - struct wdparams sc_params; /* ESDI/ATA drive parameters */ - daddr_t sc_badsect[127]; /* 126 plus trailing -1 marker */ - - TAILQ_ENTRY(wd_softc) sc_drivechain; + struct wd_link *d_link; struct buf sc_q; }; -struct wdc_softc { - struct device sc_dev; - void *sc_ih; - - int sc_iobase; /* I/O port base */ - int sc_drq; /* DMA channel */ - - TAILQ_HEAD(drivehead, wd_softc) sc_drives; - int sc_flags; -#define WDCF_ACTIVE 0x01 /* controller is active */ -#define WDCF_SINGLE 0x02 /* sector at a time mode */ -#define WDCF_ERROR 0x04 /* processing a disk error */ -#define WDCF_WANTED 0x08 /* XXX locking for wd_get_parms() */ - int sc_errors; /* errors during current transfer */ - u_char sc_status; /* copy of status register */ - u_char sc_error; /* copy of error register */ -}; - -int wdcprobe __P((struct device *, void *, void *)); -void wdcattach __P((struct device *, struct device *, void *)); -int wdcintr __P((void *)); - -struct cfattach wdc_ca = { - sizeof(struct wdc_softc), wdcprobe, wdcattach -}; - -struct cfdriver wdc_cd = { - NULL, "wdc", DV_DULL -}; - -int wdprobe __P((struct device *, void *, void *)); -void wdattach __P((struct device *, struct device *, void *)); -int wdprint __P((void *, char *)); +int wdprobe __P((struct device *, void *, void *)); +void wdattach __P((struct device *, struct device *, void *)); +int wdprint __P((void *, char *)); struct cfattach wd_ca = { sizeof(struct wd_softc), wdprobe, wdattach @@ -175,123 +101,29 @@ cdev_decl(wd); bdev_decl(wd); void wdfinish __P((struct wd_softc *, struct buf *)); -int dcintr __P((void *)); -void wdcstart __P((struct wdc_softc *)); -int wdcommand __P((struct wd_softc *, int, int, int, int, int)); -int wdcommandshort __P((struct wdc_softc *, int, int)); -int wdcontrol __P((struct wd_softc *)); -int wdsetctlr __P((struct wd_softc *)); +int wdsetctlr __P((struct wd_link *)); static void bad144intern __P((struct wd_softc *)); -int wdcreset __P((struct wdc_softc *)); -void wdcrestart __P((void *arg)); -void wdcunwedge __P((struct wdc_softc *)); -void wdctimeout __P((void *arg)); -void wderror __P((void *, struct buf *, char *)); -int wdcwait __P((struct wdc_softc *, int)); -int wdlock __P((struct wd_softc *)); -void wdunlock __P((struct wd_softc *)); - -/* ST506 spec says that if READY or SEEKCMPLT go off, then the read or write - command is aborted. */ -#define wait_for_drq(d) wdcwait(d, WDCS_DRDY | WDCS_DSC | WDCS_DRQ) -#define wait_for_ready(d) wdcwait(d, WDCS_DRDY | WDCS_DSC) -#define wait_for_unbusy(d) wdcwait(d, 0) - -int -wdcprobe(parent, match, aux) - struct device *parent; - void *match, *aux; -{ - struct wdc_softc *wdc = match; - struct isa_attach_args *ia = aux; - int iobase; - - wdc->sc_iobase = iobase = ia->ia_iobase; - - /* Check if we have registers that work. */ - outb(iobase+wd_error, 0x5a); /* Error register not writable, */ - outb(iobase+wd_cyl_lo, 0xa5); /* but all of cyllo are. */ - if (inb(iobase+wd_error) == 0x5a || inb(iobase+wd_cyl_lo) != 0xa5) - return 0; - - if (wdcreset(wdc) != 0) { - delay(500000); - if (wdcreset(wdc) != 0) - return 0; - } - - /* Select drive 0. */ - outb(iobase+wd_sdh, WDSD_IBM | 0); - - /* Wait for controller to become ready. */ - if (wait_for_unbusy(wdc) < 0) - return 0; - - /* Start drive diagnostics. */ - outb(iobase+wd_command, WDCC_DIAGNOSE); - - /* Wait for command to complete. */ - if (wait_for_unbusy(wdc) < 0) - return 0; - - ia->ia_iosize = 8; - ia->ia_msize = 0; - return 1; -} - -struct wdc_attach_args { - int wa_drive; -}; - -int -wdprint(aux, wdc) - void *aux; - char *wdc; -{ - struct wdc_attach_args *wa = aux; - - if (!wdc) - printf(" drive %d", wa->wa_drive); - return QUIET; -} - -void -wdcattach(parent, self, aux) - struct device *parent, *self; - void *aux; -{ - struct wdc_softc *wdc = (void *)self; - struct isa_attach_args *ia = aux; - struct wdc_attach_args wa; - - TAILQ_INIT(&wdc->sc_drives); - wdc->sc_drq = ia->ia_drq; - - printf("\n"); - - wdc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE, - IPL_BIO, wdcintr, wdc, wdc->sc_dev.dv_xname); - - for (wa.wa_drive = 0; wa.wa_drive < 2; wa.wa_drive++) - (void)config_found(self, (void *)&wa, wdprint); -} +int wdlock __P((struct wd_link *)); +void wdunlock __P((struct wd_link *)); int wdprobe(parent, match, aux) struct device *parent; void *match, *aux; { - struct wdc_softc *wdc = (void *)parent; + caddr_t *wdc = (void *)parent; struct cfdata *cf = match; - struct wdc_attach_args *wa = aux; - int drive = wa->wa_drive; + struct wd_link *d_link = aux; + int drive; + + if (d_link == NULL) + return 0; + if (d_link->type != DRIVE) + return 0; + drive = d_link->sc_drive; if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != drive) return 0; - - if (wdcommandshort(wdc, drive, WDCC_RECAL) != 0 || - wait_for_ready(wdc) != 0) - return 0; return 1; } @@ -302,12 +134,14 @@ wdattach(parent, self, aux) void *aux; { struct wd_softc *wd = (void *)self; - struct wdc_softc *wdc = (void *)parent; - struct wdc_attach_args *wa = aux; + struct caddr_t *wdc = (void *)parent; + struct wd_link *d_link= aux; int i, blank; char buf[41], c, *p, *q; - wd->sc_drive = wa->wa_drive; + wd->d_link = d_link; + d_link->openings = 1; + d_link->wd_softc = (caddr_t)wd; /* * Initialize and attach the disk structure. @@ -318,9 +152,11 @@ wdattach(parent, self, aux) dk_establish(&wd->sc_dk, &wd->sc_dev); - wd_get_parms(wd); - for (blank = 0, p = wd->sc_params.wdp_model, q = buf, i = 0; - i < sizeof(wd->sc_params.wdp_model); i++) { + d_link->sc_lp = wd->sc_dk.dk_label; + + wdc_get_parms(d_link); + for (blank = 0, p = d_link->sc_params.wdp_model, q = buf, i = 0; + i < sizeof(d_link->sc_params.wdp_model); i++) { c = *p++; if (c == '\0') break; @@ -335,34 +171,36 @@ wdattach(parent, self, aux) } *q++ = '\0'; - printf(": %dMB, %d cyl, %d head, %d sec, %d bytes/sec <%s>\n", - wd->sc_params.wdp_cylinders * - (wd->sc_params.wdp_heads * wd->sc_params.wdp_sectors) / + printf(": <%s>\n", buf); + printf("%s: %dMB, %d cyl, %d head, %d sec, %d bytes/sec\n", + self->dv_xname, + d_link->sc_params.wdp_cylinders * + (d_link->sc_params.wdp_heads * d_link->sc_params.wdp_sectors) / (1048576 / DEV_BSIZE), - wd->sc_params.wdp_cylinders, - wd->sc_params.wdp_heads, - wd->sc_params.wdp_sectors, - DEV_BSIZE, - buf); - - if ((wd->sc_params.wdp_capabilities & WD_CAP_DMA) != 0 && - wdc->sc_drq != DRQUNK) { - wd->sc_mode = WDM_DMA; - } else if (wd->sc_params.wdp_maxmulti > 1) { - wd->sc_mode = WDM_PIOMULTI; - wd->sc_multiple = min(wd->sc_params.wdp_maxmulti, 16); + d_link->sc_params.wdp_cylinders, + d_link->sc_params.wdp_heads, + d_link->sc_params.wdp_sectors, + DEV_BSIZE); + + if ((d_link->sc_params.wdp_capabilities & WD_CAP_DMA) != 0 && + d_link->sc_mode == WDM_DMA) { + d_link->sc_mode = WDM_DMA; + } else if (d_link->sc_params.wdp_maxmulti > 1) { + d_link->sc_mode = WDM_PIOMULTI; + d_link->sc_multiple = min(d_link->sc_params.wdp_maxmulti, 16); } else { - wd->sc_mode = WDM_PIOSINGLE; - wd->sc_multiple = 1; + d_link->sc_mode = WDM_PIOSINGLE; + d_link->sc_multiple = 1; } printf("%s: using", wd->sc_dev.dv_xname); - if (wd->sc_mode == WDM_DMA) + if (d_link->sc_mode == WDM_DMA) printf(" dma transfers,"); else printf(" %d-sector %d-bit pio transfers,", - wd->sc_multiple, (wd->sc_flags & WDF_32BIT) == 0 ? 16 : 32); - if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) + d_link->sc_multiple, + (d_link->sc_flags & WDF_32BIT) == 0 ? 16 : 32); + if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) printf(" lba addressing\n"); else printf(" chs addressing\n"); @@ -377,6 +215,7 @@ wdstrategy(bp) struct buf *bp; { struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(bp->b_dev)]; + struct wd_link *d_link= wd->d_link; int s; /* Valid request? */ @@ -388,7 +227,7 @@ wdstrategy(bp) } /* If device invalidated (e.g. media change, door open), error. */ - if ((wd->sc_flags & WDF_LOADED) == 0) { + if ((d_link->sc_flags & WDF_LOADED) == 0) { bp->b_error = EIO; goto bad; } @@ -403,23 +242,13 @@ wdstrategy(bp) */ if (WDPART(bp->b_dev) != RAW_PART && bounds_check_with_label(bp, wd->sc_dk.dk_label, - (wd->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0) + (d_link->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0) goto done; /* Queue transfer on drive, activate drive and controller if idle. */ s = splbio(); disksort(&wd->sc_q, bp); - if (!wd->sc_q.b_active) - wdstart(wd); -#if 0 - else { - struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; - if ((wdc->sc_flags & (WDCF_ACTIVE|WDCF_ERROR)) == 0) { - printf("wdstrategy: controller inactive\n"); - wdcstart(wdc); - } - } -#endif + wdstart(wd); splx(s); return; @@ -438,57 +267,47 @@ void wdstart(wd) struct wd_softc *wd; { - struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; - int active = wdc->sc_drives.tqh_first != 0; - - /* Link onto controller queue. */ - wd->sc_q.b_active = 1; - TAILQ_INSERT_TAIL(&wdc->sc_drives, wd, sc_drivechain); - - disk_busy(&wd->sc_dk); - - /* If controller not already active, start it. */ - if (!active) - wdcstart(wdc); -} - -/* - * Finish an I/O operation. Clean up the drive and controller state, set the - * residual count, and inform the upper layers that the operation is complete. - */ -void -wdfinish(wd, bp) - struct wd_softc *wd; - struct buf *bp; -{ - struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; + struct buf *dp, *bp=0; + struct wd_link *d_link = wd->d_link; + struct wdc_link *ctlr_link = d_link->ctlr_link; + struct wdc_xfer *xfer; + int blkno, nblks; + u_long p_offset; + + while (d_link->openings > 0) { + + /* Is there a buf for us ? */ + dp = &wd->sc_q; + if ((bp = dp->b_actf) == NULL) /* yes, an assign */ + return; + dp->b_actf = bp->b_actf; + + /* + * Make the command. First lock the device + */ + d_link->openings--; + if (WDPART(bp->b_dev) != RAW_PART) + p_offset = + wd->sc_dk.dk_label->d_partitions[WDPART(bp->b_dev)].p_offset; + else + p_offset = 0; - wdc->sc_flags &= ~(WDCF_SINGLE | WDCF_ERROR); - wdc->sc_errors = 0; - /* - * Move this drive to the end of the queue to give others a `fair' - * chance. - */ - if (wd->sc_drivechain.tqe_next) { - TAILQ_REMOVE(&wdc->sc_drives, wd, sc_drivechain); - if (bp->b_actf) { - TAILQ_INSERT_TAIL(&wdc->sc_drives, wd, sc_drivechain); - } else - wd->sc_q.b_active = 0; - } - bp->b_resid = wd->sc_bcount; - wd->sc_skip = 0; - wd->sc_q.b_actf = bp->b_actf; + xfer = wdc_get_xfer(ctlr_link, 0); + if (xfer == NULL) + panic("wdc_xfer"); - disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid)); + xfer->d_link = d_link; + xfer->c_bp = bp; + xfer->c_p_offset = p_offset; + xfer->databuf = bp->b_data; + xfer->c_bcount = bp->b_bcount; + xfer->c_flags |= bp->b_flags & (B_READ|B_WRITE); + xfer->c_blkno = bp->b_blkno; - if (!wd->sc_q.b_actf) { - TAILQ_REMOVE(&wdc->sc_drives, wd, sc_drivechain); - wd->sc_q.b_active = 0; - } else + /* Instrumentation. */ disk_busy(&wd->sc_dk); - - biodone(bp); + wdc_exec_xfer(wd->d_link,xfer); + } } int @@ -498,6 +317,7 @@ wdread(dev, uio, flags) int flags; { + WDDEBUG_PRINT(("wdread\n")); return (physio(wdstrategy, NULL, dev, B_READ, minphys, uio)); } @@ -508,388 +328,37 @@ wdwrite(dev, uio, flags) int flags; { + WDDEBUG_PRINT(("wdwrite\n")); return (physio(wdstrategy, NULL, dev, B_WRITE, minphys, uio)); } /* - * Start I/O on a controller. This does the calculation, and starts a read or - * write operation. Called to from wdstart() to start a transfer, from - * wdcintr() to continue a multi-sector transfer or start the next transfer, or - * wdcrestart() after recovering from an error. - */ -void -wdcstart(wdc) - struct wdc_softc *wdc; -{ - struct wd_softc *wd; - struct buf *bp; - struct disklabel *lp; - int nblks; - -#ifdef DIAGNOSTIC - if ((wdc->sc_flags & WDCF_ACTIVE) != 0) - panic("wdcstart: controller still active"); -#endif - - /* - * XXX - * This is a kluge. See comments in wd_get_parms(). - */ - if ((wdc->sc_flags & WDCF_WANTED) != 0) { - wdc->sc_flags &= ~WDCF_WANTED; - wakeup(wdc); - return; - } - -loop: - /* Is there a drive for the controller to do a transfer with? */ - wd = wdc->sc_drives.tqh_first; - if (wd == NULL) - return; - - /* Is there a transfer to this drive? If not, deactivate drive. */ - bp = wd->sc_q.b_actf; - - if (wdc->sc_errors >= WDIORETRIES) { - wderror(wd, bp, "hard error"); - bp->b_error = EIO; - bp->b_flags |= B_ERROR; - wdfinish(wd, bp); - goto loop; - } - - /* Do control operations specially. */ - if (wd->sc_state < OPEN) { - /* - * Actually, we want to be careful not to mess with the control - * state if the device is currently busy, but we can assume - * that we never get to this point if that's the case. - */ - if (wdcontrol(wd) == 0) { - /* The drive is busy. Wait. */ - return; - } - } - - /* - * WDCF_ERROR is set by wdcunwedge() and wdcintr() when an error is - * encountered. If we are in multi-sector mode, then we switch to - * single-sector mode and retry the operation from the start. - */ - if (wdc->sc_flags & WDCF_ERROR) { - wdc->sc_flags &= ~WDCF_ERROR; - if ((wdc->sc_flags & WDCF_SINGLE) == 0) { - wdc->sc_flags |= WDCF_SINGLE; - wd->sc_skip = 0; - } - } - - lp = wd->sc_dk.dk_label; - - /* When starting a transfer... */ - if (wd->sc_skip == 0) { - int part = WDPART(bp->b_dev); - daddr_t blkno; - -#ifdef WDDEBUG - printf("\n%s: wdcstart %s %d@%d; map ", wd->sc_dev.dv_xname, - (bp->b_flags & B_READ) ? "read" : "write", bp->b_bcount, - bp->b_blkno); -#endif - wd->sc_bcount = bp->b_bcount; - blkno = bp->b_blkno; - if (part != RAW_PART) - blkno += lp->d_partitions[part].p_offset; - wd->sc_blkno = blkno / (lp->d_secsize / DEV_BSIZE); - } else { -#ifdef WDDEBUG - printf(" %d)%x", wd->sc_skip, inb(wdc->sc_iobase+wd_altsts)); -#endif - } - - /* When starting a multi-sector transfer, or doing single-sector - transfers... */ - if (wd->sc_skip == 0 || (wdc->sc_flags & WDCF_SINGLE) != 0 || - wd->sc_mode == WDM_DMA) { - daddr_t blkno = wd->sc_blkno; - long cylin, head, sector; - int command; - - if ((wdc->sc_flags & WDCF_SINGLE) != 0) - nblks = 1; - else if (wd->sc_mode != WDM_DMA) - nblks = wd->sc_bcount / lp->d_secsize; - else - nblks = min(wd->sc_bcount / lp->d_secsize, 8); - - /* Check for bad sectors and adjust transfer, if necessary. */ - if ((lp->d_flags & D_BADSECT) != 0 -#ifdef B_FORMAT - && (bp->b_flags & B_FORMAT) == 0 -#endif - ) { - long blkdiff; - int i; - - for (i = 0; (blkdiff = wd->sc_badsect[i]) != -1; i++) { - blkdiff -= blkno; - if (blkdiff < 0) - continue; - if (blkdiff == 0) { - /* Replace current block of transfer. */ - blkno = - lp->d_secperunit - lp->d_nsectors - i - 1; - } - if (blkdiff < nblks) { - /* Bad block inside transfer. */ - wdc->sc_flags |= WDCF_SINGLE; - nblks = 1; - } - break; - } - /* Tranfer is okay now. */ - } - - if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) { - sector = (blkno >> 0) & 0xff; - cylin = (blkno >> 8) & 0xffff; - head = (blkno >> 24) & 0xf; - head |= WDSD_LBA; - } else { - sector = blkno % lp->d_nsectors; - sector++; /* Sectors begin with 1, not 0. */ - blkno /= lp->d_nsectors; - head = blkno % lp->d_ntracks; - blkno /= lp->d_ntracks; - cylin = blkno; - head |= WDSD_CHS; - } - - if (wd->sc_mode == WDM_PIOSINGLE || - (wdc->sc_flags & WDCF_SINGLE) != 0) - wd->sc_nblks = 1; - else if (wd->sc_mode == WDM_PIOMULTI) - wd->sc_nblks = min(nblks, wd->sc_multiple); - else - wd->sc_nblks = nblks; - wd->sc_nbytes = wd->sc_nblks * lp->d_secsize; - -#ifdef B_FORMAT - if (bp->b_flags & B_FORMAT) { - sector = lp->d_gap3; - nblks = lp->d_nsectors; - command = WDCC_FORMAT; - } else -#endif - switch (wd->sc_mode) { - case WDM_DMA: - command = (bp->b_flags & B_READ) ? - WDCC_READDMA : WDCC_WRITEDMA; - /* Start the DMA channel and bounce the buffer if - necessary. */ - isa_dmastart( - bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE, - bp->b_data + wd->sc_skip, - wd->sc_nbytes, wdc->sc_drq); - break; - case WDM_PIOMULTI: - command = (bp->b_flags & B_READ) ? - WDCC_READMULTI : WDCC_WRITEMULTI; - break; - case WDM_PIOSINGLE: - command = (bp->b_flags & B_READ) ? - WDCC_READ : WDCC_WRITE; - break; - default: -#ifdef DIAGNOSTIC - panic("bad wd mode"); -#endif - return; - } - - /* Initiate command! */ - if (wdcommand(wd, command, cylin, head, sector, nblks) != 0) { - wderror(wd, NULL, - "wdcstart: timeout waiting for unbusy"); - wdcunwedge(wdc); - return; - } - -#ifdef WDDEBUG - printf("sector %d cylin %d head %d addr %x sts %x\n", sector, - cylin, head, bp->b_data, inb(wdc->sc_iobase+wd_altsts)); -#endif - } else if (wd->sc_nblks > 1) { - /* The number of blocks in the last stretch may be smaller. */ - nblks = wd->sc_bcount / lp->d_secsize; - if (wd->sc_nblks > nblks) { - wd->sc_nblks = nblks; - wd->sc_nbytes = wd->sc_bcount; - } - } - - /* If this was a write and not using DMA, push the data. */ - if (wd->sc_mode != WDM_DMA && - (bp->b_flags & (B_READ|B_WRITE)) == B_WRITE) { - if (wait_for_drq(wdc) < 0) { - wderror(wd, NULL, "wdcstart: timeout waiting for drq"); - wdcunwedge(wdc); - return; - } - - /* Push out data. */ - if ((wd->sc_flags & WDF_32BIT) == 0) - outsw(wdc->sc_iobase+wd_data, bp->b_data + wd->sc_skip, - wd->sc_nbytes >> 1); - else - outsl(wdc->sc_iobase+wd_data, bp->b_data + wd->sc_skip, - wd->sc_nbytes >> 2); - } - - wdc->sc_flags |= WDCF_ACTIVE; - timeout(wdctimeout, wdc, WAITTIME); -} - -/* - * 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. - */ -int -wdcintr(arg) - void *arg; -{ - struct wdc_softc *wdc = arg; - struct wd_softc *wd; - struct buf *bp; - - if ((wdc->sc_flags & WDCF_ACTIVE) == 0) - /* leave it alone if we didn't ask for this interrupt */ - return 0; - - wdc->sc_flags &= ~WDCF_ACTIVE; - untimeout(wdctimeout, wdc); - - wd = wdc->sc_drives.tqh_first; - bp = wd->sc_q.b_actf; - -#ifdef WDDEBUG - printf("I%s ", wdc->sc_dev.dv_xname); -#endif - - if (wait_for_unbusy(wdc) < 0) { - wderror(wd, NULL, "wdcintr: timeout waiting for unbusy"); - wdc->sc_status |= WDCS_ERR; /* XXX */ - } - - /* Is it not a transfer, but a control operation? */ - if (wd->sc_state < OPEN) { - if (wdcontrol(wd) == 0) { - /* The drive is busy. Wait. */ - return 1; - } - wdcstart(wdc); - return 1; - } - - /* Turn off the DMA channel and unbounce the buffer. */ - if (wd->sc_mode == WDM_DMA) - isa_dmadone(bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE, - bp->b_data + wd->sc_skip, wd->sc_nbytes, wdc->sc_drq); - - /* Have we an error? */ - if (wdc->sc_status & WDCS_ERR) { -#ifdef WDDEBUG - wderror(wd, NULL, "wdcintr"); -#endif - if ((wdc->sc_flags & WDCF_SINGLE) == 0) { - wdc->sc_flags |= WDCF_ERROR; - goto restart; - } - -#ifdef B_FORMAT - if (bp->b_flags & B_FORMAT) - goto bad; -#endif - - wdcunwedge(wdc); - if (wdc->sc_errors < WDIORETRIES) - return 1; - wderror(wd, bp, "hard error"); - -#ifdef B_FORMAT - bad: -#endif - bp->b_error = EIO; - bp->b_flags |= B_ERROR; - goto done; - } - - /* If this was a read and not using DMA, fetch the data. */ - if (wd->sc_mode != WDM_DMA && - (bp->b_flags & (B_READ|B_WRITE)) == B_READ) { - if ((wdc->sc_status & (WDCS_DRDY | WDCS_DSC | WDCS_DRQ)) - != (WDCS_DRDY | WDCS_DSC | WDCS_DRQ)) { - wderror(wd, NULL, "wdcintr: read intr before drq"); - wdcunwedge(wdc); - return 1; - } - - /* Pull in data. */ - if ((wd->sc_flags & WDF_32BIT) == 0) - insw(wdc->sc_iobase+wd_data, bp->b_data + wd->sc_skip, - wd->sc_nbytes >> 1); - else - insl(wdc->sc_iobase+wd_data, bp->b_data + wd->sc_skip, - wd->sc_nbytes >> 2); - } - - /* If we encountered any abnormalities, flag it as a soft error. */ - if (wdc->sc_errors > 0 || - (wdc->sc_status & WDCS_CORR) != 0) { - wderror(wd, bp, "soft error (corrected)"); - wdc->sc_errors = 0; - } - - /* Adjust pointers for the next block, if any. */ - wd->sc_blkno += wd->sc_nblks; - wd->sc_skip += wd->sc_nbytes; - wd->sc_bcount -= wd->sc_nbytes; - - /* See if this transfer is complete. */ - if (wd->sc_bcount > 0) - goto restart; - -done: - /* Done with this transfer, with or without error. */ - wdfinish(wd, bp); - -restart: - /* Start the next operation, if any. */ - wdcstart(wdc); - - return 1; -} - -/* * Wait interruptibly for an exclusive lock. * * XXX * Several drivers do this; it should be abstracted and made MP-safe. */ int -wdlock(wd) - struct wd_softc *wd; +wdlock(d_link) + struct wd_link *d_link; { int error; + int s; + + WDDEBUG_PRINT(("wdlock\n")); + + s = splbio(); - while ((wd->sc_flags & WDF_LOCKED) != 0) { - wd->sc_flags |= WDF_WANTED; - if ((error = tsleep(wd, PRIBIO | PCATCH, "wdlck", 0)) != 0) + while ((d_link->sc_flags & WDF_LOCKED) != 0) { + d_link->sc_flags |= WDF_WANTED; + if ((error = tsleep(d_link, PRIBIO | PCATCH, + "wdlck", 0)) != 0) { + splx(s); return error; + } } - wd->sc_flags |= WDF_LOCKED; + d_link->sc_flags |= WDF_LOCKED; + splx(s); return 0; } @@ -897,14 +366,16 @@ wdlock(wd) * Unlock and wake up any waiters. */ void -wdunlock(wd) - struct wd_softc *wd; +wdunlock(d_link) + struct wd_link *d_link; { - wd->sc_flags &= ~WDF_LOCKED; - if ((wd->sc_flags & WDF_WANTED) != 0) { - wd->sc_flags &= ~WDF_WANTED; - wakeup(wd); + WDDEBUG_PRINT(("wdunlock")); + + d_link->sc_flags &= ~WDF_LOCKED; + if ((d_link->sc_flags & WDF_WANTED) != 0) { + d_link->sc_flags &= ~WDF_WANTED; + wakeup(d_link); } } @@ -915,9 +386,12 @@ wdopen(dev, flag, fmt, p) struct proc *p; { struct wd_softc *wd; + struct wd_link *d_link; int unit, part; int error; - + + WDDEBUG_PRINT(("wdopen\n")); + unit = WDUNIT(dev); if (unit >= wd_cd.cd_ndevs) return ENXIO; @@ -925,7 +399,8 @@ wdopen(dev, flag, fmt, p) if (wd == 0) return ENXIO; - if ((error = wdlock(wd)) != 0) + d_link = wd->d_link; + if ((error = wdlock(d_link)) != 0) return error; if (wd->sc_dk.dk_openmask != 0) { @@ -933,16 +408,16 @@ wdopen(dev, flag, fmt, p) * If any partition is open, but the disk has been invalidated, * disallow further opens. */ - if ((wd->sc_flags & WDF_LOADED) == 0) { + if ((d_link->sc_flags & WDF_LOADED) == 0) { error = EIO; goto bad3; } } else { - if ((wd->sc_flags & WDF_LOADED) == 0) { - wd->sc_flags |= WDF_LOADED; + if ((d_link->sc_flags & WDF_LOADED) == 0) { + d_link->sc_flags |= WDF_LOADED; /* Load the physical device parameters. */ - if (wd_get_parms(wd) != 0) { + if (wdc_get_parms(d_link) != 0) { error = ENXIO; goto bad2; } @@ -973,18 +448,18 @@ wdopen(dev, flag, fmt, p) } wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask; - wdunlock(wd); + wdunlock(d_link); return 0; bad2: - wd->sc_flags &= ~WDF_LOADED; + d_link->sc_flags &= ~WDF_LOADED; bad: if (wd->sc_dk.dk_openmask == 0) { } bad3: - wdunlock(wd); + wdunlock(d_link); return error; } @@ -998,7 +473,7 @@ wdclose(dev, flag, fmt, p) int part = WDPART(dev); int error; - if ((error = wdlock(wd)) != 0) + if ((error = wdlock(wd->d_link)) != 0) return error; switch (fmt) { @@ -1015,7 +490,7 @@ wdclose(dev, flag, fmt, p) /* XXXX Must wait for I/O to complete! */ } - wdunlock(wd); + wdunlock(wd->d_link); return 0; } @@ -1027,22 +502,25 @@ wdgetdisklabel(wd) struct wd_softc *wd; { struct disklabel *lp = wd->sc_dk.dk_label; + struct wd_link *d_link = wd->d_link; char *errstring; + WDDEBUG_PRINT(("wdgetdisklabel\n")); + bzero(lp, sizeof(struct disklabel)); bzero(wd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel)); lp->d_secsize = DEV_BSIZE; - lp->d_ntracks = wd->sc_params.wdp_heads; - lp->d_nsectors = wd->sc_params.wdp_sectors; - lp->d_ncylinders = wd->sc_params.wdp_cylinders; + lp->d_ntracks = d_link->sc_params.wdp_heads; + lp->d_nsectors = d_link->sc_params.wdp_sectors; + lp->d_ncylinders = d_link->sc_params.wdp_cylinders; lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; #if 0 strncpy(lp->d_typename, "ST506 disk", 16); lp->d_type = DTYPE_ST506; #endif - strncpy(lp->d_packname, wd->sc_params.wdp_model, 16); + strncpy(lp->d_packname, d_link->sc_params.wdp_model, 16); lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; lp->d_rpm = 3600; lp->d_interleave = 1; @@ -1058,10 +536,10 @@ wdgetdisklabel(wd) lp->d_magic2 = DISKMAGIC; lp->d_checksum = dkcksum(lp); - wd->sc_badsect[0] = -1; + d_link->sc_badsect[0] = -1; - if (wd->sc_state > RECAL) - wd->sc_state = RECAL; + if (d_link->sc_state > RECAL) + d_link->sc_state = RECAL; errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART), wdstrategy, lp, wd->sc_dk.dk_cpulabel); if (errstring) { @@ -1071,8 +549,8 @@ wdgetdisklabel(wd) * assume the DOS geometry is now in the label and try * again. XXX This is a kluge. */ - if (wd->sc_state > GEOMETRY) - wd->sc_state = GEOMETRY; + if (d_link->sc_state > GEOMETRY) + d_link->sc_state = GEOMETRY; errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART), wdstrategy, lp, wd->sc_dk.dk_cpulabel); } @@ -1081,270 +559,55 @@ wdgetdisklabel(wd) return; } - if (wd->sc_state > GEOMETRY) - wd->sc_state = GEOMETRY; + if (d_link->sc_state > GEOMETRY) + d_link->sc_state = GEOMETRY; if ((lp->d_flags & D_BADSECT) != 0) bad144intern(wd); } -/* - * Implement operations needed before read/write. - * Returns 0 if operation still in progress, 1 if completed. - */ -int -wdcontrol(wd) - struct wd_softc *wd; -{ - struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; - - switch (wd->sc_state) { - case RECAL: /* Set SDH, step rate, do recal. */ - if (wdcommandshort(wdc, wd->sc_drive, WDCC_RECAL) != 0) { - wderror(wd, NULL, "wdcontrol: recal failed (1)"); - goto bad; - } - wd->sc_state = RECAL_WAIT; - break; - - case RECAL_WAIT: - if (wdc->sc_status & WDCS_ERR) { - wderror(wd, NULL, "wdcontrol: recal failed (2)"); - goto bad; - } - /* fall through */ - case GEOMETRY: - if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) - goto multimode; - if (wdsetctlr(wd) != 0) { - /* Already printed a message. */ - goto bad; - } - wd->sc_state = GEOMETRY_WAIT; - break; - - case GEOMETRY_WAIT: - if (wdc->sc_status & WDCS_ERR) { - wderror(wd, NULL, "wdcontrol: geometry failed"); - goto bad; - } - /* fall through */ - case MULTIMODE: - multimode: - if (wd->sc_mode != WDM_PIOMULTI) - goto open; - outb(wdc->sc_iobase+wd_seccnt, wd->sc_multiple); - if (wdcommandshort(wdc, wd->sc_drive, WDCC_SETMULTI) != 0) { - wderror(wd, NULL, "wdcontrol: setmulti failed (1)"); - goto bad; - } - wd->sc_state = MULTIMODE_WAIT; - break; - - case MULTIMODE_WAIT: - if (wdc->sc_status & WDCS_ERR) { - wderror(wd, NULL, "wdcontrol: setmulti failed (2)"); - goto bad; - } - /* fall through */ - case OPEN: - open: - wdc->sc_errors = 0; - wd->sc_state = OPEN; - /* - * The rest of the initialization can be done by normal means. - */ - return 1; - - bad: - wdcunwedge(wdc); - return 0; - } - - wdc->sc_flags |= WDCF_ACTIVE; - timeout(wdctimeout, wdc, WAITTIME); - return 0; -} - -/* - * Wait for the drive to become ready and send a command. - * Return -1 if busy for too long or 0 otherwise. - * Assumes interrupts are blocked. - */ -int -wdcommand(wd, command, cylin, head, sector, count) - struct wd_softc *wd; - int command; - int cylin, head, sector, count; -{ - struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; - int iobase = wdc->sc_iobase; - int stat; - - /* Select drive, head, and addressing mode. */ - outb(iobase+wd_sdh, WDSD_IBM | (wd->sc_drive << 4) | head); - - /* Wait for it to become ready to accept a command. */ - if (command == WDCC_IDP) - stat = wait_for_unbusy(wdc); - else - stat = wdcwait(wdc, WDCS_DRDY); - if (stat < 0) - return -1; - - /* Load parameters. */ - if (wd->sc_dk.dk_label->d_type == DTYPE_ST506) - outb(iobase+wd_precomp, wd->sc_dk.dk_label->d_precompcyl / 4); - else - outb(iobase+wd_features, 0); - outb(iobase+wd_cyl_lo, cylin); - outb(iobase+wd_cyl_hi, cylin >> 8); - outb(iobase+wd_sector, sector); - outb(iobase+wd_seccnt, count); - - /* Send command. */ - outb(iobase+wd_command, command); - - return 0; -} - -/* - * Simplified version of wdcommand(). - */ -int -wdcommandshort(wdc, drive, command) - struct wdc_softc *wdc; - int drive; - int command; -{ - int iobase = wdc->sc_iobase; - - /* Select drive. */ - outb(iobase+wd_sdh, WDSD_IBM | (drive << 4)); - - if (wdcwait(wdc, WDCS_DRDY) < 0) - return -1; - - outb(iobase+wd_command, command); - - return 0; -} /* * Tell the drive what geometry to use. */ int -wdsetctlr(wd) - struct wd_softc *wd; +wdsetctlr(d_link) + struct wd_link *d_link; { + struct wd_softc *wd=(struct wd_softc *)d_link->wd_softc; -#ifdef WDDEBUG - printf("wd(%d,%d) C%dH%dS%d\n", wd->sc_dev.dv_unit, wd->sc_drive, - wd->sc_dk.dk_label->d_ncylinders, wd->sc_dk.dk_label->d_ntracks, - wd->sc_dk.dk_label->d_nsectors); -#endif - - if (wdcommand(wd, WDCC_IDP, wd->sc_dk.dk_label->d_ncylinders, + WDDEBUG_PRINT(("wd(%d,%d) C%dH%dS%d\n", wd->sc_dev.dv_unit, + d_link->sc_drive, wd->sc_dk.dk_label->d_ncylinders, + wd->sc_dk.dk_label->d_ntracks, wd->sc_dk.dk_label->d_nsectors)); + + if (wdccommand(d_link, WDCC_IDP, d_link->sc_drive, + wd->sc_dk.dk_label->d_ncylinders, wd->sc_dk.dk_label->d_ntracks - 1, 0, wd->sc_dk.dk_label->d_nsectors) != 0) { - wderror(wd, NULL, "wdsetctlr: geometry upload failed"); + wderror(d_link, NULL, "wdsetctlr: geometry upload failed"); return -1; } return 0; } -/* - * Get the drive parameters, if ESDI or ATA, or create fake ones for ST506. - */ int -wd_get_parms(wd) - struct wd_softc *wd; -{ - struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; - int i; - char tb[DEV_BSIZE]; - int s, error; - - /* - * XXX - * The locking done here, and the length of time this may keep the rest - * of the system suspended, is a kluge. This should be rewritten to - * set up a transfer and queue it through wdstart(), but it's called - * infrequently enough that this isn't a pressing matter. - */ - - s = splbio(); - - while ((wdc->sc_flags & WDCF_ACTIVE) != 0) { - wdc->sc_flags |= WDCF_WANTED; - if ((error = tsleep(wdc, PRIBIO | PCATCH, "wdprm", 0)) != 0) { - splx(s); - return error; - } - } - - if (wdcommandshort(wdc, wd->sc_drive, WDCC_IDENTIFY) != 0 || - wait_for_drq(wdc) != 0) { - /* - * We `know' there's a drive here; just assume it's old. - * This geometry is only used to read the MBR and print a - * (false) attach message. - */ - strncpy(wd->sc_dk.dk_label->d_typename, "ST506", - sizeof wd->sc_dk.dk_label->d_typename); - wd->sc_dk.dk_label->d_type = DTYPE_ST506; - - strncpy(wd->sc_params.wdp_model, "unknown", - sizeof wd->sc_params.wdp_model); - wd->sc_params.wdp_config = WD_CFG_FIXED; - wd->sc_params.wdp_cylinders = 1024; - wd->sc_params.wdp_heads = 8; - wd->sc_params.wdp_sectors = 17; - wd->sc_params.wdp_maxmulti = 0; - wd->sc_params.wdp_usedmovsd = 0; - wd->sc_params.wdp_capabilities = 0; - } else { - strncpy(wd->sc_dk.dk_label->d_typename, "ESDI/IDE", - sizeof wd->sc_dk.dk_label->d_typename); - wd->sc_dk.dk_label->d_type = DTYPE_ESDI; - - /* Read in parameter block. */ - insw(wdc->sc_iobase+wd_data, tb, sizeof(tb) / sizeof(short)); - bcopy(tb, &wd->sc_params, sizeof(struct wdparams)); - - /* Shuffle string byte order. */ - for (i = 0; i < sizeof(wd->sc_params.wdp_model); i += 2) { - u_short *p; - p = (u_short *)(wd->sc_params.wdp_model + i); - *p = ntohs(*p); - } - } - - /* Clear any leftover interrupt. */ - (void) inb(wdc->sc_iobase+wd_status); - - /* Restart the queue. */ - wdcstart(wdc); - - splx(s); - return 0; -} - -int -wdioctl(dev, cmd, addr, flag, p) +wdioctl(dev, xfer, addr, flag, p) dev_t dev; - u_long cmd; + u_long xfer; caddr_t addr; int flag; struct proc *p; { struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(dev)]; + struct wd_link *d_link = wd->d_link; int error; - - if ((wd->sc_flags & WDF_LOADED) == 0) + + WDDEBUG_PRINT(("wdioctl\n")); + + if ((d_link->sc_flags & WDF_LOADED) == 0) return EIO; - switch (cmd) { + switch (xfer) { case DIOCSBAD: if ((flag & FWRITE) == 0) return EBADF; @@ -1368,33 +631,33 @@ wdioctl(dev, cmd, addr, flag, p) if ((flag & FWRITE) == 0) return EBADF; - if ((error = wdlock(wd)) != 0) + if ((error = wdlock(wd->d_link)) != 0) return error; - wd->sc_flags |= WDF_LABELLING; + d_link->sc_flags |= WDF_LABELLING; error = setdisklabel(wd->sc_dk.dk_label, (struct disklabel *)addr, /*wd->sc_dk.dk_openmask : */0, wd->sc_dk.dk_cpulabel); if (error == 0) { - if (wd->sc_state > GEOMETRY) - wd->sc_state = GEOMETRY; - if (cmd == DIOCWDINFO) + if (d_link->sc_state > GEOMETRY) + d_link->sc_state = GEOMETRY; + if (xfer == DIOCWDINFO) error = writedisklabel(WDLABELDEV(dev), wdstrategy, wd->sc_dk.dk_label, wd->sc_dk.dk_cpulabel); } - wd->sc_flags &= ~WDF_LABELLING; - wdunlock(wd); + d_link->sc_flags &= ~WDF_LABELLING; + wdunlock(d_link); return error; case DIOCWLABEL: if ((flag & FWRITE) == 0) return EBADF; if (*(int *)addr) - wd->sc_flags |= WDF_WLABEL; + d_link->sc_flags |= WDF_WLABEL; else - wd->sc_flags &= ~WDF_WLABEL; + d_link->sc_flags &= ~WDF_WLABEL; return 0; #ifdef notyet @@ -1451,7 +714,9 @@ wdsize(dev) struct wd_softc *wd; int part; int size; - + + WDDEBUG_PRINT(("wdsize\n")); + if (wdopen(dev, 0, S_IFBLK, NULL) != 0) return -1; wd = wd_cd.cd_devs[WDUNIT(dev)]; @@ -1473,6 +738,9 @@ static int wddumprecalibrated; /* * Dump core after a system crash. + * + * XXX: This needs work! Currently, it's a major hack: the + * use of wdc_softc is very bad and should go away. */ int wddump(dev, blkno, va, size) @@ -1484,6 +752,7 @@ wddump(dev, blkno, va, size) struct wd_softc *wd; /* disk unit to do the I/O */ struct wdc_softc *wdc; /* disk controller to do the I/O */ struct disklabel *lp; /* disk's disklabel */ + struct wd_link *d_link; int unit, part; int nblks; /* total number of sectors left to write */ @@ -1496,13 +765,14 @@ wddump(dev, blkno, va, size) if (unit >= wd_cd.cd_ndevs) return ENXIO; wd = wd_cd.cd_devs[unit]; - if (wd == 0) + if (wd == (struct wd_softc *)0) return ENXIO; + d_link = wd->d_link; part = WDPART(dev); /* Make sure it was initialized. */ - if (wd->sc_state < OPEN) + if (d_link->sc_state < OPEN) return ENXIO; wdc = (void *)wd->sc_dev.dv_parent; @@ -1524,10 +794,10 @@ wddump(dev, blkno, va, size) /* Recalibrate, if first dump transfer. */ if (wddumprecalibrated == 0) { wddumprecalibrated = 1; - if (wdcommandshort(wdc, wd->sc_drive, WDCC_RECAL) != 0 || - wait_for_ready(wdc) != 0 || wdsetctlr(wd) != 0 || + if (wdccommandshort(wdc, d_link->sc_drive, WDCC_RECAL) != 0 || + wait_for_ready(wdc) != 0 || wdsetctlr(d_link) != 0 || wait_for_ready(wdc) != 0) { - wderror(wd, NULL, "wddump: recal failed"); + wderror(d_link, NULL, "wddump: recal failed"); return EIO; } } @@ -1540,7 +810,7 @@ wddump(dev, blkno, va, size) long blkdiff; int i; - for (i = 0; (blkdiff = wd->sc_badsect[i]) != -1; i++) { + for (i = 0; (blkdiff = d_link->sc_badsect[i]) != -1; i++) { blkdiff -= xlt_blkno; if (blkdiff < 0) continue; @@ -1554,7 +824,7 @@ wddump(dev, blkno, va, size) /* Tranfer is okay now. */ } - if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) { + if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) { sector = (xlt_blkno >> 0) & 0xff; cylin = (xlt_blkno >> 8) & 0xffff; head = (xlt_blkno >> 24) & 0xf; @@ -1570,17 +840,19 @@ wddump(dev, blkno, va, size) } #ifndef WD_DUMP_NOT_TRUSTED - if (wdcommand(wd, WDCC_WRITE, cylin, head, sector, 1) != 0 || + if (wdccommand(d_link, WDCC_WRITE, d_link->sc_drive, cylin, head, sector, 1) != 0 || wait_for_drq(wdc) != 0) { - wderror(wd, NULL, "wddump: write failed"); + wderror(d_link, NULL, "wddump: write failed"); return EIO; } - outsw(wdc->sc_iobase+wd_data, va, lp->d_secsize >> 1); + /* XXX XXX XXX */ + outsw(wdc->sc_iobase + wd_data, va, lp->d_secsize >> 1); /* Check data request (should be done). */ if (wait_for_ready(wdc) != 0) { - wderror(wd, NULL, "wddump: timeout waiting for ready"); + wderror(d_link, NULL, + "wddump: timeout waiting for ready"); return EIO; } #else /* WD_DUMP_NOT_TRUSTED */ @@ -1600,6 +872,8 @@ wddump(dev, blkno, va, size) return 0; } #else /* __BDEVSW_DUMP_NEW_TYPE */ + + int wddump(dev, blkno, va, size) dev_t dev; @@ -1622,157 +896,45 @@ bad144intern(wd) { struct dkbad *bt = &wd->sc_dk.dk_cpulabel->bad; struct disklabel *lp = wd->sc_dk.dk_label; + struct wd_link *d_link = wd->d_link; int i = 0; + WDDEBUG_PRINT(("bad144intern\n")); + for (; i < 126; i++) { if (bt->bt_bad[i].bt_cyl == 0xffff) break; - wd->sc_badsect[i] = + d_link->sc_badsect[i] = bt->bt_bad[i].bt_cyl * lp->d_secpercyl + (bt->bt_bad[i].bt_trksec >> 8) * lp->d_nsectors + (bt->bt_bad[i].bt_trksec & 0xff); } for (; i < 127; i++) - wd->sc_badsect[i] = -1; -} - -int -wdcreset(wdc) - struct wdc_softc *wdc; -{ - int iobase = wdc->sc_iobase; - - /* Reset the device. */ - outb(iobase+wd_ctlr, WDCTL_RST | WDCTL_IDS); - delay(1000); - outb(iobase+wd_ctlr, WDCTL_IDS); - delay(1000); - (void) inb(iobase+wd_error); - outb(iobase+wd_ctlr, WDCTL_4BIT); - - if (wait_for_unbusy(wdc) < 0) { - printf("%s: reset failed\n", wdc->sc_dev.dv_xname); - return 1; - } - - return 0; -} - -void -wdcrestart(arg) - void *arg; -{ - struct wdc_softc *wdc = arg; - int s; - - s = splbio(); - wdcstart(wdc); - splx(s); -} - -/* - * Unwedge the controller after an unexpected error. We do this by resetting - * it, marking all drives for recalibration, and stalling the queue for a short - * period to give the reset time to finish. - * NOTE: We use a timeout here, so this routine must not be called during - * autoconfig or dump. - */ -void -wdcunwedge(wdc) - struct wdc_softc *wdc; -{ - int unit; - - untimeout(wdctimeout, wdc); - (void) wdcreset(wdc); - - /* Schedule recalibrate for all drives on this controller. */ - for (unit = 0; unit < wd_cd.cd_ndevs; unit++) { - struct wd_softc *wd = wd_cd.cd_devs[unit]; - if (!wd || (void *)wd->sc_dev.dv_parent != wdc) - continue; - if (wd->sc_state > RECAL) - wd->sc_state = RECAL; - } - - wdc->sc_flags |= WDCF_ERROR; - ++wdc->sc_errors; - - /* Wake up in a little bit and restart the operation. */ - timeout(wdcrestart, wdc, RECOVERYTIME); -} - -int -wdcwait(wdc, mask) - struct wdc_softc *wdc; - int mask; -{ - int iobase = wdc->sc_iobase; - int timeout = 0; - u_char status; -#ifdef WDCNDELAY_DEBUG - extern int cold; -#endif - - for (;;) { - wdc->sc_status = status = inb(iobase+wd_status); - if ((status & WDCS_BSY) == 0 && (status & mask) == mask) - break; - if (++timeout > WDCNDELAY) - return -1; - delay(WDCDELAY); - } - if (status & WDCS_ERR) { - wdc->sc_error = inb(iobase+wd_error); - return WDCS_ERR; - } -#ifdef WDCNDELAY_DEBUG - /* After autoconfig, there should be no long delays. */ - if (!cold && timeout > WDCNDELAY_DEBUG) - printf("%s: warning: busy-wait took %dus\n", - wdc->sc_dev.dv_xname, WDCDELAY * timeout); -#endif - return 0; + d_link->sc_badsect[i] = -1; } void -wdctimeout(arg) - void *arg; +wderror(d_link, bp, msg) + struct wd_link *d_link; + struct buf *bp; + char *msg; { - struct wdc_softc *wdc = (struct wdc_softc *)arg; - int s; + struct wd_softc *wd = (struct wd_softc *)d_link->wd_softc; - s = splbio(); - if ((wdc->sc_flags & WDCF_ACTIVE) != 0) { - struct wd_softc *wd = wdc->sc_drives.tqh_first; - struct buf *bp = wd->sc_q.b_actf; - - wdc->sc_flags &= ~WDCF_ACTIVE; - wderror(wdc, NULL, "lost interrupt"); - printf("%s: lost interrupt: %sing %d@%s:%d\n", - wdc->sc_dev.dv_xname, - (bp->b_flags & B_READ) ? "read" : "writ", - wd->sc_nblks, wd->sc_dev.dv_xname, wd->sc_blkno); - wdcunwedge(wdc); + if (bp) { + diskerr(bp, "wd", msg, LOG_PRINTF, bp->b_bcount, + wd->sc_dk.dk_label); + printf("\n"); } else - wderror(wdc, NULL, "missing untimeout"); - splx(s); + printf("%s: %s\n", wd->sc_dev.dv_xname, msg); } void -wderror(dev, bp, msg) - void *dev; +wddone(d_link, bp) + struct wd_link *d_link; struct buf *bp; - char *msg; { - struct wd_softc *wd = dev; - struct wdc_softc *wdc = dev; + struct wd_softc *wd = (void *)d_link->wd_softc; - if (bp) { - diskerr(bp, "wd", msg, LOG_PRINTF, wd->sc_skip / DEV_BSIZE, - wd->sc_dk.dk_label); - printf("\n"); - } else - printf("%s: %s: status %b error %b\n", wdc->sc_dev.dv_xname, - msg, wdc->sc_status, WDCS_BITS, wdc->sc_error, WDERR_BITS); + disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid)); } |