/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Paul Kranenburg.
*
* 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.
*/
/*-
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Don Ahn.
*
* 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. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*
* @(#)fd.c 7.4 (Berkeley) 5/25/91
*/
/*-
* Copyright (c) 1993, 1994, 1995 Charles M. Hannum.
*
* This code is derived from software contributed to Berkeley by
* Don Ahn.
*
* 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 University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*
* @(#)fd.c 7.4 (Berkeley) 5/25/91
*/
/*
* Floppies come in various flavors, e.g., 1.2MB vs 1.44MB; here is how
* we tell them apart.
*/
struct fd_type {
int sectrac; /* sectors per track */
int heads; /* number of heads */
int seccyl; /* sectors per cylinder */
int secsize; /* size code for sectors */
int datalen; /* data len when secsize = 0 */
int steprate; /* step rate and head unload time */
int gap1; /* gap len between sectors */
int gap2; /* formatting gap */
int cylinders; /* total num of cylinders */
int size; /* size of disk in sectors */
int step; /* steps per cylinder */
int rate; /* transfer speed code */
int fillbyte; /* format fill byte */
int interleave; /* interleave factor (formatting) */
const char *name;
};
/* The order of entries in the following table is important -- BEWARE! */
struct fd_type fd_types[] = {
{ 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS,0xf6,1, "1.44MB" }, /* 1.44MB diskette */
{ 9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS,0xf6,1, "720KB" }, /* 3.5" 720kB diskette */
{ 9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS,0xf6,1, "360KB/x" }, /* 360kB in 720kB drive */
{ 8,2,16,3,0xff,0xdf,0x35,0x74,77,1232,1,FDC_500KBPS,0xf6,1, "1.2MB/NEC" } /* 1.2 MB japanese format */
};
/* software state, per disk (with up to 4 disks per ctlr) */
struct fd_softc {
device_t sc_dev; /* generic device info */
struct disk sc_dk; /* generic disk info */
struct fd_type *sc_deftype; /* default type descriptor */
struct fd_type *sc_type; /* current type descriptor */
daddr_t sc_blkno; /* starting block number */
int sc_bcount; /* byte count left */
int sc_skip; /* bytes already transferred */
int sc_nblks; /* number of blocks currently transferring */
int sc_nbytes; /* number of bytes currently transferring */
int sc_drive; /* physical unit number */
int sc_flags;
#define FD_OPEN 0x01 /* it's open */
#define FD_MOTOR 0x02 /* motor should be on */
#define FD_MOTOR_WAIT 0x04 /* motor coming up */
int sc_cylin; /* where we think the head is */
int sc_opts; /* user-set options */
TAILQ_ENTRY(fd_softc) sc_drivechain;
int sc_ops; /* I/O ops since last switch */
struct bufq_state *sc_q;/* pending I/O requests */
int sc_active; /* number of active I/O requests */
};
/*
* This hack from Chris Torek: apparently DOR really
* addresses MSR/DRS on a 82072.
* We used to rely on the VERSION command to tell the
* difference (which did not work).
*/
/* First, check the size of the register bank */
if (size < 8)
/* It isn't a 82077 */
return;
#ifdef SUN4
/* Then probe the DOR register offset */
if (bus_space_probe(tag, addr,
1, /* probe size */
FDREG77_DOR, /* offset */
0, /* flags */
NULL, NULL) == 0) {
/* It isn't a 82077 */
return;
}
#endif
v = bus_space_read_1(tag, handle, FDREG77_DOR);
if (v == NE7_RQM) {
/*
* Value in DOR looks like it's really MSR
*/
bus_space_write_1(tag, handle, FDREG77_DOR, FDC_250KBPS);
v = bus_space_read_1(tag, handle, FDREG77_DOR);
if (v == NE7_RQM) {
/*
* The value in the DOR didn't stick;
* it isn't a 82077
*/
return;
}
}
fdc->sc_flags |= FDC_82077;
}
/*
* Arguments passed between fdcattach and fdprobe.
*/
struct fdc_attach_args {
int fa_drive;
struct fd_type *fa_deftype;
};
/*
* Print the location of a disk drive (called just before attaching the
* the drive). If `fdc' is not NULL, the drive was found but was not
* in the system config file; print the drive name as well.
* Return QUIET (config_find ignores this if the device was configured) to
* avoid printing `fdN not configured' messages.
*/
int
fdprint(void *aux, const char *fdc)
{
register struct fdc_attach_args *fa = aux;
if (!fdc)
aprint_normal(" drive %d", fa->fa_drive);
return QUIET;
}
/*
* Configure several parameters and features on the FDC.
* Return 0 on success.
*/
static int
fdconf(struct fdc_softc *fdc)
{
int vroom;
if (fdc_wrfifo(fdc, NE7CMD_DUMPREG) || fdcresult(fdc) != 10)
return -1;
/*
* dumpreg[7] seems to be a motor-off timeout; set it to whatever
* the PROM thinks is appropriate.
*/
if ((vroom = fdc->sc_status[7]) == 0)
vroom = 0x64;
/* Configure controller to use FIFO and Implied Seek */
if (fdc_wrfifo(fdc, NE7CMD_CFG) != 0)
return -1;
if (fdc_wrfifo(fdc, vroom) != 0)
return -1;
if (fdc_wrfifo(fdc, fdc->sc_cfg) != 0)
return -1;
if (fdc_wrfifo(fdc, 0) != 0) /* PRETRK */
return -1;
/* No result phase for the NE7CMD_CFG command */
if (drive_attached == 0) {
/* XXX - dis-establish interrupts here */
/* return -1; */
}
return 0;
}
int
fdmatch(device_t parent, cfdata_t match, void *aux)
{
struct fdc_softc *fdc = device_private(parent);
bus_space_tag_t t = fdc->sc_bustag;
bus_space_handle_t h = fdc->sc_handle;
struct fdc_attach_args *fa = aux;
int drive = fa->fa_drive;
int n, ok;
if (drive > 0)
/* XXX - for now, punt on more than one drive */
return 0;
if ((fdc->sc_flags & FDC_82077) != 0) {
/* select drive and turn on motor */
bus_space_write_1(t, h, fdc->sc_reg_dor,
drive | FDO_FRST | FDO_MOEN(drive));
/* wait for motor to spin up */
delay(250000);
#ifdef SUN4
} else {
auxregbisc(AUXIO4C_FDS, 0);
#endif
}
fdc->sc_nstat = 0;
fdc_wrfifo(fdc, NE7CMD_RECAL);
fdc_wrfifo(fdc, drive);
/* Wait for recalibration to complete */
for (n = 0; n < 10000; n++) {
uint8_t v;
delay(1000);
v = bus_space_read_1(t, h, fdc->sc_reg_msr);
if ((v & (NE7_RQM|NE7_DIO|NE7_CB)) == NE7_RQM) {
/* wait a bit longer till device *really* is ready */
delay(100000);
if (fdc_wrfifo(fdc, NE7CMD_SENSEI))
break;
if (fdcresult(fdc) == 1 && fdc->sc_status[0] == 0x80)
/*
* Got `invalid command'; we interpret it
* to mean that the re-calibrate hasn't in
* fact finished yet
*/
continue;
break;
}
}
n = fdc->sc_nstat;
#ifdef FD_DEBUG
if (fdc_debug) {
int i;
printf("fdprobe: %d stati:", n);
for (i = 0; i < n; i++)
printf(" 0x%x", fdc->sc_status[i]);
printf("\n");
}
#endif
ok = (n == 2 && (fdc->sc_status[0] & 0xf8) == 0x20) ? 1 : 0;
/* turn off motor */
if ((fdc->sc_flags & FDC_82077) != 0) {
/* deselect drive and turn motor off */
bus_space_write_1(t, h, fdc->sc_reg_dor, FDO_FRST | FDO_DS);
#ifdef SUN4
} else {
auxregbisc(0, AUXIO4C_FDS);
#endif
}
fdc_wrfifo(fdc, NE7CMD_SPECIFY);
fdc_wrfifo(fdc, type->steprate);
/* XXX head load time == 6ms */
fdc_wrfifo(fdc, 6 | NE7_SPECIFY_NODMA);
/*
* Initialize and attach the disk structure.
*/
disk_init(&fd->sc_dk, device_xname(fd->sc_dev), &fddkdriver);
disk_attach(&fd->sc_dk);
/*
* Establish a mountroot_hook anyway in case we booted
* with RB_ASKNAME and get selected as the boot device.
*/
mountroothook_establish(fd_mountroot_hook, fd->sc_dev);
fd_set_geometry(fd);
/* Make sure the drive motor gets turned off at shutdown time. */
if (!pmf_device_register1(self, fdsuspend, NULL, fdshutdown))
aprint_error_dev(self, "couldn't establish power handler\n");
}
/*
* Move this drive to the end of the queue to give others a `fair'
* chance. We only force a switch if N operations are completed while
* another drive is waiting to be serviced, since there is a long motor
* startup delay whenever we switch.
*/
(void)bufq_get(fd->sc_q);
if (fd->sc_drivechain.tqe_next && ++fd->sc_ops >= 8) {
fd->sc_ops = 0;
TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
if (bufq_peek(fd->sc_q) != NULL) {
TAILQ_INSERT_TAIL(&fdc->sc_drives, fd, sc_drivechain);
} else
fd->sc_active = 0;
}
bp->b_resid = fd->sc_bcount;
fd->sc_skip = 0;
biodone(bp);
/* turn off motor 5s from now */
callout_reset(&fd->sc_motoroff_ch, 5 * hz, fd_motor_off, fd);
fdc->sc_state = DEVIDLE;
}
void
fdc_reset(struct fdc_softc *fdc)
{
bus_space_tag_t t = fdc->sc_bustag;
bus_space_handle_t h = fdc->sc_handle;
s = splbio();
fd->sc_flags &= ~FD_MOTOR_WAIT;
if ((fdc->sc_drives.tqh_first == fd) && (fdc->sc_state == MOTORWAIT))
(void)fdcstate(fdc);
splx(s);
}
/*
* Get status bytes off the FDC after a command has finished
* Returns the number of status bytes read; -1 on error.
* The return value is also stored in `sc_nstat'.
*/
int
fdcresult(struct fdc_softc *fdc)
{
bus_space_tag_t t = fdc->sc_bustag;
bus_space_handle_t h = fdc->sc_handle;
int j, n = 0;
for (j = 10000; j; j--) {
uint8_t v = bus_space_read_1(t, h, fdc->sc_reg_msr);
v &= (NE7_DIO | NE7_RQM | NE7_CB);
if (v == NE7_RQM)
return fdc->sc_nstat = n;
if (v == (NE7_DIO | NE7_RQM | NE7_CB)) {
if (n >= sizeof(fdc->sc_status)) {
log(LOG_ERR, "fdcresult: overrun\n");
return -1;
}
fdc->sc_status[n++] =
bus_space_read_1(t, h, fdc->sc_reg_fifo);
} else
delay(1);
}
/*
* Write a command byte to the FDC.
* Returns 0 on success; -1 on failure (i.e. timeout)
*/
int
fdc_wrfifo(struct fdc_softc *fdc, uint8_t x)
{
bus_space_tag_t t = fdc->sc_bustag;
bus_space_handle_t h = fdc->sc_handle;
int i;
for (i = 100000; i-- > 0;) {
uint8_t v = bus_space_read_1(t, h, fdc->sc_reg_msr);
if ((v & (NE7_DIO|NE7_RQM)) == NE7_RQM) {
/* The chip is ready */
bus_space_write_1(t, h, fdc->sc_reg_fifo, x);
return 0;
}
delay(1);
}
return -1;
}
int
fdc_diskchange(struct fdc_softc *fdc)
{
#ifdef SUN4
if (CPU_ISSUN4M && (fdc->sc_flags & FDC_82077) != 0) {
#endif
bus_space_tag_t t = fdc->sc_bustag;
bus_space_handle_t h = fdc->sc_handle;
uint8_t v = bus_space_read_1(t, h, fdc->sc_reg_dir);
return (v & FDI_DCHG) != 0;
#ifdef SUN4
} else if (CPU_ISSUN4C) {
return (*AUXIO4C_REG & AUXIO4C_FDC) != 0;
}
return 0;
#endif
}
int
fdopen(dev_t dev, int flags, int fmt, struct lwp *l)
{
int pmask;
struct fd_softc *fd;
struct fd_type *type;
fd = device_lookup_private(&fd_cd, FDUNIT(dev));
if (fd == NULL)
return ENXIO;
type = fd_dev_to_type(fd, dev);
if (type == NULL)
return ENXIO;
#ifdef DIAGNOSTIC
/* only got here if controller's drive queue was inactive; should
be in idle state */
if (fdc->sc_state != DEVIDLE) {
printf("fdcstart: not idle\n");
return;
}
#endif
(void)fdcstate(fdc);
}
/* Just ensure it has the right spl. */
s = splbio();
(void)fdcstate(fdc);
splx(s);
}
/*
* hardware interrupt entry point: used only if no `fast trap' * (in-window)
* handler is available. Unfortunately, we have no reliable way to
* determine that the interrupt really came from the floppy controller;
* just hope that the other devices that share this interrupt level
* can do better..
*/
int
fdc_c_hwintr(void *arg)
{
struct fdc_softc *fdc = arg;
bus_space_tag_t t = fdc->sc_bustag;
bus_space_handle_t h = fdc->sc_handle;
switch (fdc->sc_itask) {
case FDC_ITASK_NONE:
return 0;
case FDC_ITASK_SENSEI:
if (fdc_wrfifo(fdc, NE7CMD_SENSEI) != 0 || fdcresult(fdc) == -1)
fdc->sc_istatus = FDC_ISTATUS_ERROR;
else
fdc->sc_istatus = FDC_ISTATUS_DONE;
softint_schedule(fdc->sc_sicookie);
return 1;
case FDC_ITASK_RESULT:
if (fdcresult(fdc) == -1)
fdc->sc_istatus = FDC_ISTATUS_ERROR;
else
fdc->sc_istatus = FDC_ISTATUS_DONE;
softint_schedule(fdc->sc_sicookie);
return 1;
case FDC_ITASK_DMA:
/* Proceed with pseudo-DMA below */
break;
default:
printf("fdc: stray hard interrupt: itask=%d\n", fdc->sc_itask);
fdc->sc_istatus = FDC_ISTATUS_SPURIOUS;
softint_schedule(fdc->sc_sicookie);
return 1;
}
/*
* Pseudo DMA in progress
*/
for (;;) {
uint8_t msr;
msr = bus_space_read_1(t, h, fdc->sc_reg_msr);
if ((msr & NE7_RQM) == 0)
/* That's all this round. */
break;
if (fdc->sc_istatus == FDC_ISTATUS_ERROR) {
/* Prevent loop if the reset sequence produces errors */
if (fdc->sc_state != RESETCOMPLETE &&
fdc->sc_state != RECALWAIT &&
fdc->sc_state != RECALCOMPLETE)
fdc->sc_state = DORESET;
}
/* Clear I task/status field */
fdc->sc_istatus = FDC_ISTATUS_NONE;
fdc->sc_itask = FDC_ITASK_NONE;
loop:
/* Is there a drive for the controller to do a transfer with? */
fd = fdc->sc_drives.tqh_first;
if (fd == NULL) {
fdc->sc_state = DEVIDLE;
return 0;
}
/* Is there a transfer to this drive? If not, deactivate drive. */
bp = bufq_peek(fd->sc_q);
if (bp == NULL) {
fd->sc_ops = 0;
TAILQ_REMOVE(&fdc->sc_drives, fd, sc_drivechain);
fd->sc_active = 0;
goto loop;
}
if (bp->b_flags & B_FORMAT)
finfo = (struct ne7_fd_formb *)bp->b_data;
switch (fdc->sc_state) {
case DEVIDLE:
fdc->sc_errors = 0;
fd->sc_skip = 0;
fd->sc_bcount = bp->b_bcount;
fd->sc_blkno = (bp->b_blkno * DEV_BSIZE) / FD_BSIZE(fd);
callout_stop(&fd->sc_motoroff_ch);
if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) {
fdc->sc_state = MOTORWAIT;
return 1;
}
if ((fd->sc_flags & FD_MOTOR) == 0) {
/* Turn on the motor, being careful about pairing. */
struct fd_softc *ofd = fdc->sc_fd[fd->sc_drive ^ 1];
if (ofd && ofd->sc_flags & FD_MOTOR) {
callout_stop(&ofd->sc_motoroff_ch);
ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
}
fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT;
fd_set_motor(fdc);
fdc->sc_state = MOTORWAIT;
if ((fdc->sc_flags & FDC_NEEDMOTORWAIT) != 0) { /*XXX*/
/* Allow .25s for motor to stabilize. */
callout_reset(&fd->sc_motoron_ch, hz / 4,
fd_motor_on, fd);
} else {
fd->sc_flags &= ~FD_MOTOR_WAIT;
goto loop;
}
return 1;
}
/* Make sure the right drive is selected. */
fd_set_motor(fdc);
if (fdc_diskchange(fdc))
goto dodskchg;
/*FALLTHROUGH*/
case DOSEEK:
doseek:
if ((fdc->sc_flags & FDC_EIS) &&
(bp->b_flags & B_FORMAT) == 0) {
fd->sc_cylin = bp->b_cylinder;
/* We use implied seek */
goto doio;
}
case DODSKCHG:
dodskchg:
/*
* Disk change: force a seek operation by going to cyl 1
* followed by a recalibrate.
*/
disk_busy(&fd->sc_dk);
callout_reset(&fdc->sc_timo_ch, 4 * hz, fdctimeout, fdc);
fd->sc_cylin = -1;
fdc->sc_nstat = 0;
fdc->sc_state = DSKCHGWAIT;
case SEEKWAIT:
callout_stop(&fdc->sc_timo_ch);
fdc->sc_state = SEEKCOMPLETE;
if (fdc->sc_flags & FDC_NEEDHEADSETTLE) {
/* allow 1/50 second for heads to settle */
callout_reset(&fdc->sc_intr_ch, hz / 50,
fdcpseudointr, fdc);
return 1; /* will return later */
}
/*FALLTHROUGH*/
case SEEKCOMPLETE:
/* no data on seek */
disk_unbusy(&fd->sc_dk, 0, 0);
case IOTIMEDOUT:
/*
* Try to abort the I/O operation without resetting
* the chip first. Poke TC and arrange to pick up
* the timed out I/O command's status.
*/
fdc->sc_itask = FDC_ITASK_RESULT;
fdc->sc_state = IOCLEANUPWAIT;
fdc->sc_nstat = 0;
/* 1/10 second should be enough */
callout_reset(&fdc->sc_timo_ch, hz / 10, fdctimeout, fdc);
FTC_FLIP;
return 1;
case IOCLEANUPTIMEDOUT:
case SEEKTIMEDOUT:
case RECALTIMEDOUT:
case RESETTIMEDOUT:
case DSKCHGTIMEDOUT:
fdcstatus(fdc, "timeout");
/* All other timeouts always roll through to a chip reset */
fdcretry(fdc);
/* Force reset, no matter what fdcretry() says */
fdc->sc_state = DORESET;
goto loop;
xxx:
/*
* We get here if the chip locks up in FDC_WRFIFO()
* Cancel any operation and schedule a reset
*/
callout_stop(&fdc->sc_timo_ch);
fdcretry(fdc);
fdc->sc_state = DORESET;
goto loop;
case 1: case 2: case 3:
/* didn't work; try recalibrating */
fdc->sc_state = DORECAL;
break;
case 4:
if (fdc->sc_nstat == 7 &&
fdc->sc_status[0] == 0 &&
fdc->sc_status[1] == 0 &&
fdc->sc_status[2] == 0) {
/*
* We've retried a few times and we've got
* valid status and all three status bytes
* are zero. Assume this condition is the
* result of no disk loaded into the drive.
*/
aprint_error_dev(fdc->sc_dev, "no medium?\n");
error = ENODEV;
goto failsilent;
}
/* still no go; reset the bastard */
fdc->sc_state = DORESET;
break;
case DIOCLOCK:
/*
* Nothing to do here, really.
*/
return 0;
case DIOCEJECT:
if (*(int *)addr == 0) {
int part = DISKPART(dev);
/*
* Don't force eject: check that we are the only
* partition open. If so, unlock it.
*/
if ((fd->sc_dk.dk_openmask & ~(1 << part)) != 0 ||
fd->sc_dk.dk_bopenmask + fd->sc_dk.dk_copenmask !=
fd->sc_dk.dk_openmask) {
return EBUSY;
}
}
/* FALLTHROUGH */
case ODIOCEJECT:
if (fdc->sc_flags & FDC_NOEJECT)
return EINVAL;
fd_do_eject(fd);
return 0;
case FDIOCSETFORMAT:
if ((flag & FWRITE) == 0)
return EBADF; /* must be opened for writing */
form_parms = (struct fdformat_parms *)addr;
if (form_parms->fdformat_version != FDFORMAT_VERSION)
return EINVAL;/* wrong version of formatting prog */
i = form_parms->nbps >> 7;
if ((form_parms->nbps & 0x7f) || ffs(i) == 0 ||
i & ~(1 << (ffs(i)-1)))
/* not a power-of-two multiple of 128 */
return EINVAL;
case FDIOCFORMAT_TRACK:
if((flag & FWRITE) == 0)
/* must be opened for writing */
return EBADF;
form_cmd = (struct fdformat_cmd *)addr;
if (form_cmd->formatcmd_version != FDFORMAT_VERSION)
/* wrong version of formatting prog */
return EINVAL;
/*
* Call the generic disklabel extraction routine. If there's
* not a label there, fake it.
*/
if (readdisklabel(dev, fdstrategy, lp, clp) != NULL) {
strncpy(lp->d_packname, "default label",
sizeof(lp->d_packname));
/*
* Reset the partition info; it might have gotten
* trashed in readdisklabel().
*
* XXX Why do we have to do this? readdisklabel()
* should be safe...
*/
for (i = 0; i < MAXPARTITIONS; ++i) {
lp->d_partitions[i].p_offset = 0;
if (i == RAW_PART) {
lp->d_partitions[i].p_size =
lp->d_secpercyl * lp->d_ncylinders;
lp->d_partitions[i].p_fstype = FS_BSDFFS;
} else {
lp->d_partitions[i].p_size = 0;
lp->d_partitions[i].p_fstype = FS_UNUSED;
}
}
lp->d_npartitions = RAW_PART + 1;
}
}