1127 lines
26 KiB
C
1127 lines
26 KiB
C
/* $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 <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/file.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/device.h>
|
|
#include <sys/disklabel.h>
|
|
#include <sys/disk.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/kthread.h>
|
|
#if NRND > 0
|
|
#include <sys/rnd.h>
|
|
#endif
|
|
|
|
#include <machine/intr.h>
|
|
#include <machine/bus.h>
|
|
|
|
#include <dev/mca/mcavar.h>
|
|
|
|
#include <dev/mca/edcreg.h>
|
|
#include <dev/mca/edvar.h>
|
|
#include <dev/mca/edcvar.h>
|
|
|
|
/* #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);
|
|
}
|
|
}
|
|
}
|