/* $NetBSD: ed_mca.c,v 1.7 2001/05/14 07:35:33 jdolecek Exp $ */ /* * Copyright (c) 2001 The NetBSD Foundation, Inc. * * This code is derived from software contributed to The NetBSD Foundation * by Jaromir Dolecek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Disk goo for MCA ESDI controller driver. */ #include "rnd.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if NRND > 0 #include #endif #include #include #include #include #include #include /* #define WDCDEBUG */ #ifdef WDCDEBUG #define WDCDEBUG_PRINT(args, level) printf args #else #define WDCDEBUG_PRINT(args, level) #endif #define EDLABELDEV(dev) (MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART)) /* XXX: these should go elsewhere */ cdev_decl(edmca); bdev_decl(edmca); static int ed_mca_probe __P((struct device *, struct cfdata *, void *)); static void ed_mca_attach __P((struct device *, struct device *, void *)); struct cfattach ed_mca_ca = { sizeof(struct ed_softc), ed_mca_probe, ed_mca_attach }; extern struct cfdriver ed_cd; static int ed_get_params __P((struct ed_softc *)); static int ed_lock __P((struct ed_softc *)); static void ed_unlock __P((struct ed_softc *)); static void edgetdisklabel __P((struct ed_softc *)); static void edgetdefaultlabel __P((struct ed_softc *, struct disklabel *)); static void ed_shutdown __P((void*)); static void __edstart __P((struct ed_softc*, struct buf *)); static void bad144intern __P((struct ed_softc *)); static void edworker __P((void *)); static void ed_spawn_worker __P((void *)); static void edmcadone __P((struct ed_softc *, struct buf *)); static void ed_bio __P((struct ed_softc *, int, int)); static void ed_bio_done __P((struct ed_softc *)); static struct dkdriver eddkdriver = { edmcastrategy }; /* * Just check if it's possible to identify the disk. */ static int ed_mca_probe(parent, match, aux) struct device *parent; struct cfdata *match; void *aux; { u_int16_t cmd_args[2]; struct edc_mca_softc *sc = (void *) parent; struct ed_attach_args *eda = (void *) aux; int found = 1; /* * Get Device Configuration (09). */ cmd_args[0] = 14; /* Options: 00s110, s: 0=Physical 1=Pseudo */ cmd_args[1] = 0; if (edc_run_cmd(sc, CMD_GET_DEV_CONF, eda->sc_devno, cmd_args, 2, 0, 1)) found = 0; return (found); } static void ed_mca_attach(parent, self, aux) struct device *parent, *self; void *aux; { struct ed_softc *ed = (void *) self; struct edc_mca_softc *sc = (void *) parent; struct ed_attach_args *eda = (void *) aux; char pbuf[8]; int error, nsegs; ed->edc_softc = sc; ed->sc_dmat = eda->sc_dmat; ed->sc_devno = eda->sc_devno; edc_add_disk(sc, ed, eda->sc_devno); BUFQ_INIT(&ed->sc_q); spinlockinit(&ed->sc_q_lock, "edbqlock", 0); lockinit(&ed->sc_lock, PRIBIO | PCATCH, "edlck", 0, 0); if (ed_get_params(ed)) { printf(": IDENTIFY failed, no disk found\n"); return; } format_bytes(pbuf, sizeof(pbuf), (u_int64_t) ed->sc_capacity * DEV_BSIZE); printf(": %s, %u cyl, %u head, %u sec, 512 bytes/sect x %u sectors\n", pbuf, ed->cyl, ed->heads, ed->sectors, ed->sc_capacity); printf("%s: %u spares/cyl, %s, %s, %s, %s, %s\n", ed->sc_dev.dv_xname, ed->spares, (ed->drv_flags & (1 << 0)) ? "NoRetries" : "Retries", (ed->drv_flags & (1 << 1)) ? "Removable" : "Fixed", (ed->drv_flags & (1 << 2)) ? "SkewedFormat" : "NoSkew", (ed->drv_flags & (1 << 3)) ? "ZeroDefect" : "Defects", (ed->drv_flags & (1 << 4)) ? "InvalidSecondary" : "SecondaryOK" ); /* Create a DMA map for mapping individual transfer bufs */ if ((error = bus_dmamap_create(ed->sc_dmat, 65536, 1, 65536, 65536, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &ed->dmamap_xfer)) != 0) { printf("%s: unable to create xfer DMA map, error=%d\n", ed->sc_dev.dv_xname, error); return; } /* * Allocate DMA memory used in case where passed buf isn't * physically contiguous. */ ed->sc_dmam_sz = MAXPHYS; if ((error = bus_dmamem_alloc(ed->sc_dmat, ed->sc_dmam_sz, ed->sc_dmam_sz, 65536, ed->sc_dmam, 1, &nsegs, BUS_DMA_WAITOK|BUS_DMA_STREAMING)) != 0) { printf("%s: unable to allocate DMA memory for xfer, errno=%d\n", ed->sc_dev.dv_xname, error); bus_dmamap_destroy(ed->sc_dmat, ed->dmamap_xfer); return; } /* * Map the memory. */ if ((error = bus_dmamem_map(ed->sc_dmat, ed->sc_dmam, 1, ed->sc_dmam_sz, &ed->sc_dmamkva, BUS_DMA_WAITOK)) != 0) { printf("%s: unable to map DMA memory, error=%d\n", ed->sc_dev.dv_xname, error); bus_dmamem_free(ed->sc_dmat, ed->sc_dmam, 1); bus_dmamap_destroy(ed->sc_dmat, ed->dmamap_xfer); return; } /* * Initialize and attach the disk structure. */ ed->sc_dk.dk_driver = &eddkdriver; ed->sc_dk.dk_name = ed->sc_dev.dv_xname; disk_attach(&ed->sc_dk); #if 0 wd->sc_wdc_bio.lp = wd->sc_dk.dk_label; #endif ed->sc_sdhook = shutdownhook_establish(ed_shutdown, ed); if (ed->sc_sdhook == NULL) printf("%s: WARNING: unable to establish shutdown hook\n", ed->sc_dev.dv_xname); #if NRND > 0 rnd_attach_source(&ed->rnd_source, ed->sc_dev.dv_xname, RND_TYPE_DISK, 0); #endif config_pending_incr(); kthread_create(ed_spawn_worker, (void *) ed); ed->sc_flags |= EDF_INIT; } void ed_spawn_worker(arg) void *arg; { struct ed_softc *ed = (struct ed_softc *) arg; int error; /* Now, everything is ready, start a kthread */ if ((error = kthread_create1(edworker, ed, &ed->sc_worker, "%s", ed->sc_dev.dv_xname))) { printf("%s: cannot spawn worker thread: errno=%d\n", ed->sc_dev.dv_xname, error); panic("ed_spawn_worker"); } } /* * Read/write routine for a buffer. Validates the arguments and schedules the * transfer. Does not wait for the transfer to complete. */ void edmcastrategy(bp) struct buf *bp; { struct ed_softc *wd = device_lookup(&ed_cd, DISKUNIT(bp->b_dev)); struct disklabel *lp = wd->sc_dk.dk_label; daddr_t blkno; int s; WDCDEBUG_PRINT(("edmcastrategy (%s)\n", wd->sc_dev.dv_xname), DEBUG_XFERS); /* Valid request? */ if (bp->b_blkno < 0 || (bp->b_bcount % lp->d_secsize) != 0 || (bp->b_bcount / lp->d_secsize) >= (1 << NBBY)) { bp->b_error = EINVAL; goto bad; } /* If device invalidated (e.g. media change, door open), error. */ if ((wd->sc_flags & WDF_LOADED) == 0) { bp->b_error = EIO; goto bad; } /* If it's a null transfer, return immediately. */ if (bp->b_bcount == 0) goto done; /* * Do bounds checking, adjust transfer. if error, process. * If end of partition, just return. */ if (DISKPART(bp->b_dev) != RAW_PART && bounds_check_with_label(bp, wd->sc_dk.dk_label, (wd->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0) goto done; /* * Now convert the block number to absolute and put it in * terms of the device's logical block size. */ if (lp->d_secsize >= DEV_BSIZE) blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE); else blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize); if (DISKPART(bp->b_dev) != RAW_PART) blkno += lp->d_partitions[DISKPART(bp->b_dev)].p_offset; bp->b_rawblkno = blkno; /* Queue transfer on drive, activate drive and controller if idle. */ s = splbio(); simple_lock(&wd->sc_q_lock); disksort_blkno(&wd->sc_q, bp); simple_unlock(&wd->sc_q_lock); /* Ring the worker thread */ wd->sc_flags |= EDF_PROCESS_QUEUE; wakeup_one(&wd->sc_q); splx(s); return; bad: bp->b_flags |= B_ERROR; done: /* Toss transfer; we're done early. */ bp->b_resid = bp->b_bcount; biodone(bp); } static void ed_bio(struct ed_softc *ed, int async, int poll) { u_int16_t cmd_args[4]; int error=0; u_int16_t track; u_int16_t cyl; u_int8_t head; u_int8_t sector; /* Get physical bus mapping for buf. */ if (bus_dmamap_load(ed->sc_dmat, ed->dmamap_xfer, ed->sc_data, ed->sc_bcount, NULL, BUS_DMA_WAITOK|BUS_DMA_STREAMING) != 0) { /* * Use our DMA safe memory to get data to/from device. */ if ((error = bus_dmamap_load(ed->sc_dmat, ed->dmamap_xfer, ed->sc_dmamkva, ed->sc_bcount, NULL, BUS_DMA_WAITOK|BUS_DMA_STREAMING)) != 0) { printf("%s: unable to load raw data for xfer, errno=%d\n", ed->sc_dev.dv_xname, error); goto out; } ed->sc_flags |= EDF_BOUNCEBUF; /* If data write, copy the data to our bounce buffer. */ if (!ed->sc_read) memcpy(ed->sc_dmamkva, ed->sc_data, ed->sc_bcount); } ed->sc_flags |= EDF_DMAMAP_LOADED; track = ed->sc_rawblkno / ed->sectors; head = track % ed->heads; cyl = track / ed->heads; sector = ed->sc_rawblkno % ed->sectors; WDCDEBUG_PRINT(("__edstart %s: map: %u %u %u\n", ed->sc_dev.dv_xname, cyl, sector, head), DEBUG_XFERS); mca_disk_busy(); /* Read or Write Data command */ cmd_args[0] = 2; /* Options 0000010 */ cmd_args[1] = ed->sc_bcount / DEV_BSIZE; cmd_args[2] = ((cyl & 0x1f) << 11) | (head << 5) | sector; cmd_args[3] = ((cyl & 0x3E0) >> 5); if (edc_run_cmd(ed->edc_softc, (ed->sc_read) ? CMD_READ_DATA : CMD_WRITE_DATA, ed->sc_devno, cmd_args, 4, async, poll)) { printf("%s: data i/o command failed\n", ed->sc_dev.dv_xname); mca_disk_unbusy(); error = EIO; } out: if (error) ed->sc_error = error; } static void __edstart(ed, bp) struct ed_softc *ed; struct buf *bp; { WDCDEBUG_PRINT(("__edstart %s (%s): %lu %lu %u\n", ed->sc_dev.dv_xname, (bp->b_flags & B_READ) ? "read" : "write", bp->b_bcount, bp->b_resid, bp->b_rawblkno), DEBUG_XFERS); /* Instrumentation. */ disk_busy(&ed->sc_dk); ed->sc_flags |= EDF_DK_BUSY; ed->sc_data = bp->b_data; ed->sc_rawblkno = bp->b_rawblkno; ed->sc_bcount = bp->b_bcount; ed->sc_read = bp->b_flags & B_READ; ed_bio(ed, 1, 0); } static void ed_bio_done(ed) struct ed_softc *ed; { /* * If read transfer finished without error and using a bounce * buffer, copy the data to buf. */ if (ed->sc_error == 0 && (ed->sc_flags & EDF_BOUNCEBUF) && ed->sc_read) memcpy(ed->sc_data, ed->sc_dmamkva, ed->sc_bcount); ed->sc_flags &= ~EDF_BOUNCEBUF; /* Unload buf from DMA map */ if (ed->sc_flags & EDF_DMAMAP_LOADED) { bus_dmamap_unload(ed->sc_dmat, ed->dmamap_xfer); ed->sc_flags &= ~EDF_DMAMAP_LOADED; } mca_disk_unbusy(); } static void edmcadone(ed, bp) struct ed_softc *ed; struct buf *bp; { WDCDEBUG_PRINT(("eddone %s\n", ed->sc_dev.dv_xname), DEBUG_XFERS); if (ed->sc_error) { bp->b_error = ed->sc_error; bp->b_flags |= B_ERROR; } else { /* Set resid, most commonly to zero. */ bp->b_resid = ed->sc_status_block[SB_RESBLKCNT_IDX] * DEV_BSIZE; } ed_bio_done(ed); /* If disk was busied, unbusy it now */ if (ed->sc_flags & EDF_DK_BUSY) { disk_unbusy(&ed->sc_dk, (bp->b_bcount - bp->b_resid)); ed->sc_flags &= ~EDF_DK_BUSY; } #if NRND > 0 rnd_add_uint32(&ed->rnd_source, bp->b_blkno); #endif biodone(bp); } int edmcaread(dev, uio, flags) dev_t dev; struct uio *uio; int flags; { WDCDEBUG_PRINT(("edread\n"), DEBUG_XFERS); return (physio(edmcastrategy, NULL, dev, B_READ, minphys, uio)); } int edmcawrite(dev, uio, flags) dev_t dev; struct uio *uio; int flags; { WDCDEBUG_PRINT(("edwrite\n"), DEBUG_XFERS); return (physio(edmcastrategy, NULL, dev, B_WRITE, minphys, uio)); } /* * Wait interruptibly for an exclusive lock. */ static int ed_lock(ed) struct ed_softc *ed; { int error; int s; WDCDEBUG_PRINT(("ed_lock\n"), DEBUG_FUNCS); s = splbio(); error = lockmgr(&ed->sc_lock, LK_EXCLUSIVE, NULL); splx(s); return (error); } /* * Unlock and wake up any waiters. */ static void ed_unlock(ed) struct ed_softc *ed; { WDCDEBUG_PRINT(("ed_unlock\n"), DEBUG_FUNCS); (void) lockmgr(&ed->sc_lock, LK_RELEASE, NULL); } int edmcaopen(dev, flag, fmt, p) dev_t dev; int flag, fmt; struct proc *p; { struct ed_softc *wd; int part, error; WDCDEBUG_PRINT(("edopen\n"), DEBUG_FUNCS); wd = device_lookup(&ed_cd, DISKUNIT(dev)); if (wd == NULL || (wd->sc_flags & EDF_INIT) == 0) return (ENXIO); if ((error = ed_lock(wd)) != 0) goto bad4; if (wd->sc_dk.dk_openmask != 0) { /* * If any partition is open, but the disk has been invalidated, * disallow further opens. */ if ((wd->sc_flags & WDF_LOADED) == 0) { error = EIO; goto bad3; } } else { if ((wd->sc_flags & WDF_LOADED) == 0) { wd->sc_flags |= WDF_LOADED; /* Load the physical device parameters. */ ed_get_params(wd); /* Load the partition info if not already loaded. */ edgetdisklabel(wd); } } part = DISKPART(dev); /* Check that the partition exists. */ if (part != RAW_PART && (part >= wd->sc_dk.dk_label->d_npartitions || wd->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { error = ENXIO; goto bad; } /* Insure only one open at a time. */ switch (fmt) { case S_IFCHR: wd->sc_dk.dk_copenmask |= (1 << part); break; case S_IFBLK: wd->sc_dk.dk_bopenmask |= (1 << part); break; } wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask; ed_unlock(wd); return 0; bad: if (wd->sc_dk.dk_openmask == 0) { } bad3: ed_unlock(wd); bad4: return (error); } int edmcaclose(dev, flag, fmt, p) dev_t dev; int flag, fmt; struct proc *p; { struct ed_softc *wd = device_lookup(&ed_cd, DISKUNIT(dev)); int part = DISKPART(dev); int error; WDCDEBUG_PRINT(("edmcaclose\n"), DEBUG_FUNCS); if ((error = ed_lock(wd)) != 0) return error; switch (fmt) { case S_IFCHR: wd->sc_dk.dk_copenmask &= ~(1 << part); break; case S_IFBLK: wd->sc_dk.dk_bopenmask &= ~(1 << part); break; } wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask; if (wd->sc_dk.dk_openmask == 0) { #if 0 wd_flushcache(wd, AT_WAIT); #endif /* XXXX Must wait for I/O to complete! */ if (! (wd->sc_flags & WDF_KLABEL)) wd->sc_flags &= ~WDF_LOADED; } ed_unlock(wd); return 0; } static void edgetdefaultlabel(wd, lp) struct ed_softc *wd; struct disklabel *lp; { WDCDEBUG_PRINT(("edgetdefaultlabel\n"), DEBUG_FUNCS); memset(lp, 0, sizeof(struct disklabel)); lp->d_secsize = DEV_BSIZE; lp->d_ntracks = wd->heads; lp->d_nsectors = wd->sectors; lp->d_ncylinders = wd->cyl; lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; lp->d_type = DTYPE_ESDI; strncpy(lp->d_typename, "ESDI", 16); strncpy(lp->d_packname, "fictitious", 16); lp->d_secperunit = wd->sc_capacity; lp->d_rpm = 3600; lp->d_interleave = 1; lp->d_flags = 0; lp->d_partitions[RAW_PART].p_offset = 0; lp->d_partitions[RAW_PART].p_size = lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; lp->d_npartitions = RAW_PART + 1; lp->d_magic = DISKMAGIC; lp->d_magic2 = DISKMAGIC; lp->d_checksum = dkcksum(lp); } /* * Fabricate a default disk label, and try to read the correct one. */ static void edgetdisklabel(wd) struct ed_softc *wd; { struct disklabel *lp = wd->sc_dk.dk_label; char *errstring; WDCDEBUG_PRINT(("edgetdisklabel\n"), DEBUG_FUNCS); memset(wd->sc_dk.dk_cpulabel, 0, sizeof(struct cpu_disklabel)); edgetdefaultlabel(wd, lp); #if 0 wd->sc_badsect[0] = -1; if (wd->drvp->state > RECAL) wd->drvp->drive_flags |= DRIVE_RESET; #endif errstring = readdisklabel(MAKEDISKDEV(0, wd->sc_dev.dv_unit, RAW_PART), edmcastrategy, lp, wd->sc_dk.dk_cpulabel); if (errstring) { /* * This probably happened because the drive's default * geometry doesn't match the DOS geometry. We * assume the DOS geometry is now in the label and try * again. XXX This is a kluge. */ #if 0 if (wd->drvp->state > RECAL) wd->drvp->drive_flags |= DRIVE_RESET; #endif errstring = readdisklabel(MAKEDISKDEV(0, wd->sc_dev.dv_unit, RAW_PART), edmcastrategy, lp, wd->sc_dk.dk_cpulabel); } if (errstring) { printf("%s: %s\n", wd->sc_dev.dv_xname, errstring); return; } #if 0 if (wd->drvp->state > RECAL) wd->drvp->drive_flags |= DRIVE_RESET; #endif #ifdef HAS_BAD144_HANDLING if ((lp->d_flags & D_BADSECT) != 0) bad144intern(wd); #endif } int edmcaioctl(dev, xfer, addr, flag, p) dev_t dev; u_long xfer; caddr_t addr; int flag; struct proc *p; { struct ed_softc *wd = device_lookup(&ed_cd, DISKUNIT(dev)); int error; #ifdef __HAVE_OLD_DISKLABEL struct disklabel newlabel; #endif WDCDEBUG_PRINT(("edioctl\n"), DEBUG_FUNCS); if ((wd->sc_flags & WDF_LOADED) == 0) return EIO; switch (xfer) { #ifdef HAS_BAD144_HANDLING case DIOCSBAD: if ((flag & FWRITE) == 0) return EBADF; wd->sc_dk.dk_cpulabel->bad = *(struct dkbad *)addr; wd->sc_dk.dk_label->d_flags |= D_BADSECT; bad144intern(wd); return 0; #endif case DIOCGDINFO: *(struct disklabel *)addr = *(wd->sc_dk.dk_label); return 0; #ifdef __HAVE_OLD_DISKLABEL case ODIOCGDINFO: newlabel = *(wd->sc_dk.dk_label); if (newlabel.d_npartitions > OLDMAXPARTITIONS) return ENOTTY; memcpy(addr, &newlabel, sizeof (struct olddisklabel)); return 0; #endif case DIOCGPART: ((struct partinfo *)addr)->disklab = wd->sc_dk.dk_label; ((struct partinfo *)addr)->part = &wd->sc_dk.dk_label->d_partitions[DISKPART(dev)]; return 0; case DIOCWDINFO: case DIOCSDINFO: #ifdef __HAVE_OLD_DISKLABEL case ODIOCWDINFO: case ODIOCSDINFO: #endif { struct disklabel *lp; #ifdef __HAVE_OLD_DISKLABEL if (xfer == ODIOCSDINFO || xfer == ODIOCWDINFO) { memset(&newlabel, 0, sizeof newlabel); memcpy(&newlabel, addr, sizeof (struct olddisklabel)); lp = &newlabel; } else #endif lp = (struct disklabel *)addr; if ((flag & FWRITE) == 0) return EBADF; if ((error = ed_lock(wd)) != 0) return error; wd->sc_flags |= WDF_LABELLING; error = setdisklabel(wd->sc_dk.dk_label, lp, /*wd->sc_dk.dk_openmask : */0, wd->sc_dk.dk_cpulabel); if (error == 0) { #if 0 if (wd->drvp->state > RECAL) wd->drvp->drive_flags |= DRIVE_RESET; #endif if (xfer == DIOCWDINFO #ifdef __HAVE_OLD_DISKLABEL || xfer == ODIOCWDINFO #endif ) error = writedisklabel(EDLABELDEV(dev), edmcastrategy, wd->sc_dk.dk_label, wd->sc_dk.dk_cpulabel); } wd->sc_flags &= ~WDF_LABELLING; ed_unlock(wd); return error; } case DIOCKLABEL: if (*(int *)addr) wd->sc_flags |= WDF_KLABEL; else wd->sc_flags &= ~WDF_KLABEL; return 0; case DIOCWLABEL: if ((flag & FWRITE) == 0) return EBADF; if (*(int *)addr) wd->sc_flags |= WDF_WLABEL; else wd->sc_flags &= ~WDF_WLABEL; return 0; case DIOCGDEFLABEL: edgetdefaultlabel(wd, (struct disklabel *)addr); return 0; #ifdef __HAVE_OLD_DISKLABEL case ODIOCGDEFLABEL: edgetdefaultlabel(wd, &newlabel); if (newlabel.d_npartitions > OLDMAXPARTITIONS) return ENOTTY; memcpy(addr, &newlabel, sizeof (struct olddisklabel)); return 0; #endif #ifdef notyet case DIOCWFORMAT: if ((flag & FWRITE) == 0) return EBADF; { register struct format_op *fop; struct iovec aiov; struct uio auio; fop = (struct format_op *)addr; aiov.iov_base = fop->df_buf; aiov.iov_len = fop->df_count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_resid = fop->df_count; auio.uio_segflg = 0; auio.uio_offset = fop->df_startblk * wd->sc_dk.dk_label->d_secsize; auio.uio_procp = p; error = physio(wdformat, NULL, dev, B_WRITE, minphys, &auio); fop->df_count -= auio.uio_resid; fop->df_reg[0] = wdc->sc_status; fop->df_reg[1] = wdc->sc_error; return error; } #endif default: return ENOTTY; } #ifdef DIAGNOSTIC panic("edioctl: impossible"); #endif } #if 0 #ifdef B_FORMAT int edmcaformat(struct buf *bp) { bp->b_flags |= B_FORMAT; return edmcastrategy(bp); } #endif #endif int edmcasize(dev) dev_t dev; { struct ed_softc *wd; int part, omask; int size; WDCDEBUG_PRINT(("edsize\n"), DEBUG_FUNCS); wd = device_lookup(&ed_cd, DISKUNIT(dev)); if (wd == NULL) return (-1); part = DISKPART(dev); omask = wd->sc_dk.dk_openmask & (1 << part); if (omask == 0 && edmcaopen(dev, 0, S_IFBLK, NULL) != 0) return (-1); 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 && edmcaclose(dev, 0, S_IFBLK, NULL) != 0) return (-1); return (size); } /* #define WD_DUMP_NOT_TRUSTED if you just want to watch */ static int eddoingadump = 0; static int eddumprecalibrated = 0; static int eddumpmulti = 1; /* * Dump core after a system crash. */ int edmcadump(dev, blkno, va, size) dev_t dev; daddr_t blkno; caddr_t va; size_t size; { struct ed_softc *ed; /* disk unit to do the I/O */ struct disklabel *lp; /* disk's disklabel */ int part; int nblks; /* total number of sectors left to write */ /* Check if recursive dump; if so, punt. */ if (eddoingadump) return EFAULT; eddoingadump = 1; ed = device_lookup(&ed_cd, DISKUNIT(dev)); if (ed == NULL) return (ENXIO); part = DISKPART(dev); /* Make sure it was initialized. */ if ((ed->sc_flags & EDF_INIT) == 0) return ENXIO; /* Convert to disk sectors. Request must be a multiple of size. */ lp = ed->sc_dk.dk_label; if ((size % lp->d_secsize) != 0) return EFAULT; nblks = size / lp->d_secsize; blkno = blkno / (lp->d_secsize / DEV_BSIZE); /* Check transfer bounds against partition size. */ if ((blkno < 0) || ((blkno + nblks) > lp->d_partitions[part].p_size)) return EINVAL; /* Offset block number to start of partition. */ blkno += lp->d_partitions[part].p_offset; /* Recalibrate, if first dump transfer. */ if (eddumprecalibrated == 0) { eddumprecalibrated = 1; eddumpmulti = 8; #if 0 wd->drvp->state = RESET; #endif } while (nblks > 0) { ed->sc_data = va; ed->sc_rawblkno = blkno; ed->sc_bcount = min(nblks, eddumpmulti) * lp->d_secsize; ed->sc_read = 0; ed_bio(ed, 0, 1); if (ed->sc_error) return (ed->sc_error); ed_bio_done(ed); /* update block count */ nblks -= min(nblks, eddumpmulti); blkno += min(nblks, eddumpmulti); va += min(nblks, eddumpmulti) * lp->d_secsize; } eddoingadump = 0; return (0); } #ifdef HAS_BAD144_HANDLING /* * Internalize the bad sector table. */ static void bad144intern(wd) struct ed_softc *wd; { struct dkbad *bt = &wd->sc_dk.dk_cpulabel->bad; struct disklabel *lp = wd->sc_dk.dk_label; int i = 0; WDCDEBUG_PRINT(("bad144intern\n"), DEBUG_XFERS); for (; i < NBT_BAD; i++) { if (bt->bt_bad[i].bt_cyl == 0xffff) break; wd->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 < NBT_BAD+1; i++) wd->sc_badsect[i] = -1; } #endif static int ed_get_params(ed) struct ed_softc *ed; { u_int16_t cmd_args[2]; /* * Get Device Configuration (09). */ cmd_args[0] = 14; /* Options: 00s110, s: 0=Physical 1=Pseudo */ cmd_args[1] = 0; if (edc_run_cmd(ed->edc_softc, CMD_GET_DEV_CONF, ed->sc_devno, cmd_args, 2, 0, 1)) return (1); ed->spares = ed->sc_status_block[1] >> 8; ed->drv_flags = ed->sc_status_block[1] & 0x1f; ed->rba = ed->sc_status_block[2] | (ed->sc_status_block[3] << 16); /* Instead of using: ed->cyl = ed->sc_status_block[4]; ed->heads = ed->sc_status_block[5] & 0xff; ed->sectors = ed->sc_status_block[5] >> 8; * we fabricate the numbers from RBA count, so that * number of sectors is 32 and heads 64. This seems * to be necessary for integrated ESDI controller. */ ed->sectors = 32; ed->heads = 64; ed->cyl = ed->rba / (ed->heads * ed->sectors); ed->sc_capacity = ed->rba; return (0); } /* * Our shutdown hook. We attempt to park disk's head only. */ void ed_shutdown(arg) void *arg; { #if 0 struct ed_softc *ed = arg; u_int16_t cmd_args[2]; /* Issue Park Head command */ cmd_args[0] = 6; /* Options: 000110 */ cmd_args[1] = 0; (void) edc_run_cmd(ed->edc_softc, CMD_PARK_HEAD, ed->sc_devno, cmd_args, 2, 0); #endif } /* * Main worker thread function. */ void edworker(arg) void *arg; { struct ed_softc *ed = (struct ed_softc *) arg; struct buf *bp; int s; config_pending_decr(); for(;;) { /* Wait until awakened */ (void) tsleep(&ed->sc_q, PRIBIO, "edidle", 0); if ((ed->sc_flags & EDF_PROCESS_QUEUE) == 0) panic("edworker: expecting process queue"); ed->sc_flags &= ~EDF_PROCESS_QUEUE; for(;;) { /* Is there a buf for us ? */ simple_lock(&ed->sc_q_lock); if ((bp = BUFQ_FIRST(&ed->sc_q)) == NULL) { simple_unlock(&ed->sc_q_lock); break; } BUFQ_REMOVE(&ed->sc_q, bp); simple_unlock(&ed->sc_q_lock); /* Schedule i/o operation */ ed->sc_error = 0; s = splbio(); __edstart(ed, bp); /* * Wait until the command executes; edc_intr() wakes * us up. */ if (ed->sc_error == 0) (void)tsleep(&ed->edc_softc, PRIBIO, "edwrk",0); /* Handle i/o results */ edmcadone(ed, bp); splx(s); } } }