From f97ca3e4fd92124b49f2ee72ca83c6ffefc1fd33 Mon Sep 17 00:00:00 2001 From: csapuntz Date: Mon, 10 Apr 2000 07:06:14 +0000 Subject: [PATCH] Added support for wd detach (merge from NetBSD). Support for SCSI/ATAPI detach is not here yet. Minor cleanup of wdc. Downgrade to UDMA mode 1 before going further. Want to stay in UDMA modes because they're more error-resilient due to a CRC. Got rid of some of the ridiculous amount of softc sharing going on. Hopefully, this will make the life of whoever goes in and fixes the ref-counting to be correct easier. --- sys/dev/ata/ata_wdc.c | 40 ++++- sys/dev/ata/atavar.h | 8 +- sys/dev/ata/wd.c | 285 ++++++++++++++++++++++------------ sys/dev/ata/wdvar.h | 4 +- sys/dev/atapiscsi/atapiscsi.c | 27 ++-- sys/dev/ic/wdc.c | 160 ++++++++++++------- sys/dev/ic/wdcvar.h | 10 +- sys/dev/pci/pciide.c | 4 +- sys/dev/pcmcia/wdc_pcmcia.c | 153 +++++++++++++++--- 9 files changed, 485 insertions(+), 206 deletions(-) diff --git a/sys/dev/ata/ata_wdc.c b/sys/dev/ata/ata_wdc.c index b9f075f1a01..32147d4ecfa 100644 --- a/sys/dev/ata/ata_wdc.c +++ b/sys/dev/ata/ata_wdc.c @@ -95,6 +95,7 @@ #define DEBUG_STATUS 0x04 #define DEBUG_FUNCS 0x08 #define DEBUG_PROBE 0x10 + #ifdef WDCDEBUG int wdcdebug_wd_mask = 0; #define WDCDEBUG_PRINT(args, level) \ @@ -115,6 +116,7 @@ struct cfdriver wdc_cd = { void wdc_ata_bio_start __P((struct channel_softc *,struct wdc_xfer *)); void _wdc_ata_bio_start __P((struct channel_softc *,struct wdc_xfer *)); int wdc_ata_bio_intr __P((struct channel_softc *, struct wdc_xfer *, int)); +void wdc_ata_bio_kill_xfer __P((struct channel_softc *,struct wdc_xfer *)); void wdc_ata_bio_done __P((struct channel_softc *, struct wdc_xfer *)); int wdc_ata_ctrl_intr __P((struct channel_softc *, struct wdc_xfer *, int)); int wdc_ata_err __P((struct ata_drive_datas *, struct ata_bio *)); @@ -148,6 +150,7 @@ wdc_ata_bio(drvp, ata_bio) xfer->c_bcount = ata_bio->bcount; xfer->c_start = wdc_ata_bio_start; xfer->c_intr = wdc_ata_bio_intr; + xfer->c_kill_xfer = wdc_ata_bio_kill_xfer; wdc_exec_xfer(chp, xfer); return (ata_bio->flags & ATA_ITSDONE) ? WDC_COMPLETE : WDC_QUEUED; } @@ -222,7 +225,7 @@ _wdc_ata_bio_start(chp, xfer) ata_delay = ATA_DELAY; else ata_delay = ATA_DELAY; -again: + again: /* * * When starting a multi-sector transfer, or doing single-sector @@ -238,7 +241,7 @@ again: long blkdiff; int i; for (i = 0; (blkdiff = ata_bio->badsect[i]) != -1; - i++) { + i++) { blkdiff -= ata_bio->blkno; if (blkdiff < 0) continue; @@ -255,7 +258,7 @@ again: } break; } - /* Transfer is okay now. */ + /* Transfer is okay now. */ } if (ata_bio->flags & ATA_LBA) { sect = (ata_bio->blkno >> 0) & 0xff; @@ -322,8 +325,8 @@ again: /* The number of blocks in the last stretch may be smaller. */ nblks = xfer->c_bcount / ata_bio->lp->d_secsize; if (ata_bio->nblks > nblks) { - ata_bio->nblks = nblks; - ata_bio->nbytes = xfer->c_bcount; + ata_bio->nblks = nblks; + ata_bio->nbytes = xfer->c_bcount; } } /* If this was a write and not using DMA, push the data. */ @@ -346,7 +349,7 @@ again: ata_bio->nbytes); } -intr: /* Wait for IRQ (either real or polled) */ + intr: /* Wait for IRQ (either real or polled) */ if ((ata_bio->flags & ATA_POLL) == 0) { chp->ch_flags |= WDCF_IRQ_WAIT; } else { @@ -357,7 +360,7 @@ intr: /* Wait for IRQ (either real or polled) */ goto again; } return; -timeout: + timeout: printf("%s:%d:%d: not ready, st=0x%02x, err=0x%02x\n", chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, chp->ch_status, chp->ch_error); @@ -506,6 +509,27 @@ end: return 1; } + +void +wdc_ata_bio_kill_xfer(chp, xfer) + struct channel_softc *chp; + struct wdc_xfer *xfer; +{ + struct ata_bio *ata_bio = xfer->cmd; + + untimeout(wdctimeout, chp); + /* remove this command from xfer queue */ + wdc_free_xfer(chp, xfer); + + ata_bio->flags |= ATA_ITSDONE; + ata_bio->error = ERR_NODEV; + ata_bio->r_error = WDCE_ABRT; + if ((ata_bio->flags & ATA_POLL) == 0) { + WDCDEBUG_PRINT(("wdc_ata_done: wddone\n"), DEBUG_XFERS); + wddone(ata_bio->wd); + } +} + void wdc_ata_bio_done(chp, xfer) struct channel_softc *chp; @@ -536,7 +560,7 @@ wdc_ata_bio_done(chp, xfer) ata_bio->flags |= ATA_ITSDONE; if ((ata_bio->flags & ATA_POLL) == 0) { WDCDEBUG_PRINT(("wdc_ata_done: wddone\n"), DEBUG_XFERS); - wddone(chp->ch_drive[drive].drv_softc); + wddone(ata_bio->wd); } WDCDEBUG_PRINT(("wdcstart from wdc_ata_done, flags 0x%x\n", chp->ch_flags), DEBUG_XFERS); diff --git a/sys/dev/ata/atavar.h b/sys/dev/ata/atavar.h index 859f76aa311..8448949adcd 100644 --- a/sys/dev/ata/atavar.h +++ b/sys/dev/ata/atavar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: atavar.h,v 1.4 1999/10/09 03:42:03 csapuntz Exp $ */ +/* $OpenBSD: atavar.h,v 1.5 2000/04/10 07:06:16 csapuntz Exp $ */ /* $NetBSD: atavar.h,v 1.13 1999/03/10 13:11:43 bouyer Exp $ */ /* @@ -55,6 +55,7 @@ struct ata_drive_datas { #define DRIVE_DMAERR 0x0100 /* Udma transfer had crc error, don't try DMA */ #define DRIVE_DSCBA 0x0200 /* DSC in buffer availability mode */ #define DRIVE_DSCWAIT 0x0400 /* In wait for DSC to be asserted */ + /* * Current setting of drive's PIO, DMA and UDMA modes. * Is initialised by the disks drivers at attach time, and may be @@ -83,7 +84,8 @@ struct ata_drive_datas { /* downgrade mode after this many successive errors */ #define NERRS_MAX 2 - struct device *drv_softc; /* ATA/PI drive's softc, can be NULL */ + char drive_name[31]; + int cf_flags; void *chnl_softc; /* channel softc */ }; @@ -151,6 +153,8 @@ struct wdc_command { void *callback_arg; /* argument passed to *callback() */ }; +extern int at_poll; + int wdc_exec_command __P((struct ata_drive_datas *, struct wdc_command*)); #define WDC_COMPLETE 0x01 #define WDC_QUEUED 0x02 diff --git a/sys/dev/ata/wd.c b/sys/dev/ata/wd.c index 5f14d80a80e..6aa3eb0d250 100644 --- a/sys/dev/ata/wd.c +++ b/sys/dev/ata/wd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: wd.c,v 1.11 2000/04/07 09:12:44 niklas Exp $ */ +/* $OpenBSD: wd.c,v 1.12 2000/04/10 07:06:16 csapuntz Exp $ */ /* $NetBSD: wd.c,v 1.193 1999/02/28 17:15:27 explorer Exp $ */ /* @@ -88,6 +88,7 @@ #if NRND > 0 #include #endif +#include #include @@ -110,6 +111,7 @@ #define WDUNIT(dev) DISKUNIT(dev) #define WDPART(dev) DISKPART(dev) +#define WDMINOR(unit, part) DISKMINOR(unit, part) #define MAKEWDDEV(maj, unit, part) MAKEDISKDEV(maj, unit, part) #define WDLABELDEV(dev) (MAKEWDDEV(major(dev), WDUNIT(dev), RAW_PART)) @@ -136,7 +138,6 @@ struct wd_softc { /* IDE disk soft states */ struct ata_bio sc_wdc_bio; /* current transfer */ struct buf *sc_bp; /* buf being transfered */ - void *wdc_softc; /* pointer to our parent */ struct ata_drive_datas *drvp; /* Our controller's infos */ int openings; struct ataparams sc_params;/* drive characteistics found */ @@ -152,6 +153,7 @@ struct wd_softc { #define WDF_LOADED 0x10 /* parameters loaded */ #define WDF_WAIT 0x20 /* waiting for resources */ #define WDF_LBA 0x40 /* using LBA mode */ + int sc_capacity; int cyl; /* actual drive parameters */ int heads; @@ -160,6 +162,7 @@ struct wd_softc { #if NRND > 0 rndsource_element_t rnd_source; #endif + void *sc_sdhook; }; #define sc_drive sc_wdc_bio.drive @@ -173,10 +176,14 @@ int wdprobe __P((struct device *, struct cfdata *, void *)); int wdprobe __P((struct device *, void *, void *)); #endif void wdattach __P((struct device *, struct device *, void *)); +int wddetach __P((struct device *, int)); +int wdactivate __P((struct device *, enum devact)); +void wdzeroref __P((struct device *)); int wdprint __P((void *, char *)); struct cfattach wd_ca = { - sizeof(struct wd_softc), wdprobe, wdattach + sizeof(struct wd_softc), wdprobe, wdattach, + wddetach, wdactivate, wdzeroref }; #ifdef __OpenBSD__ @@ -228,8 +235,11 @@ bdev_decl(wd); #ifdef DKBAD void bad144intern __P((struct wd_softc *)); #endif -int wdlock __P((struct wd_softc *)); -void wdunlock __P((struct wd_softc *)); + +#define wdlock(wd) disk_lock(&(wd)->sc_dk) +#define wdunlock(wd) disk_unlock(&(wd)->sc_dk) +#define wdlookup(unit) (struct wd_softc *)device_lookup(&wd_cd, (unit)) + int wdprobe(parent, match_, aux) @@ -282,13 +292,14 @@ wdattach(parent, self, aux) WDCDEBUG_PRINT(("wdattach\n"), DEBUG_FUNCS | DEBUG_PROBE); wd->openings = aa_link->aa_openings; - wd->drvp = aa_link->aa_drv_data;; - wd->wdc_softc = parent; - /* give back our softc to our caller */ - wd->drvp->drv_softc = &wd->sc_dev; + wd->drvp = aa_link->aa_drv_data; + + strncpy(wd->drvp->drive_name, wd->sc_dev.dv_xname, + sizeof(wd->drvp->drive_name) - 1); + wd->drvp->cf_flags = wd->sc_dev.dv_cfdata->cf_flags; /* read our drive info */ - if (wd_get_params(wd, AT_POLL, &wd->sc_params) != 0) { + if (wd_get_params(wd, at_poll, &wd->sc_params) != 0) { printf("%s: IDENTIFY failed\n", wd->sc_dev.dv_xname); return; } @@ -364,7 +375,8 @@ wdattach(parent, self, aux) wd->sc_dk.dk_name = wd->sc_dev.dv_xname; disk_attach(&wd->sc_dk); wd->sc_wdc_bio.lp = wd->sc_dk.dk_label; - if (shutdownhook_establish(wd_shutdown, wd) == NULL) + wd->sc_sdhook = shutdownhook_establish(wd_shutdown, wd); + if (wd->sc_sdhook == NULL) printf("%s: WARNING: unable to establish shutdown hook\n", wd->sc_dev.dv_xname); #if NRND > 0 @@ -373,6 +385,79 @@ wdattach(parent, self, aux) #endif } +int +wdactivate(self, act) + struct device *self; + enum devact act; +{ + int rv = 0; + + switch (act) { + case DVACT_ACTIVATE: + break; + + case DVACT_DEACTIVATE: + /* + * Nothing to do; we key off the device's DVF_ACTIVATE. + */ + break; + } + return (rv); +} + + +int +wddetach(self, flags) + struct device *self; + int flags; +{ + struct wd_softc *sc = (struct wd_softc *)self; + struct buf *dp, *bp; + int s, bmaj, cmaj, mn; + + /* Remove unprocessed buffers from queue */ + s = splbio(); + for (dp = &sc->sc_q; (bp = dp->b_actf) != NULL; ) { + dp->b_actf = bp->b_actf; + + bp->b_error = ENXIO; + bp->b_flags |= B_ERROR; + biodone(bp); + } + splx(s); + + /* locate the major number */ + mn = WDMINOR(self->dv_unit, 0); + + for (bmaj = 0; bmaj < nblkdev; bmaj++) + if (bdevsw[bmaj].d_open == wdopen) + vdevgone(bmaj, mn, mn + MAXPARTITIONS - 1, VBLK); + for (cmaj = 0; cmaj < nchrdev; cmaj++) + if (cdevsw[cmaj].d_open == wdopen) + vdevgone(cmaj, mn, mn + MAXPARTITIONS - 1, VCHR); + + /* Get rid of the shutdown hook. */ + if (sc->sc_sdhook != NULL) + shutdownhook_disestablish(sc->sc_sdhook); + +#if NRND > 0 + /* Unhook the entropy source. */ + rnd_detach_source(&sc->rnd_source); +#endif + + return (0); +} + +void +wdzeroref(self) + struct device *self; +{ + struct wd_softc *sc = (struct wd_softc *)self; + + /* Detach disk. */ + disk_detach(&sc->sc_dk); +} + /* * Read/write routine for a buffer. Validates the arguments and schedules the * transfer. Does not wait for the transfer to complete. @@ -381,8 +466,15 @@ void wdstrategy(bp) struct buf *bp; { - struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(bp->b_dev)]; + struct wd_softc *wd; int s; + + wd = wdlookup(WDUNIT(bp->b_dev)); + if (wd == NULL) { + bp->b_error = ENXIO; + goto bad; + } + WDCDEBUG_PRINT(("wdstrategy (%s)\n", wd->sc_dev.dv_xname), DEBUG_XFERS); @@ -417,6 +509,7 @@ wdstrategy(bp) disksort(&wd->sc_q, bp); wdstart(wd); splx(s); + device_unref(&wd->sc_dev); return; bad: bp->b_flags |= B_ERROR; @@ -424,6 +517,8 @@ done: /* Toss transfer; we're done early. */ bp->b_resid = bp->b_bcount; biodone(bp); + if (wd != NULL) + device_unref(&wd->sc_dev); } /* @@ -486,6 +581,7 @@ __wdstart(wd, bp) wd->sc_wdc_bio.flags |= ATA_READ; wd->sc_wdc_bio.bcount = bp->b_bcount; wd->sc_wdc_bio.databuf = bp->b_data; + wd->sc_wdc_bio.wd = wd; /* Instrumentation. */ disk_busy(&wd->sc_dk); switch (wdc_ata_bio(wd->drvp, &wd->sc_wdc_bio)) { @@ -515,6 +611,10 @@ wddone(v) bp->b_resid = wd->sc_wdc_bio.bcount; errbuf[0] = '\0'; switch (wd->sc_wdc_bio.error) { + case ERR_NODEV: + bp->b_flags |= B_ERROR; + bp->b_error = ENXIO; + break; case ERR_DMA: errbuf = "DMA error"; goto retry; @@ -530,7 +630,8 @@ wddone(v) (wd->sc_wdc_bio.r_error & ~(WDCE_MC | WDCE_MCR)) == 0) goto noerror; ata_perror(wd->drvp, wd->sc_wdc_bio.r_error, errbuf); -retry: /* Just reset and retry. Can we do more ? */ +retry: + /* Just reset and retry. Can we do more ? */ wdc_reset_channel(wd->drvp); diskerr(bp, "wd", errbuf, LOG_PRINTF, wd->sc_wdc_bio.blkdone, wd->sc_dk.dk_label); @@ -594,53 +695,6 @@ wdwrite(dev, uio, flags) return (physio(wdstrategy, NULL, dev, B_WRITE, minphys, uio)); } -/* - * 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; -{ - int error; - int s; - - WDCDEBUG_PRINT(("wdlock\n"), DEBUG_FUNCS); - - s = splbio(); - - while ((wd->sc_flags & WDF_LOCKED) != 0) { - wd->sc_flags |= WDF_WANTED; - if ((error = tsleep(wd, PRIBIO | PCATCH, - "wdlck", 0)) != 0) { - splx(s); - return error; - } - } - wd->sc_flags |= WDF_LOCKED; - splx(s); - return 0; -} - -/* - * Unlock and wake up any waiters. - */ -void -wdunlock(wd) - struct wd_softc *wd; -{ - - WDCDEBUG_PRINT(("wdunlock\n"), DEBUG_FUNCS); - - wd->sc_flags &= ~WDF_LOCKED; - if ((wd->sc_flags & WDF_WANTED) != 0) { - wd->sc_flags &= ~WDF_WANTED; - wakeup(wd); - } -} - int wdopen(dev, flag, fmt, p) dev_t dev; @@ -652,10 +706,9 @@ wdopen(dev, flag, fmt, p) int error; WDCDEBUG_PRINT(("wdopen\n"), DEBUG_FUNCS); + unit = WDUNIT(dev); - if (unit >= wd_cd.cd_ndevs) - return ENXIO; - wd = wd_cd.cd_devs[unit]; + wd = wdlookup(unit); if (wd == NULL) return ENXIO; @@ -717,6 +770,7 @@ wdopen(dev, flag, fmt, p) wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask; wdunlock(wd); + device_unref(&wd->sc_dev); return 0; bad: @@ -730,6 +784,7 @@ bad4: if (wd->sc_dk.dk_openmask == 0) wdc_ata_delref(wd->drvp); #endif + device_unref(&wd->sc_dev); return error; } @@ -739,13 +794,17 @@ wdclose(dev, flag, fmt, p) int flag, fmt; struct proc *p; { - struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(dev)]; + struct wd_softc *wd; int part = WDPART(dev); - int error; + int error = 0; + + wd = wdlookup(WDUNIT(dev)); + if (wd == NULL) + return ENXIO; WDCDEBUG_PRINT(("wdclose\n"), DEBUG_FUNCS); if ((error = wdlock(wd)) != 0) - return error; + goto exit; switch (fmt) { case S_IFCHR: @@ -767,7 +826,10 @@ wdclose(dev, flag, fmt, p) } wdunlock(wd); - return 0; + + exit: + device_unref(&wd->sc_dev); + return (error); } void @@ -775,7 +837,6 @@ wdgetdefaultlabel(wd, lp) struct wd_softc *wd; struct disklabel *lp; { - WDCDEBUG_PRINT(("wdgetdefaultlabel\n"), DEBUG_FUNCS); bzero(lp, sizeof(struct disklabel)); @@ -867,13 +928,19 @@ wdioctl(dev, xfer, addr, flag, p) int flag; struct proc *p; { - struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(dev)]; - int error; + struct wd_softc *wd; + int error = 0; WDCDEBUG_PRINT(("wdioctl\n"), DEBUG_FUNCS); - if ((wd->sc_flags & WDF_LOADED) == 0) - return EIO; + wd = wdlookup(WDUNIT(dev)); + if (wd == NULL) + return ENXIO; + + if ((wd->sc_flags & WDF_LOADED) == 0) { + error = EIO; + goto exit; + } switch (xfer) { #ifdef DKBAD @@ -883,38 +950,40 @@ wdioctl(dev, xfer, addr, flag, p) DKBAD(wd->sc_dk.dk_cpulabel) = *(struct dkbad *)addr; wd->sc_dk.dk_label->d_flags |= D_BADSECT; bad144intern(wd); - return 0; + goto exit; #endif case DIOCRLDINFO: wdgetdisklabel(dev, wd, wd->sc_dk.dk_label, wd->sc_dk.dk_cpulabel, 0); - return 0; + goto exit; case DIOCGPDINFO: { struct cpu_disklabel osdep; wdgetdisklabel(dev, wd, (struct disklabel *)addr, &osdep, 1); - return 0; + goto exit; } case DIOCGDINFO: *(struct disklabel *)addr = *(wd->sc_dk.dk_label); - return 0; + goto exit; case DIOCGPART: ((struct partinfo *)addr)->disklab = wd->sc_dk.dk_label; ((struct partinfo *)addr)->part = &wd->sc_dk.dk_label->d_partitions[WDPART(dev)]; - return 0; + goto exit; case DIOCWDINFO: case DIOCSDINFO: - if ((flag & FWRITE) == 0) - return EBADF; + if ((flag & FWRITE) == 0) { + error = EBADF; + goto exit; + } if ((error = wdlock(wd)) != 0) - return error; + goto exit; wd->sc_flags |= WDF_LABELLING; error = setdisklabel(wd->sc_dk.dk_label, @@ -931,21 +1000,24 @@ wdioctl(dev, xfer, addr, flag, p) wd->sc_flags &= ~WDF_LABELLING; wdunlock(wd); - return error; + goto exit; case DIOCWLABEL: - if ((flag & FWRITE) == 0) - return EBADF; + if ((flag & FWRITE) == 0) { + error = EBADF; + goto exit; + } + if (*(int *)addr) wd->sc_flags |= WDF_WLABEL; else wd->sc_flags &= ~WDF_WLABEL; - return 0; + goto exit; #ifndef __OpenBSD__ case DIOCGDEFLABEL: wdgetdefaultlabel(wd, (struct disklabel *)addr); - return 0; + goto exit; #endif #ifdef notyet @@ -972,7 +1044,7 @@ wdioctl(dev, xfer, addr, flag, p) fop->df_count -= auio.uio_resid; fop->df_reg[0] = wdc->sc_status; fop->df_reg[1] = wdc->sc_error; - return error; + goto exit; } #endif @@ -981,8 +1053,10 @@ wdioctl(dev, xfer, addr, flag, p) * Make sure this command is (relatively) safe first */ if ((((atareq_t *) addr)->flags & ATACMD_READ) == 0 && - (flag & FWRITE) == 0) - return (EBADF); + (flag & FWRITE) == 0) { + error = EBADF; + goto exit; + } { struct wd_ioctl *wi; atareq_t *atareq = (atareq_t *) addr; @@ -1020,16 +1094,21 @@ wdioctl(dev, xfer, addr, flag, p) } *atareq = wi->wi_atareq; wi_free(wi); - return(error); + goto exit; } default: - return ENOTTY; + error = ENOTTY; + goto exit; } #ifdef DIAGNOSTIC panic("wdioctl: impossible"); #endif + + exit: + device_unref(&wd->sc_dev); + return (error); } #ifdef B_FORMAT @@ -1047,30 +1126,33 @@ wdsize(dev) dev_t dev; { struct wd_softc *wd; - int part, unit, omask; + int part, omask; int size; WDCDEBUG_PRINT(("wdsize\n"), DEBUG_FUNCS); - unit = WDUNIT(dev); - if (unit >= wd_cd.cd_ndevs) - return (-1); - wd = wd_cd.cd_devs[unit]; + wd = wdlookup(WDUNIT(dev)); if (wd == NULL) return (-1); part = WDPART(dev); omask = wd->sc_dk.dk_openmask & (1 << part); - if (omask == 0 && wdopen(dev, 0, S_IFBLK, NULL) != 0) - return (-1); + if (omask == 0 && wdopen(dev, 0, S_IFBLK, NULL) != 0) { + size = -1; + goto exit; + } + if (wd->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) size = -1; else size = wd->sc_dk.dk_label->d_partitions[part].p_size * (wd->sc_dk.dk_label->d_secsize / DEV_BSIZE); if (omask == 0 && wdclose(dev, 0, S_IFBLK, NULL) != 0) - return (-1); + size = -1; + + exit: + device_unref(&wd->sc_dev); return (size); } @@ -1103,10 +1185,8 @@ wddump(dev, blkno, va, size) wddoingadump = 1; unit = WDUNIT(dev); - if (unit >= wd_cd.cd_ndevs) - return ENXIO; - wd = wd_cd.cd_devs[unit]; - if (wd == (struct wd_softc *)0) + wd = wdlookup(unit); + if (wd == NULL) return ENXIO; part = WDPART(dev); @@ -1147,6 +1227,7 @@ again: wd->sc_wdc_bio.bcount = min(nblks, wddumpmulti) * lp->d_secsize; wd->sc_wdc_bio.databuf = va; + wd->sc_wdc_bio.wd = wd; #ifndef WD_DUMP_NOT_TRUSTED switch (wdc_ata_bio(wd->drvp, &wd->sc_wdc_bio)) { case WDC_TRY_AGAIN: diff --git a/sys/dev/ata/wdvar.h b/sys/dev/ata/wdvar.h index 46fb4ee0c18..5dc67ffedbe 100644 --- a/sys/dev/ata/wdvar.h +++ b/sys/dev/ata/wdvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: wdvar.h,v 1.1 1999/07/18 21:25:18 csapuntz Exp $ */ +/* $OpenBSD: wdvar.h,v 1.2 2000/04/10 07:06:16 csapuntz Exp $ */ /* $NetBSD: wdvar.h,v 1.3 1998/11/11 19:38:27 bouyer Exp $ */ /* @@ -58,8 +58,10 @@ struct ata_bio { #define ERR_DF 2 /* Drive fault */ #define ERR_DMA 3 /* DMA error */ #define TIMEOUT 4 /* device timed out */ +#define ERR_NODEV 5 /* device bas been detached */ u_int8_t r_error; /* copy of error register */ daddr_t badsect[127]; /* 126 plus trailing -1 marker */ + struct wd_softc *wd; }; /* drive states stored in ata_drive_datas */ diff --git a/sys/dev/atapiscsi/atapiscsi.c b/sys/dev/atapiscsi/atapiscsi.c index d888f693f1a..d8a5aab4215 100644 --- a/sys/dev/atapiscsi/atapiscsi.c +++ b/sys/dev/atapiscsi/atapiscsi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: atapiscsi.c,v 1.24 2000/01/12 17:14:02 csapuntz Exp $ */ +/* $OpenBSD: atapiscsi.c,v 1.25 2000/04/10 07:06:17 csapuntz Exp $ */ /* * This code is derived from code with the copyright below. @@ -241,8 +241,11 @@ atapiscsi_attach(parent, self, aux) (wdc_atapi_get_params(chp, drive, id) == COMPLETE)) { /* Temporarily, the device will be called atapiscsi. */ - drvp->drv_softc = (struct device*)as; - wdc_probe_caps(drvp, id); + strncpy(drvp->drive_name, as->sc_dev.dv_xname, + sizeof(drvp->drive_name) - 1); + drvp->cf_flags = as->sc_dev.dv_cfdata->cf_flags; + + wdc_probe_caps(drvp, id); WDCDEBUG_PRINT( ("general config %04x capabilities %04x ", @@ -283,10 +286,13 @@ atapiscsi_attach(parent, self, aux) struct scsi_link *link = scsi->sc_link[drive][0]; struct ata_drive_datas *drvp = &chp->ch_drive[drive]; - if (drvp->drv_softc == (struct device *)as && link) { - drvp->drv_softc = link->device_softc; - wdc_print_caps(drvp); - } + if (!link) continue; + + strncpy(drvp->drive_name, + ((struct device *)(link->device_softc))->dv_xname, + sizeof(drvp->drive_name) - 1); + + wdc_print_caps(drvp); } } } @@ -344,7 +350,7 @@ wdc_atapi_get_params(chp, drive, id) wdc_c.r_command = ATAPI_SOFT_RESET; wdc_c.r_st_bmask = 0; wdc_c.r_st_pmask = 0; - wdc_c.flags = AT_POLL; + wdc_c.flags = at_poll; wdc_c.timeout = ATAPI_RESET_WAIT; if (wdc_exec_command(drvp, &wdc_c) != WDC_COMPLETE) { printf("wdc_atapi_get_params: ATAPI_SOFT_RESET failed for" @@ -367,7 +373,7 @@ wdc_atapi_get_params(chp, drive, id) delay(5000); retry: - if (ata_get_params(drvp, AT_POLL, id) != 0) { + if (ata_get_params(drvp, at_poll, id) != 0) { WDCDEBUG_PRINT(("wdc_atapi_get_params: ATAPI_IDENTIFY_DEVICE " "failed for drive %s:%d:%d\n", chp->wdc->sc_dev.dv_xname, chp->channel, drive), @@ -1451,7 +1457,6 @@ wdc_atapi_done(chp, xfer, timeout) int timeout; { struct scsi_xfer *sc_xfer = xfer->cmd; - int need_done = xfer->c_flags & C_NEEDDONE; struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; int doing_dma = xfer->c_flags & C_DMA; @@ -1471,7 +1476,7 @@ wdc_atapi_done(chp, xfer, timeout) wdc_downgrade_mode(drvp); } - if (need_done) { + if (!(xfer->c_flags & C_POLL)) { WDCDEBUG_PRINT(("wdc_atapi_done: scsi_done\n"), DEBUG_XFERS); scsi_done(sc_xfer); } diff --git a/sys/dev/ic/wdc.c b/sys/dev/ic/wdc.c index 0601039499e..6760fd1bee4 100644 --- a/sys/dev/ic/wdc.c +++ b/sys/dev/ic/wdc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: wdc.c,v 1.15 1999/12/14 08:28:15 csapuntz Exp $ */ +/* $OpenBSD: wdc.c,v 1.16 2000/04/10 07:06:14 csapuntz Exp $ */ /* $NetBSD: wdc.c,v 1.68 1999/06/23 19:00:17 bouyer Exp $ */ @@ -109,6 +109,7 @@ void __wdccommand_done __P((struct channel_softc *, struct wdc_xfer *)); void __wdccommand_start __P((struct channel_softc *, struct wdc_xfer *)); int __wdccommand_intr __P((struct channel_softc *, struct wdc_xfer *, int)); int wdprint __P((void *, const char *)); +void wdc_kill_pending __P((struct channel_softc *)); #define DEBUG_INTR 0x01 @@ -118,6 +119,7 @@ int wdprint __P((void *, const char *)); #define DEBUG_PROBE 0x10 #define DEBUG_STATUSX 0x20 #define DEBUG_SDRIVE 0x40 +#define DEBUG_DETACH 0x80 #ifdef WDCDEBUG int wdcdebug_mask = 0; @@ -127,6 +129,7 @@ int wdc_nxfer = 0; #define WDCDEBUG_PRINT(args, level) #endif +int at_poll = AT_POLL; u_int8_t wdc_default_read_reg __P((struct channel_softc *, enum wdc_regs)); void wdc_default_write_reg __P((struct channel_softc *, enum wdc_regs, u_int8_t)); @@ -366,7 +369,6 @@ wdcprobe(chp) /* * Sanity check to see if the wdc channel responds at all. */ - if (chp->wdc == NULL || (chp->wdc->cap & WDC_CAPABILITY_NO_EXTRA_RESETS) == 0) { CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM); @@ -384,7 +386,7 @@ wdcprobe(chp) ret_value &= ~0x01; if (st1 == 0xff) ret_value &= ~0x02; - if (ret_value == 0) + if (ret_value == 0) return 0; } @@ -446,6 +448,24 @@ wdcprobe(chp) return (ret_value); } +/* + * Call activate routine of underlying devices. + */ +int +wdcactivate(self, act) + struct device *self; + enum devact act; +{ + int error = 0; + int s; + + s = splbio(); + config_activate_children(self, act); + splx(s); + + return (error); +} + void wdcattach(chp) struct channel_softc *chp; @@ -457,6 +477,10 @@ wdcattach(chp) struct ata_atapi_attach aa_link; struct ataparams params; static int inited = 0; + extern int cold; + + if (!cold) + at_poll = AT_WAIT; #ifndef __OpenBSD__ if ((error = wdc_addref(chp)) != 0) { @@ -482,7 +506,7 @@ wdcattach(chp) inited++; } TAILQ_INIT(&chp->ch_queue->sc_xfer); - + for (i = 0; i < 2; i++) { chp->ch_drive[i].chnl_softc = chp; chp->ch_drive[i].drive = i; @@ -498,7 +522,7 @@ wdcattach(chp) chp->ch_flags |= WDCF_ONESLAVE; /* Issue a IDENTIFY command, to try to detect slave ghost */ - if (ata_get_params(&chp->ch_drive[i], AT_POLL, ¶ms) == + if (ata_get_params(&chp->ch_drive[i], at_poll, ¶ms) == CMD_OK) { /* If IDENTIFY succeded, this is not an OLD ctrl */ chp->ch_drive[0].drive_flags &= ~DRIVE_OLD; @@ -600,7 +624,7 @@ wdcattach(chp) * ones */ for (i = 0; i < 2; i++) { - if (chp->ch_drive[i].drv_softc == NULL) + if (chp->ch_drive[i].drive_name[0] == 0) chp->ch_drive[i].drive_flags = 0; else chp->ch_drive[i].state = 0; @@ -685,6 +709,22 @@ wdcstart(chp) xfer->c_start(chp, xfer); } +int +wdcdetach(chp, flags) + struct channel_softc *chp; + int flags; +{ + int s, rv; + + s = splbio(); + wdc_kill_pending(chp); + + rv = config_detach_children((struct device *)chp->wdc, flags); + splx(s); + + return (rv); +} + /* restart an interrupted I/O */ void wdcrestart(v) @@ -711,6 +751,7 @@ wdcintr(arg) { struct channel_softc *chp = arg; struct wdc_xfer *xfer; + int ret; if ((chp->ch_flags & WDCF_IRQ_WAIT) == 0) { WDCDEBUG_PRINT(("wdcintr: inactive controller\n"), DEBUG_INTR); @@ -721,7 +762,12 @@ wdcintr(arg) untimeout(wdctimeout, chp); chp->ch_flags &= ~WDCF_IRQ_WAIT; xfer = chp->ch_queue->sc_xfer.tqh_first; - return xfer->c_intr(chp, xfer, 1); + ret = xfer->c_intr(chp, xfer, 1); +#if notyet + if (ret == 0) + chp->ch_flags |= WDCF_IRQ_WAIT; +#endif + return (ret); } /* Put all disk in RESET state */ @@ -746,6 +792,9 @@ wdcreset(chp, verb) { int drv_mask1, drv_mask2; + if (!chp->_vtbl) + chp->_vtbl = &wdc_default_vtbl; + CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM); /* master */ CHP_WRITE_REG(chp, wdr_ctlr, WDCTL_RST | WDCTL_IDS); delay(1000); @@ -941,10 +990,9 @@ wdc_probe_caps(drvp, params) struct ataparams *params; { struct channel_softc *chp = drvp->chnl_softc; - struct device *drv_dev = drvp->drv_softc; struct wdc_softc *wdc = chp->wdc; int i, printed; - int cf_flags; + int cf_flags = drvp->cf_flags; if ((wdc->cap & (WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32)) == (WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32)) { @@ -956,7 +1004,7 @@ wdc_probe_caps(drvp, params) * and compare results. */ drvp->drive_flags |= DRIVE_CAP32; - ata_get_params(drvp, AT_POLL, ¶ms2); + ata_get_params(drvp, at_poll, ¶ms2); if (bcmp(params, ¶ms2, sizeof(struct ataparams)) != 0) { /* Not good. fall back to 16bits */ drvp->drive_flags &= ~DRIVE_CAP32; @@ -1006,7 +1054,7 @@ wdc_probe_caps(drvp, params) */ if ((wdc->cap & WDC_CAPABILITY_MODE) != 0) if (ata_set_mode(drvp, 0x08 | (i + 3), - AT_POLL) != CMD_OK) + at_poll) != CMD_OK) continue; if (!printed) { printed = 1; @@ -1036,7 +1084,7 @@ wdc_probe_caps(drvp, params) continue; if ((wdc->cap & WDC_CAPABILITY_DMA) && (wdc->cap & WDC_CAPABILITY_MODE)) - if (ata_set_mode(drvp, 0x20 | i, AT_POLL) + if (ata_set_mode(drvp, 0x20 | i, at_poll) != CMD_OK) continue; if (!printed) { @@ -1060,7 +1108,7 @@ wdc_probe_caps(drvp, params) if ((wdc->cap & WDC_CAPABILITY_MODE) && (wdc->cap & WDC_CAPABILITY_UDMA)) if (ata_set_mode(drvp, 0x40 | i, - AT_POLL) != CMD_OK) + at_poll) != CMD_OK) continue; if (wdc->cap & WDC_CAPABILITY_UDMA) { if ((wdc->cap & WDC_CAPABILITY_MODE) && @@ -1082,7 +1130,6 @@ wdc_probe_caps(drvp, params) else if (drvp->PIO_cap > 2) drvp->ata_vers = 2; /* should be at last ATA-2 */ } - cf_flags = drv_dev->dv_cfdata->cf_flags; if (cf_flags & ATA_CONFIG_PIO_SET) { drvp->PIO_mode = (cf_flags & ATA_CONFIG_PIO_MODES) >> ATA_CONFIG_PIO_OFF; @@ -1178,9 +1225,7 @@ void wdc_print_caps(drvp) struct ata_drive_datas *drvp; { - struct device *drv_dev = drvp->drv_softc; - - printf("%s: can use ", drv_dev->dv_xname); + printf("%s: can use ", drvp->drive_name); if (drvp->drive_flags & DRIVE_CAP32) { printf("32-bit"); @@ -1209,9 +1254,8 @@ wdc_downgrade_mode(drvp) struct ata_drive_datas *drvp; { struct channel_softc *chp = drvp->chnl_softc; - struct device *drv_dev = drvp->drv_softc; struct wdc_softc *wdc = chp->wdc; - int cf_flags = drv_dev->dv_cfdata->cf_flags; + int cf_flags = drvp->cf_flags; /* if drive or controller don't know its mode, we can't do much */ if ((drvp->drive_flags & DRIVE_MODE) == 0 || @@ -1227,31 +1271,29 @@ wdc_downgrade_mode(drvp) * If we were using Ultra-DMA mode > 2, downgrade to mode 2 first. * Maybe we didn't properly notice the cable type */ - if ((drvp->drive_flags & DRIVE_UDMA) && drvp->UDMA_mode > 2) { - drvp->UDMA_mode = 2; - printf("%s: transfer error, downgrading to DMA mode %d\n", - drv_dev->dv_xname, drvp->UDMA_mode); - } - - /* - * If we were using ultra-DMA, don't downgrade to multiword DMA - * if we noticed a CRC error. It has been noticed that CRC errors - * in ultra-DMA lead to silent data corruption in multiword DMA. - * Data corruption is less likely to occur in PIO mode. - */ - - if ((drvp->drive_flags & DRIVE_UDMA) && + if ((drvp->drive_flags & DRIVE_UDMA) && drvp->UDMA_mode >= 2) { + drvp->UDMA_mode = (drvp->UDMA_mode == 2) ? 1 : 2; + printf("%s: transfer error, downgrading to Ultra-DMA mode %d\n", + drvp->drive_name, drvp->UDMA_mode); + } else if ((drvp->drive_flags & DRIVE_UDMA) && (drvp->drive_flags & DRIVE_DMAERR) == 0) { + /* + * If we were using ultra-DMA, don't downgrade to + * multiword DMA if we noticed a CRC error. It has + * been noticed that CRC errors in ultra-DMA lead to + * silent data corruption in multiword DMA. Data + * corruption is less likely to occur in PIO mode. + */ drvp->drive_flags &= ~DRIVE_UDMA; drvp->drive_flags |= DRIVE_DMA; drvp->DMA_mode = drvp->DMA_cap; printf("%s: transfer error, downgrading to DMA mode %d\n", - drv_dev->dv_xname, drvp->DMA_mode); + drvp->drive_name, drvp->DMA_mode); } else if (drvp->drive_flags & (DRIVE_DMA | DRIVE_UDMA)) { drvp->drive_flags &= ~(DRIVE_DMA | DRIVE_UDMA); drvp->PIO_mode = drvp->PIO_cap; printf("%s: transfer error, downgrading to PIO mode %d\n", - drv_dev->dv_xname, drvp->PIO_mode); + drvp->drive_name, drvp->PIO_mode); } else /* already using PIO, can't downgrade */ return 0; @@ -1289,6 +1331,7 @@ wdc_exec_command(drvp, wdc_c) xfer->cmd = wdc_c; xfer->c_start = __wdccommand_start; xfer->c_intr = __wdccommand_intr; + xfer->c_kill_xfer = __wdccommand_done; s = splbio(); wdc_exec_xfer(chp, xfer); @@ -1413,7 +1456,6 @@ __wdccommand_done(chp, xfer) struct channel_softc *chp; struct wdc_xfer *xfer; { - int needdone = xfer->c_flags & C_NEEDDONE; struct wdc_command *wdc_c = xfer->cmd; WDCDEBUG_PRINT(("__wdccommand_done %s:%d:%d\n", @@ -1442,12 +1484,12 @@ __wdccommand_done(chp, xfer) } wdc_free_xfer(chp, xfer); WDCDEBUG_PRINT(("__wdccommand_done before callback\n"), DEBUG_INTR); - if (needdone) { - if (wdc_c->flags & AT_WAIT) - wakeup(wdc_c); - else + + if (wdc_c->flags & AT_WAIT) + wakeup(wdc_c); + else + if (wdc_c->callback) wdc_c->callback(wdc_c->callback_arg); - } wdcstart(chp); WDCDEBUG_PRINT(("__wdccommand_done returned\n"), DEBUG_INTR); return; @@ -1532,7 +1574,6 @@ wdc_exec_xfer(chp, xfer) WDCDEBUG_PRINT(("wdcstart from wdc_exec_xfer, flags 0x%x\n", chp->ch_flags), DEBUG_XFERS); wdcstart(chp); - xfer->c_flags |= C_NEEDDONE; /* we can now call upper level done() */ } struct wdc_xfer * @@ -1591,6 +1632,24 @@ wdc_free_xfer(chp, xfer) splx(s); } + +/* + * Kill off all pending xfers for a channel_softc. + * + * Must be called at splbio(). + */ +void +wdc_kill_pending(chp) + struct channel_softc *chp; +{ + struct wdc_xfer *xfer; + + while ((xfer = TAILQ_FIRST(&chp->ch_queue->sc_xfer)) != NULL) { + chp = xfer->chp; + (*xfer->c_kill_xfer)(chp, xfer); + } +} + static void __wdcerror(chp, msg) struct channel_softc *chp; @@ -1600,18 +1659,11 @@ __wdcerror(chp, msg) if (xfer == NULL) printf("%s:%d: %s\n", chp->wdc->sc_dev.dv_xname, chp->channel, msg); - else { - struct device *drv_dev = chp->ch_drive[xfer->drive].drv_softc; - - if (drv_dev) { - printf("%s(%s:%d:%d): %s\n", drv_dev->dv_xname, - chp->wdc->sc_dev.dv_xname, - chp->channel, xfer->drive, msg); - } else { - printf("%s:%d:%d: %s\n", chp->wdc->sc_dev.dv_xname, - chp->channel, xfer->drive, msg); - } - } + else + printf("%s(%s:%d:%d): %s\n", + chp->ch_drive[xfer->drive].drive_name, + chp->wdc->sc_dev.dv_xname, + chp->channel, xfer->drive, msg); } /* diff --git a/sys/dev/ic/wdcvar.h b/sys/dev/ic/wdcvar.h index efc9a1ca121..3aedbec42b0 100644 --- a/sys/dev/ic/wdcvar.h +++ b/sys/dev/ic/wdcvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: wdcvar.h,v 1.7 1999/12/14 18:07:43 csapuntz Exp $ */ +/* $OpenBSD: wdcvar.h,v 1.8 2000/04/10 07:06:15 csapuntz Exp $ */ /* $NetBSD: wdcvar.h,v 1.17 1999/04/11 20:50:29 bouyer Exp $ */ /*- @@ -70,7 +70,7 @@ struct channel_softc { /* Per channel data */ u_int8_t ch_error; /* copy of error register */ /* per-drive infos */ struct ata_drive_datas ch_drive[2]; - + /* * channel queues. May be the same for all channels, if hw channels * are not independants @@ -205,6 +205,7 @@ struct wdc_xfer { void (*c_start) __P((struct channel_softc *, struct wdc_xfer *)); int (*c_intr) __P((struct channel_softc *, struct wdc_xfer *, int)); int (*c_done) __P((struct channel_softc *, struct wdc_xfer *, int)); + void (*c_kill_xfer) __P((struct channel_softc *, struct wdc_xfer *)); /* Used by ATAPISCSI */ int timeout; @@ -226,6 +227,8 @@ struct wdc_xfer { int wdcprobe __P((struct channel_softc *)); void wdcattach __P((struct channel_softc *)); +int wdcdetach __P((struct channel_softc *, int)); +int wdcactivate __P((struct device *, enum devact)); int wdcintr __P((void *)); void wdc_exec_xfer __P((struct channel_softc *, struct wdc_xfer *)); struct wdc_xfer *wdc_get_xfer __P((int)); /* int = WDC_NOSLEEP/CANSLEEP */ @@ -238,7 +241,8 @@ int wdcreset __P((struct channel_softc *, int)); #define VERBOSE 1 #define SILENT 0 /* wdcreset will not print errors */ int wdcwait __P((struct channel_softc *, int, int, int)); -void wdcbit_bucket __P(( struct channel_softc *, int)); +void wdcbit_bucket __P((struct channel_softc *, int)); + void wdccommand __P((struct channel_softc *, u_int8_t, u_int8_t, u_int16_t, u_int8_t, u_int8_t, u_int8_t, u_int8_t)); void wdccommandshort __P((struct channel_softc *, int, int)); diff --git a/sys/dev/pci/pciide.c b/sys/dev/pci/pciide.c index 55b07125c97..e62efc2287b 100644 --- a/sys/dev/pci/pciide.c +++ b/sys/dev/pci/pciide.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pciide.c,v 1.21 2000/03/24 17:47:41 chris Exp $ */ +/* $OpenBSD: pciide.c,v 1.22 2000/04/10 07:06:17 csapuntz Exp $ */ /* $NetBSD: pciide.c,v 1.48 1999/11/28 20:05:18 bouyer Exp $ */ /* @@ -1114,7 +1114,7 @@ pciide_print_modes(cp) if ((drvp->drive_flags & DRIVE) == 0) continue; printf("%s(%s:%d:%d): using PIO mode %d", - drvp->drv_softc->dv_xname, + drvp->drive_name, sc->sc_wdcdev.sc_dev.dv_xname, chp->channel, drive, drvp->PIO_mode); if (drvp->drive_flags & DRIVE_DMA) diff --git a/sys/dev/pcmcia/wdc_pcmcia.c b/sys/dev/pcmcia/wdc_pcmcia.c index eac7ba8e124..d93033fd191 100644 --- a/sys/dev/pcmcia/wdc_pcmcia.c +++ b/sys/dev/pcmcia/wdc_pcmcia.c @@ -1,4 +1,4 @@ -/* $OpenBSD: wdc_pcmcia.c,v 1.7 2000/02/01 17:01:48 fgsch Exp $ */ +/* $OpenBSD: wdc_pcmcia.c,v 1.8 2000/04/10 07:06:15 csapuntz Exp $ */ /* $NetBSD: wdc_pcmcia.c,v 1.19 1999/02/19 21:49:43 abs Exp $ */ /*- @@ -80,13 +80,18 @@ struct wdc_pcmcia_softc { int sc_auxiowindow; void *sc_ih; struct pcmcia_function *sc_pf; + int sc_flags; +#define WDC_PCMCIA_ATTACH 0x0001 }; static int wdc_pcmcia_match __P((struct device *, void *, void *)); static void wdc_pcmcia_attach __P((struct device *, struct device *, void *)); +int wdc_pcmcia_detach __P((struct device *, int)); +int wdc_pcmcia_activate __P((struct device *, enum devact)); struct cfattach wdc_pcmcia_ca = { - sizeof(struct wdc_pcmcia_softc), wdc_pcmcia_match, wdc_pcmcia_attach + sizeof(struct wdc_pcmcia_softc), wdc_pcmcia_match, wdc_pcmcia_attach, + wdc_pcmcia_detach, wdc_pcmcia_activate }; struct wdc_pcmcia_product { @@ -280,14 +285,14 @@ wdc_pcmcia_attach(parent, self, aux) if (cfe == NULL) { printf(": can't handle card info\n"); - return; + goto no_config_entry; } /* Enable the card. */ pcmcia_function_init(pa->pf, cfe); if (pcmcia_function_enable(pa->pf)) { printf(": function enable failed\n"); - return; + goto enable_failed; } /* @@ -306,9 +311,8 @@ wdc_pcmcia_attach(parent, self, aux) if (pcmcia_io_map(pa->pf, quirks & WDC_PCMCIA_FORCE_16BIT_IO ? PCMCIA_WIDTH_IO16 : PCMCIA_WIDTH_AUTO, 0, sc->sc_pioh.size, &sc->sc_pioh, &sc->sc_iowindow)) { - /* XXX should unallocate */ printf(": can't map first I/O space\n"); - return; + goto iomap_failed; } /* @@ -316,12 +320,12 @@ wdc_pcmcia_attach(parent, self, aux) * So whether the work around like above is necessary or not * is unknown. XXX. */ - if (cfe->num_iospace > 1 && - pcmcia_io_map(pa->pf, PCMCIA_WIDTH_AUTO, 0, + if (cfe->num_iospace <= 1) + sc->sc_auxiowindow = -1; + else if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_AUTO, 0, sc->sc_auxpioh.size, &sc->sc_auxpioh, &sc->sc_auxiowindow)) { - /* XXX should unallocate */ printf(": can't map second I/O space\n"); - return; + goto iomapaux_failed; } printf(" port 0x%lx/%d", @@ -347,7 +351,7 @@ wdc_pcmcia_attach(parent, self, aux) M_DEVBUF, M_NOWAIT); if (sc->wdc_channel.ch_queue == NULL) { printf("can't allocate memory for command queue\n"); - return; + goto ch_queue_alloc_failed; } if (quirks & WDC_PCMCIA_NO_EXTRA_RESETS) sc->sc_wdcdev.cap |= WDC_CAPABILITY_NO_EXTRA_RESETS; @@ -372,36 +376,139 @@ wdc_pcmcia_attach(parent, self, aux) printf("\n"); + sc->sc_flags |= WDC_PCMCIA_ATTACH; wdcattach(&sc->wdc_channel); + sc->sc_flags &= ~WDC_PCMCIA_ATTACH; + return; + + ch_queue_alloc_failed: + /* Unmap our aux i/o window. */ + if (sc->sc_auxiowindow != -1) + pcmcia_io_unmap(sc->sc_pf, sc->sc_auxiowindow); + + iomapaux_failed: + /* Unmap our i/o window. */ + pcmcia_io_unmap(sc->sc_pf, sc->sc_iowindow); + + iomap_failed: + /* Disable the function */ + pcmcia_function_disable(sc->sc_pf); + + enable_failed: + /* Unmap our i/o space. */ + pcmcia_io_free(sc->sc_pf, &sc->sc_pioh); + if (cfe->num_iospace == 2) + pcmcia_io_free(sc->sc_pf, &sc->sc_auxpioh); + + no_config_entry: + sc->sc_iowindow = -1; } int -wdc_pcmcia_enable(arg, onoff) - void *arg; - int onoff; +wdc_pcmcia_detach(self, flags) + struct device *self; + int flags; { - struct wdc_pcmcia_softc *sc = arg; + struct wdc_pcmcia_softc *sc = (struct wdc_pcmcia_softc *)self; + int error; + + if (sc->sc_iowindow == -1) + /* Nothing to detach */ + return (0); + + if ((error = wdcdetach(&sc->wdc_channel, flags)) != 0) + return (error); + + if (sc->wdc_channel.ch_queue != NULL) + free(sc->wdc_channel.ch_queue, M_DEVBUF); + + /* Unmap our i/o window and i/o space. */ + pcmcia_io_unmap(sc->sc_pf, sc->sc_iowindow); + pcmcia_io_free(sc->sc_pf, &sc->sc_pioh); + if (sc->sc_auxiowindow != -1) { + pcmcia_io_unmap(sc->sc_pf, sc->sc_auxiowindow); + pcmcia_io_free(sc->sc_pf, &sc->sc_auxpioh); + } + + return (0); +} - if (onoff) { - /* Establish the interrupt handler. */ - sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_BIO, wdcintr, - &sc->wdc_channel); +int +wdc_pcmcia_activate(self, act) + struct device *self; + enum devact act; +{ + struct wdc_pcmcia_softc *sc = (struct wdc_pcmcia_softc *)self; + int rv = 0, s; + + s = splbio(); + switch (act) { + case DVACT_ACTIVATE: + sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_BIO, + wdcintr, &sc->wdc_channel); if (sc->sc_ih == NULL) { - printf("%s: couldn't establish interrupt handler\n", + printf("%s: " + "couldn't establish interrupt handler\n", sc->sc_wdcdev.sc_dev.dv_xname); - return (EIO); + rv = EIO; + break; } if (pcmcia_function_enable(sc->sc_pf)) { printf("%s: couldn't enable PCMCIA function\n", sc->sc_wdcdev.sc_dev.dv_xname); pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); - return (EIO); + rv = EIO; + break; } - } else { + + wdcreset(&sc->wdc_channel, VERBOSE); + rv = wdcactivate(self, act); + break; + + case DVACT_DEACTIVATE: pcmcia_function_disable(sc->sc_pf); pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); + rv = wdcactivate(self, act); + break; + } + splx(s); + return (rv); +} + +#if 0 +int +wdc_pcmcia_enable(arg, onoff) + void *arg; + int onoff; +{ + struct wdc_pcmcia_softc *sc = arg; + + if (onoff) { + if ((sc->sc_flags & WDC_PCMCIA_ATTACH) == 0) { + sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_BIO, + wdcintr, &sc->wdc_channel); + if (sc->sc_ih == NULL) { + printf("%s: " + "couldn't establish interrupt handler\n", + sc->sc_wdcdev.sc_dev.dv_xname); + return (EIO); + } + + if (pcmcia_function_enable(sc->sc_pf)) { + printf("%s: couldn't enable PCMCIA function\n", + sc->sc_wdcdev.sc_dev.dv_xname); + pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); + return (EIO); + } + wdcreset(&sc->wdc_channel, VERBOSE); + } + } else { + pcmcia_function_disable(sc->sc_pf); + if ((sc->sc_flags & WDC_PCMCIA_ATTACH) == 0) + pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); } return (0); } +#endif -- 2.20.1