/*
* Copyright (c) 2001 The NetBSD Foundation, Inc.
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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 drive goo for MCA ESDI controller driver.
*/
/*
* Just check if it's possible to identify the disk.
*/
static int
ed_mca_probe(device_t parent, cfdata_t cf, void *aux)
{
struct edc_mca_softc *sc = device_private(parent);
struct ed_attach_args *eda = aux;
u_int16_t cmd_args[2];
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->edc_drive, cmd_args, 2, 1))
found = 0;
/*
* Initialize and attach the disk structure.
*/
disk_init(&ed->sc_dk, device_xname(ed->sc_dev), &eddkdriver);
disk_attach(&ed->sc_dk);
rnd_attach_source(&ed->rnd_source, device_xname(ed->sc_dev),
RND_TYPE_DISK, RND_FLAG_DEFAULT);
ed->sc_flags |= EDF_INIT;
/*
* XXX We should try to discovery wedges here, but
* XXX that would mean being able to do I/O. Should
* XXX use config_defer() here.
*/
}
/*
* Read/write routine for a buffer. Validates the arguments and schedules the
* transfer. Does not wait for the transfer to complete.
*/
void
edmcastrategy(struct buf *bp)
{
struct ed_softc *ed;
struct disklabel *lp;
daddr_t blkno;
ed = device_lookup_private(&ed_cd, DISKUNIT(bp->b_dev));
lp = ed->sc_dk.dk_label;
/* If device invalidated (e.g. media change, door open), error. */
if ((ed->sc_flags & WDF_LOADED) == 0) {
bp->b_error = EIO;
goto done;
}
/* 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(&ed->sc_dk, bp,
(ed->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. */
mutex_enter(&ed->sc_q_lock);
bufq_put(ed->sc_q, bp);
mutex_exit(&ed->sc_q_lock);
/* Ring the worker thread */
wakeup(ed->edc_softc);
/*
* If there are wedges, and this is not RAW_PART, then we
* need to fail.
*/
if (wd->sc_dk.dk_nwedges != 0 && part != RAW_PART) {
error = EBUSY;
goto bad1;
}
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 bad1;
}
} else {
if ((wd->sc_flags & WDF_LOADED) == 0) {
int s;
wd->sc_flags |= WDF_LOADED;
/* Load the physical device parameters. */
s = splbio();
ed_get_params(wd, NULL);
splx(s);
/* Load the partition info if not already loaded. */
edgetdisklabel(dev, wd);
}
}
/* 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 bad1;
}
/* 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;
int
edmcaclose(dev_t dev, int flag, int fmt, struct lwp *l)
{
struct ed_softc *wd = device_lookup_private(&ed_cd, DISKUNIT(dev));
int part = DISKPART(dev);
errstring = readdisklabel(
EDLABELDEV(dev), edmcastrategy, lp, ed->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 |= ATA_DRIVE_RESET;
#endif
errstring = readdisklabel(EDLABELDEV(dev),
edmcastrategy, lp, ed->sc_dk.dk_cpulabel);
}
if (errstring) {
printf("%s: %s\n", device_xname(ed->sc_dev), errstring);
return;
}
}
int
edmcaioctl(dev_t dev, u_long xfer, void *addr, int flag, struct lwp *l)
{
struct ed_softc *ed = device_lookup_private(&ed_cd, DISKUNIT(dev));
int error;
/* #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_t dev, daddr_t blkno, void *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 */
int error;
/* Check if recursive dump; if so, punt. */
if (eddoingadump)
return EFAULT;
eddoingadump = 1;
ed = device_lookup_private(&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) {
error = edc_bio(ed->edc_softc, ed, va, blkno,
uimin(nblks, eddumpmulti) * lp->d_secsize, 0, 1);
if (error)
return (error);