|
|
coherent
/* (-lgl
* COHERENT Device Driver Kit version 2.0.1.b
* Copyright (c) 1982, 1991, 1992 by Mark Williams Company.
* All rights reserved. May not be copied without permission.
-lgl) */
/*
* This is a driver for the IBM AT and "above"
* floppy drives, using interrupts and DMA on
* the NEC 756 floppy chip. Ugh.
* Handles single, double and quad
* density drives, 8, 9, 15 or 18 sectors per track.
* It wil NOT run on an XT; it requires the CMOS setup memory.
*
* Minor device assignments: xxuuhkkk
* uu - unit = 0/1/2/3
* kkk - kind, struct fdata infra.
* h - alternating head rather than side by side
*
*/
#include <sys/coherent.h>
#ifdef _I386
#include <sys/reg.h>
#else
#include <sys/i8086.h>
#endif
#include <sys/buf.h>
#include <sys/con.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/uproc.h>
#include <sys/proc.h>
#include <sys/fdioctl.h>
/*
* Additional internal commands not available
* to outside users:
*/
#include <sys/sched.h>
#include <sys/dmac.h>
#include <sys/devices.h>
#include <sys/seg.h>
#include <sys/inode.h>
#define BIT(n) (1 << (n))
#define MAXDRVS 2 /* Maximum number of drives that we will support */
#define MAXSCTRS 21 /* Maximum acceptable sectors per track. It is */
/* possible to put as many as 10 sectors/track */
/* on low density diskettes, 18 sectors/track on */
/* 5 1/4" high-density diskettes, and 21 sectors */
/* per track on 3 1/2" high density diskettes. */
/* the maximum on "2.88" meg diskettes is not */
/* known as of this writing, but may be as high */
/* as 42. */
#define MAXTYPE 4 /* Maximum "type" value as set in the CMOS RAM. */
/* The current value of this is 4, the type for */
/* 1.44 meg diskettes. */
#ifdef FL_XTRA
/* This is conditioned out for now due to end-of-volume problems. */
#define FL_CYL_2STEP 42
#define FL_CYL_HDLO 82
#define FL_CYL_HDHI 83
#else
#define FL_CYL_2STEP 40
#define FL_CYL_HDLO 80
#define FL_CYL_HDHI 80
#endif
/*
* Patchable parameters.
*/
int fl_srt = 0xD; /* Floppy seek step rate, in unit -2 millisec */
/* NOT DIRECTLY ENCODED */
/* All drives HAVE to work at 5 msec/step, so */
/* they HAVE to work at 6 msec/step! (The use of */
/* 8 msec/step in the old PCs was just IBM being */
/* excessively conservative. MS/PC-DOS, starting with */
/* version 2.0, changes it to 6 msec/step. There */
/* are, in fact, lots of drives that will step at */
/* 4 msec/step, but there's no sense in pushing it.) */
int fl_hlt = 1; /* Floppy head load time, in unit 4 millisec */
int fl_hut = 0xF; /* Floppy head unload time, in unit 32 millisec */
int fl_disp = 0; /* If nonzero, print drive parameters on screen */
/*
* Patchable variables for compatibility with IBM products:
*
* FL_DSK_CH_PROB - some machines always have the disk changed line turned on.
* Currently some PS/1's (Consultant, Professional - possibly most of them)
* have this problem, so the default value of zero assumes normal disk change
* line operation.
*
* FL_AUTO_PARM - Only try to autosense floppy parameters if this variable
* is nonzero. The PS/2-L40 floppy controller apparently has trouble changing
* from low density to high density. Missing address marks when reading
* a HD floppy are the symptom if FL_AUTO_PARM is set when it shouldn't be.
*/
int FL_DSK_CH_PROB = 0;
int FL_AUTO_PARM = 0;
static int jopen;
/*
Here's the problem. We need to be able to tell if the disk has been changed.
There is an i/o port we can read, but on some systems, we can get constant
false positives. This causes massive floppy slowdown as the disk is constantly
recalibrating. The solution is not completely satisfactory, but it's the best
one I could come up with. Basically, what I said was since we can't tell when
the disk has changed, we will act as if it has changed every time we do an
open or a reset. The code
if (FL_DSK_CH_PROB)
jopen = 2;
indicates the need to pretend that the disk has changed. It is set to 2 since
there are two parts to the change procedure. Additional code dependent on
the value of FL_DSK_CH_PROB says that if we have not just down an open, then
we should skip the recal. Otherwise, decrement the counter, and do the
recal. - mlk */
int flload();
int flunload();
void flopen();
int flclose();
int flblock();
int flread();
int flwrite();
int flioctl();
int fldelay();
int flintr();
int fltimeout();
int nulldev();
void fldrvstatus();
CON flcon = {
DFBLK | DFCHR, /* Flags */
FL_MAJOR, /* Major index */
flopen, /* Open */
flclose, /* Close */
flblock, /* Block */
flread, /* Read */
flwrite, /* Write */
flioctl, /* Ioctl */
nulldev, /* Powerfail */
fltimeout, /* Timeout */
flload, /* Load */
flunload /* Unload */
};
#define MTIMER 2 /* Motor timeout */
#define FDCDOR 0x3F2 /* Digital output */
#define FDCDAT 0x3F5 /* Data register */
#define FDCMSR 0x3F4 /* Main status register */
#define FDCRATE 0x3F7 /* Transfer rate (500,300,250 Kbps) */
#define FDCCHGL 0x3F7 /* Port where we read the disk */
/* changed line */
#define DSKCHGD 0x80 /* Diskette changed line bit. */
#define DORDS 0x03 /* Drive select bits */
#define DORNMR 0x04 /* Not master reset */
#define DORIEN 0x08 /* Interrupt, DMA enable */
#define DORMS 0xF0 /* Motor enables */
#define MSRDB 0x0F /* Drive busy */
#define MSRCB 0x10 /* Control busy */
#define MSRNDMA 0x20 /* Not DMA */
#define MSRDIO 0x40 /* Data direction */
#define MSRRQM 0x80 /* Request for master */
/*
* Status Register 0 - Bit Definitions.
*/
#define ST0_US0 0x01 /* Unit Select 0 */
#define ST0_US1 0x02 /* Unit Select 1 */
#define ST0_HD 0x04 /* Head Address */
#define ST0_NR 0x08 /* Not Ready */
#define ST0_EC 0x10 /* Equipment Check */
#define ST0_SE 0x20 /* Seek End */
#define ST0_IC 0xC0 /* Interrupt code */
#define ST0_NT 0x00 /* Normal Termination */
/*
* Status Register 1 - Bit Definitions.
*/
#define ST1_MA 0x01 /* Missing Address Mark */
#define ST1_NW 0x02 /* Not writeable */
#define ST1_ND 0x04 /* No Data */
/* 0x08 */ /* Not used - always 0 */
#define ST1_OR 0x10 /* Overrun */
#define ST1_DE 0x20 /* Data Error */
/* 0x40 */ /* Not used - always 0 */
#define ST1_EN 0x80 /* End of Cylinder */
/*
* Status Register 2 - Bit Definitions.
*/
#define ST2_MD 0x01 /* Missing Address Mark in Data Field */
#define ST2_BC 0x02 /* Bad Cylinder */
#define ST2_SN 0x04 /* Scan Not Satisfied */
#define ST2_SH 0x08 /* Scan Equal Hit */
#define ST2_WC 0x10 /* Wrong Cylinder */
#define ST2_DD 0x20 /* Data Error in Data Field */
#define ST2_CM 0x40 /* Control Mark */
/* 0x80 */ /* Not used - always 0 */
/*
* Status Register 3 - Bit Definitions.
*/
#define ST3_US0 0x01 /* Unit Select 0 */
#define ST3_US1 0x02 /* Unit Select 1 */
#define ST3_HD 0x04 /* Head Address */
#define ST3_TS 0x08 /* Two Sides */
#define ST3_T0 0x10 /* Track 0 */
#define ST3_RDY 0x20 /* Ready */
#define ST3_WP 0x40 /* Write Protected */
#define ST3_FT 0x80 /* Fault */
/*
* Controller Commands.
*/
#define CMDSPEC 0x03 /* Specify */
#define CMDRCAL 0x07 /* Recal */
#define CMDSEEK 0x0F /* Seek */
#define CMDRDAT 0x66 /* Read data */
#define CMDWDAT 0x45 /* Write data */
#define CMDSINT 0x08 /* Sense interrupt status */
#define CMDSDRV 0x04 /* Sense drive status */
#define CMDRDID 0x4A /* Read ID */
#define CMDFMT 0x4D /* Format track */
/*
* Driver States.
*/
#define SIDLE 0 /* Idle */
#define SSEEK 1 /* Need seek */
#define SRDWR 2 /* Need read/write command */
#define SENDIO 3 /* Need end I/O processing */
#define SDELAY 4 /* Delay before next disk operation */
#define SHDLY 5 /* Head settling delay before r/w */
#define SLOCK 6 /* Got DMA controller lock */
#define SRECAL1 7 /* First recalibrate attempt */
#define SRECAL2 8 /* Try seeking to track 2 */
#define SRECAL3 9 /* Second recalibrate, if necessary */
#define SGOTO2 10 /* After seek to cylinder 2 */
#define SRDID 11 /* Get sector ID from FDC */
#define SSIDTST 12 /* Testing # of sides */
#define funit(x) (minor(x) >> 4) /* Unit/drive number */
#define fkind(x) (minor(x) & 0x7) /* Kind of format */
#define fhbyh(x) (minor(x) & 0x8) /* 0=Side by side, 1=Head by head */
static /* Parameters for each kind of format */
struct FDATA {
int fd_size; /* Blocks per diskette */
int fd_nhds; /* Heads per drive */
int fd_trks; /* Tracks per side */
int fd_offs; /* Sector base */
int fd_nspt; /* Sectors per track */
char fd_GPL[4]; /* Controller gap param (indexed by rate) */
char fd_N; /* Controller size param */
char fd_FGPL; /* Format gap length */
} fdata[] = {
/* 8 sectors per track, surface by surface seek. */
{ 320,1,40,0, 8, { 0x00,0x20,0x20 }, 2,0x58 }, /* Single sided */
{ 640,2,40,0, 8, { 0x00,0x20,0x20 }, 2,0x58 }, /* Double sided */
{ 1280,2,80,0, 8, { 0x00,0x20,0x20 }, 2,0x58 }, /* Quad density */
/* 9 sectors per track, surface by surface seek. */
{ 360,1,40,0, 9, { 0x00,0x20,0x20 }, 2,0x50 }, /* Single sided */
{ 720,2,40,0, 9, { 0x00,0x20,0x20 }, 2,0x50 }, /* Double sided */
{ 1440,2,80,0, 9, { 0x00,0x20,0x20 }, 2,0x50 }, /* Quad density */
/* 15 sectors per track, surface by surface seek. */
{ 2400,2,80,0,15, { 0x1B,0x00,0x00 }, 2,0x54 }, /* High capacity */
/* 18 sectors per track, surface by surface seek. */
{ 2880,2,80,0,18, { 0x1B,0x00,0x00 }, 2,0x6C } /* 1.44 3.5" */
};
static /* Parameters for each device type */
struct FRATES {
char fl_hi_kind; /* "fdata" initial try for hi dens. */
char fl_hi_rate; /* -1 here for lo-dens. */
char fl_lo_kind; /* Lo-dens "fdata" entry to try 1st.*/
char fl_lo_rate; /* Proper lo-density rate for type. */
char dflt_kind; /* Default parameters. */
char dflt_rate; /* Default data rate. */
} frates[] = {
{ 4,-1, 4,-1, 4, 2 }, /* Type 0 = no drive */
{ 4,-1, 4, 2, 4, 2 }, /* Type 1 = 360K */
{ 6, 0, 4, 1, 6, 0 }, /* Type 2 = 1.2M */
{ 4,-1, 5, 2, 5, 2 }, /* Type 3 = 720K */
{ 7, 0, 5, 2, 7, 0 } /* Type 4 = 1.44M */
};
static
struct FL {
BUF *fl_actf; /* Queue, forward */
BUF *fl_actl; /* Queue, backward */
paddr_t fl_addr; /* Address */
int fl_nsec; /* # of sectors */
int fl_secn; /* Current sector */
struct FDATA fl_fd[MAXDRVS]; /* Disk kind data */
int fl_fcyl; /* Floppy cylinder # */
int fl_2step[MAXDRVS]; /* =1 for double-stepping */
char fl_incal[MAXDRVS]; /* Disk in cal flags and current cyl */
char fl_dsk_chngd[MAXDRVS]; /* Diskette changed flags */
char fl_ndsk; /* # of drives */
char fl_unit; /* Current unit # */
char fl_selected_unit; /* Last unit selected */
char fl_mask; /* Handy unit mask */
char fl_hbyh; /* 0/1 = Side by side/Head by head */
char fl_nerr; /* Error count */
int fl_ncmdstat; /* Number of cmd status bytes recvd */
char fl_cmdstat[8]; /* Command Status buffer */
int fl_nintstat; /* Number of intr status bytes recvd */
char fl_intstat[4]; /* Interrupt Status buffer */
int fl_ndrvstat; /* Number of drv status bytes read */
char fl_drvstat[2]; /* Drive Status buffer */
int fl_fsec; /* Floppy sector # */
int fl_head; /* Floppy head */
char fl_state; /* Processing state */
char fl_mstatus; /* Motor status */
char fl_time[MAXDRVS]; /* Motor timeout */
char fl_rate[MAXDRVS]; /* Data rate: 500,300,250,?? kbps */
char fl_type[MAXDRVS]; /* Type of drive: 2 = HiCap */
char fl_rate_set; /* Currently set data rate */
int fl_wflag; /* Write operation */
int fl_recov; /* Recovery initiated */
int fl_opct[MAXDRVS]; /* open count for each unit */
int fl_we[MAXDRVS]; /* write enable for each unit */
} fl;
/*
* We need some areas in global RAM to use as BUF structures
* and as data areas for special functions such as formatting,
* reading the drive status and reading sector IDs. There is
* one set for each drive. When the blocks for a drive are in
* use, the "drv_locked" char is set to non-zero, and is
* cleared otherwise. While this scheme doesn't provide
* complete reentrancy, it does allow both drives to be used
* "at once" by separate tasks.
*/
static char drv_locked[MAXDRVS]; /* One for each possible drive */
static char sw3[MAXDRVS];
static BUF flbuf[MAXDRVS];
/*
* These next items are related to the floppy disk system
* in general, so we only need one of each.
*/
static TIM fltim;
static TIM fldmalck; /* DMA lock deferred function structure. */
static char scratch_buffer[BSIZE];
static char fl_clrng_cd;
static char fl_intlv_ct, /* Counts sectors to find interleave. */
fl_get_intlv, /* =1 to start search for interleave. */
fl_lk4_id, /* Sector ID to look for for interleave. */
fl_alt_kind, /* Alternate disk parameter index and */
fl_alt_rate, /* data rate to use if not first value. */
fl_1st_ID, /* Detects when all track sectors scanned*/
fl_hi_ID; /* Highest sector ID read so far. */
/*-------- DEBUG START ---------*/
#define FL_DEBUG 1
#if FL_DEBUG
#define PUSH3(a,b,c) push3(a,b,c)
#define POP3() pop3()
#define XX 75
static int A[XX], B[XX], C[XX];
static int xxp = 0; /* next avail index */
static push3(a,b,c)
int a,b,c;
{
if (xxp < XX) {
A[xxp] = a;
B[xxp] = b;
C[xxp] = c;
xxp++;
}
}
static pop3()
{
int i;
printf("pop3: ");
for (i = 0; i < xxp; i++) {
printf("c5=%d 1st=%d hi=%d ", A[i], B[i], C[i]);
}
xxp = 0;
printf(":done ");
}
#else
#define PUSH3(a,b,c)
#define POP3()
#endif
/*-------- DEBUG END ---------*/
/*
* The load routine asks the
* switches how many drives are present
* in the machine, and sets up the field
* in the floppy database. It also grabs
* the level 6 interrupt vector.
*/
static
flload()
{
register int eflag;
register int s, t;
fl_clrng_cd = 0;
/*
* Ensure DMA channel 2 is turned off.
* The Computerland ROM does not disable DMA channel after autoboot
* from hard disk. The Western Digital controller board appears to
* send a dma burst when the floppy controller chip is reset.
*/
dmaoff(2);
/*
* Read floppy equipment byte from CMOS ram
* drive 0 is in high nibble, drive 1 is in low nibble.
*/
outb(0x70, 0x10);
/* delay */
eflag = inb(0x71);
/*
* Reinitialize patchable parameters for IBM AT.
*/
fl_srt = 0xD; /* Floppy seek step rate, in unit -2 ms */
/* NOT DIRECTLY ENCODED */
fl_hlt = 25; /* Floppy head load time, in unit 4 ms */
/*
* Define AT drive information.
*/
fl.fl_type[0] = eflag >> 4;
fl.fl_type[1] = eflag & 15;
fl.fl_ndsk = 0;
for (s = 0; s < MAXDRVS; s++) {
drv_locked[s] = 0;
fl.fl_dsk_chngd[s] = 1;
fl.fl_incal[s] = -1;
t = fl.fl_type[s];
if (t > MAXTYPE) /* --in case we get, like, */
fl.fl_type[s] = t = MAXTYPE; /* a 2.88 meg drive. */
fl.fl_rate[s] = frates[t].dflt_rate;
fl.fl_fd[s] = fdata[frates[t].dflt_kind];
if (t) fl.fl_ndsk = s + 1; /* Type 0 = no drive. */
if (FL_DSK_CH_PROB)
jopen = 2;
}
fl.fl_rate_set = 0;
/*
* Initialize the floppy disk controller (if we
* have any floppy drives).
*/
if (fl.fl_ndsk) {
s = sphi();
outb(FDCDOR, 0);
setivec(6, &flintr);
/* "Not Reset FDC" must remain low for at least 14 clocks */
outb(FDCDOR, 0);
fl.fl_state = SIDLE;
outb(FDCDOR, DORNMR | DORIEN);
fl.fl_mstatus = 0; /* No motors on */
fl.fl_selected_unit = -1; /* No unit selected */
flspecify();
outb(FDCRATE, fl.fl_rate_set);
spl(s);
}
}
/*
* Release resources.
*/
flunload()
{
/*
* Cancel timed function.
*/
timeout(&fltim, 0, NULL, NULL);
/*
* Cancel periodic (1 second) invocation.
*/
drvl[FL_MAJOR].d_time = 0;
/*
* Turn motors off.
*/
outb(FDCDOR, DORNMR); /* Leave interrupts disabled. */
/*
* Clear interrupt vector.
*/
if (fl.fl_ndsk)
clrivec(6);
}
/*
* The open routine screens out
* opens of illegal minor devices and
* performs the NEC specify command if
* this is the very first floppy disk
* open call.
*/
static void
flopen(dev, mode)
dev_t dev;
int mode;
{
register int unit_number = funit(dev);
register int s;
/*
* Validate existence and data rate (Gap length != 0).
*/
if ((unit_number >= fl.fl_ndsk)
|| (fl.fl_type[unit_number] == 0)
|| (fdata[fkind(dev)].fd_GPL[flrate(dev)] == 0)) {
u.u_error = ENXIO;
goto badFlopen; /* status. */
}
if (FL_DSK_CH_PROB)
jopen = 2;
/*
* May need to write - see if diskette is write proteced.
* We do this with a "Sense Drive Status" command. Since
* this requires the use of the FDC, we have to schedule it
* like data transfer I/O or FORMAT even though it doesn't
* use the DMA.
*/
if (fl.fl_opct[unit_number] == 0) { /* first open for this floppy */
if (drv_locked[unit_number]) { /* Work areas avail? */
u.u_error = EBUSY; /* No. */
goto badFlopen; /* status. */
} else {
drv_locked[unit_number] = 1; /* Grab work areas. */
flbuf[unit_number].b_dev = dev;
flbuf[unit_number].b_req = BFLSTAT;
sw3[unit_number] = 0;
/* Get drive status. */
s = flQhang(&flbuf[unit_number]);
for (;;) {
s = sphi();
if (fl.fl_state == SIDLE)
flfsm();
spl(s);
if (sw3[unit_number])
break;
if (fl.fl_state != SIDLE)
#ifdef _I386
x_sleep(&fl.fl_state,
pridisk, slpriSigCatch, "flopen");
#else
v_sleep(&fl.fl_state,
CVBLKIO, IVBLKIO, SVBLKIO,
"flopen");
#endif
if (SELF->p_ssig && nondsig()) { /* signal? */
u.u_error = EINTR;
goto badFlopen;
}
}
if (flbuf[unit_number].b_resid != 0) {
u.u_error = EDATTN; /* Couldn't get drive */
goto badFlopen; /* status. */
}
/* The payoff - set write enable status. */
fl.fl_we[unit_number] =
((sw3[unit_number] & ST3_WP)==0);
drv_locked[unit_number] = 0; /* Release work areas */
}
/*
* If the drive is low density (no change line) we should
* flag the need to verify the disk format and density.
* High density drives (which are also dual density) have
* change lines that we can check each time we want to read
* the drive.
*/
if (frates[fl.fl_type[unit_number]].fl_hi_rate == -1) {
fl.fl_incal[unit_number] = -1;
fl.fl_dsk_chngd[unit_number] = 1;
if (FL_DSK_CH_PROB)
jopen = 2;
}
} /* end of first open stuff */
/* If opening for write, volume must be write enabled. */
if ((mode & IPW) && !fl.fl_we[unit_number]) {
printf("fd%d: <Write Protected>\n", fl.fl_unit);
u.u_error = EROFS; /* Diskette write */
goto badFlopen; /* protected. */
}
fl.fl_opct[unit_number]++;
badFlopen:
return;
}
/*
* flclose()
*/
static
flclose(dev, mode)
dev_t dev;
int mode;
{
register int unit_number = funit(dev);
fl.fl_opct[unit_number]--;
}
/*
* The read routine just calls
* off to the common raw I/O processing
* code, using a static buffer header in
* the driver.
*/
static
flread(dev, iop)
dev_t dev;
IO *iop;
{
dmareq(&flbuf[funit(dev)], iop, dev, BREAD);
}
/*
* The write routine is just like the
* read routine, except that the function code
* is write instead of read.
*/
static
flwrite(dev, iop)
dev_t dev;
IO *iop;
{
dmareq(&flbuf[funit(dev)], iop, dev, BWRITE);
}
/*
* The ioctl routine simply queues a format request
* using the flbuf for the specified drive.
* The only valid command is to format a track.
* The parameter block contains the header records supplied to the controller.
*/
static
flioctl(dev, com, par)
dev_t dev;
int com;
char *par;
{
register unsigned s;
register struct fdata *fdp;
unsigned hd, cyl;
if (com != FDFORMAT) {
u.u_error = EINVAL;
return;
}
fdp = &fdata[fkind(dev)]; /* Locate formatting */
cyl = getubd(par); /* parameters. */
hd = getubd(par+1);
if (hd > 1 || cyl >= fdp->fd_trks) {
u.u_error = EINVAL;
return;
}
/*
* The following may need some explanation.
* dmareq will:
* claim the buffer,
* bounds check the parameter buffer,
* lock the parameter buffer in memory,
* convert io_seek to b_bno,
* dispatch the request,
* wait for completion,
* and unlock the parameter buffer.
* The b_bno is reconverted to hd, cyl in flfsm.
*/
s = fhbyh(dev) ? (cyl * fdp->fd_nhds + hd) : (hd * fdp->fd_trks + cyl);
s *= fdp->fd_nspt;
u.u_io.io_seek = ((long)s) * BSIZE;
#ifdef _I386
u.u_io.io.vbase = par;
#else
u.u_io.io_base = par;
#endif
u.u_io.io_ioc = fdp->fd_nspt * 4;
dmareq(&flbuf[funit(dev)], &u.u_io, dev, BFLFMT);
return 0;
}
/*
* Start up block I/O on a
* buffer. Check that the block number
* is not out of range, given the style of
* the disk. Put the buffer header into the
* device queue. Start up the disk if the
* device is idle.
*/
static
flblock(bp)
register BUF *bp;
{
register int s;
register unsigned bno;
/* Nasty implicit dependency on BSIZE == 2**9 */
bno = bp->b_bno + (bp->b_count >> 9) - 1;
{ /*DEBUG*/
int first = bp->b_bno, last = bno;
int fdatasz = fdata[fkind(bp->b_dev)].fd_size;
int fl_fdsz = fl.fl_fd[funit(bp->b_dev)].fd_size;
/*DEBUG*/
if ((bp->b_req == BFLFMT)
&& ((unsigned)bp->b_bno >= fdata[fkind(bp->b_dev)].fd_size)) {
bp->b_flag |= BFERR;
bdone(bp);
return;
}
if (bp->b_req != BFLFMT) {
if ((unsigned)bp->b_bno >=
fl.fl_fd[funit(bp->b_dev)].fd_size) {
bp->b_flag |= BFERR;
bdone(bp);
return;
}
if (bno >= fl.fl_fd[funit(bp->b_dev)].fd_size) {
if (bp->b_flag & BFRAW) {
bp->b_flag |= BFERR;
}
bp->b_resid = bp->b_count;
bdone(bp); /* return w/ b_resid != 0 */
return;
}
if ((bp->b_count & 0x1FF) != 0) {
bp->b_flag |= BFERR;
bdone(bp);
return;
}
}
} /*DEBUG*/
flQhang(bp); /* Put the block in the queue. */
s = sphi();
if (fl.fl_state == SIDLE) /* --if necessary, to */
flfsm(); /* get things moving. */
spl(s);
}
/*
* This routine hangs a BUF in the processing queue
*/
static
flQhang(bp)
register BUF *bp;
{
register int s = sphi(); /* No interrupts during chaining, please */
bp->b_actf = NULL;
if (fl.fl_actf == NULL)
fl.fl_actf = bp;
else
fl.fl_actl->b_actf = bp;
fl.fl_actl = bp;
spl(s);
}
/*
* This finite state machine is
* responsible for all sequencing on the disk.
* It builds the commands, does the seeks, spins up
* the drive motor for 1 second on the first call,
* and so on.
* Note that the format command is rather obscurely shoehorned into this.
*/
static
flfsm()
{
register BUF *bp;
register int flcmd;
register int i;
int dods; /* for PS/1, do disk swap */
again:
bp = fl.fl_actf;
switch (fl.fl_state) {
case SIDLE:
T_HAL(0x40000, printf("SIDLE "));
drvl[FL_MAJOR].d_time = 1;
if (bp == NULL)
break;
fl.fl_unit = funit(bp->b_dev);
fl.fl_mask = 0x10 << fl.fl_unit;
#if 0
printf("drv%d: cmd=%d (%s), position=%d, count=%d\n",
fl.fl_unit,
bp->b_req,
(bp->b_req == BREAD) ? "BREAD"
: (bp->b_req == BWRITE) ? "BWRITE"
: (bp->b_req == BFLSTAT) ? "BFLSTAT"
: (bp->b_req == BFLFMT) ? "BFLFMT" : "?????",
bp->b_bno,
bp->b_count);
#endif
/*
* We do an entire check for drive status here
*/
if (bp->b_req == BFLSTAT) {
fl.fl_drvstat[0] = 0;
fldrvstatus();
sw3[fl.fl_unit] = fl.fl_drvstat[0] | 3;
bp->b_resid = (fl.fl_ndrvstat == 1) ? 0 : 1;
fl.fl_actf = bp->b_actf;
fl.fl_state = SIDLE;
goto again;
}
fl.fl_hbyh = fhbyh(bp->b_dev);
fl.fl_addr = bp->b_paddr;
fl.fl_secn = bp->b_bno;
fl.fl_time[fl.fl_unit] = 0;
if ((fl.fl_nsec = bp->b_count>>9) == 0)
fl.fl_nsec = 1;
fl.fl_nerr = 0;
/*
* Motor is turned off - turn it on, wait 1 second
* (for write operations only)
*/
if (((fl.fl_mstatus & fl.fl_mask) == 0)
|| (fl.fl_unit != fl.fl_selected_unit)) {
fldrvselect();
if ((bp->b_req == BWRITE)
|| (bp->b_req == BFLFMT)) {
timeout(&fltim, HZ, fldelay, SSEEK);
fl.fl_state = SDELAY;
break;
}
}
/* no break */
case SSEEK:
T_HAL(0x40000, printf("SSEEK "));
fldrvselect(); /* Keep drive turned on */
/*
* Test dual-density drive's disk changed line. We must
* test now before we (possibly) recalibrate the drive
* which would lose us the disk changed indication.
*/
if ((frates[fl.fl_type[fl.fl_unit]].fl_hi_rate != -1)
&& (inb(FDCCHGL) & DSKCHGD)
&& (fl_clrng_cd == 0)) {
/* See note at def of FL_DSK_CH_PROB above */
if (FL_DSK_CH_PROB) {
if (jopen) {
jopen--;
fl.fl_dsk_chngd[fl.fl_unit] = 1;
fl.fl_incal[fl.fl_unit] = -1;
}
} else {
fl.fl_dsk_chngd[fl.fl_unit] = 1;
fl.fl_incal[fl.fl_unit] = -1;
}
}
fl_clrng_cd = 0;
/*
* If we have a format command on cylinder zero, head
* zero, we must recalibrate the drive first, and set
* up the transfer speed and FDC stuff. We ignore
* a disk changed condition since the current format
* (it may, remember, be unformatted!) is of no
* consequence.
*/
if (bp->b_req == BFLFMT) {
fl.fl_dsk_chngd[fl.fl_unit] = 0;
fl.fl_fd[fl.fl_unit] = fdata[fkind(bp->b_dev)];
fl.fl_rate[fl.fl_unit] =
fl.fl_rate_set = flrate(bp->b_dev);
if ((fl.fl_fd[fl.fl_unit].fd_trks < 45)
&& (fl.fl_type[fl.fl_unit] != 1))
fl.fl_2step[fl.fl_unit] = 1;
outb(FDCRATE, fl.fl_rate_set);
if (fl.fl_secn == 0)
fl.fl_incal[fl.fl_unit] = -1;
}
/*
* Drive is not calibrated - seek to track 0.
*/
if (fl.fl_incal[fl.fl_unit] == -1) {
flput(CMDRCAL);
flput(fl.fl_unit);
fl.fl_state = SRECAL1;
break;
} else goto Recalibrated;
case SRECAL1:
T_HAL(0x40000, printf("SRECAL1 "));
/*
* If the recalibrate had to step more than 77 cylinders
* it will fail. We must check for this condition and
* try once more. With some controllers we will also get
* an error if the head STARTS over cylinder 0. In either
* event we will force a seek to track 2, then recalibrate
* again. If this fails, we can't recalibrate the drive.
*/
if ((fl.fl_nintstat != 2)
|| ((fl.fl_intstat[0] & (ST0_IC | ST0_SE)) != ST0_SE)) {
flput(CMDSEEK);
flput(fl.fl_unit);
flput(2);
fl.fl_state = SRECAL2;
break;
} else goto RecalibrateOK;
case SRECAL2:
T_HAL(0x40000, printf("SRECAL2 "));
flput(CMDRCAL);
flput(fl.fl_unit);
fl.fl_state = SRECAL3;
break;
case SRECAL3:
T_HAL(0x40000, printf("SRECAL3 "));
if ((fl.fl_nintstat != 2)
|| ((fl.fl_intstat[0] & (ST0_IC | ST0_SE)) != ST0_SE)) {
RecalFailed:
printf("fd%d: <Can't Recalibrate>\n", fl.fl_unit);
clrQ(bp->b_dev);
goto again;
}
RecalibrateOK:
flput(CMDSEEK); /* We now get off of cyl 0 */
flput(fl.fl_unit); /* to try to clear the disk */
flput(2); /* changed line, which acts */
fl.fl_state = SGOTO2; /* differently on different */
break; /* controllers. <sigh> We */
/* use cyl 2 since all for- */
case SGOTO2: /* matted disks will have a */
T_HAL(0x40000, printf("SGOTO2 "));
if ((fl.fl_nintstat != 2) /* track here. */
|| ((fl.fl_intstat[0] & (ST0_IC | ST0_SE)) != ST0_SE))
goto RecalFailed;
fl.fl_incal[fl.fl_unit] = 2; /* Heads now on cylinder 2. */
Recalibrated:
/*
* Now, if we don't have to check the interleave factor,
* we can continue with the seek!
*/
if (fl.fl_dsk_chngd[fl.fl_unit] == 0) goto RateKnown;
/*
* <sigh>. Okay, first we'll try the requested density.
*/
/* First we'll make sure */
/* we're sitting on cyl 2. */
if (fl.fl_incal[fl.fl_unit] != 2) goto RecalibrateOK;
/*
* We start by trying the requested density:
*/
/* Get requested rate.*/
i = fl.fl_rate[fl.fl_unit] = flrate(bp->b_dev);
/* This next mess gets*/
/* the disk parameters*/
/* and the alternate */
/* values. */
if (i == frates[fl.fl_type[fl.fl_unit]].fl_hi_rate){
fl.fl_fd[fl.fl_unit] =
fdata[frates[fl.fl_type[fl.fl_unit]].fl_hi_kind];
if (FL_AUTO_PARM) {
fl_alt_kind =
frates[fl.fl_type[fl.fl_unit]].fl_lo_kind;
fl_alt_rate =
frates[fl.fl_type[fl.fl_unit]].fl_lo_rate;
}
else {
fl_alt_kind =
frates[fl.fl_type[fl.fl_unit]].fl_hi_kind;
fl_alt_rate =
frates[fl.fl_type[fl.fl_unit]].fl_hi_rate;
}
} else {
fl.fl_fd[fl.fl_unit] =
fdata[frates[fl.fl_type[fl.fl_unit]].fl_lo_kind];
if (FL_AUTO_PARM) {
fl_alt_kind =
frates[fl.fl_type[fl.fl_unit]].fl_hi_kind;
fl_alt_rate =
frates[fl.fl_type[fl.fl_unit]].fl_hi_rate;
} else {
fl_alt_kind =
frates[fl.fl_type[fl.fl_unit]].fl_lo_kind;
fl_alt_rate =
frates[fl.fl_type[fl.fl_unit]].fl_lo_rate;
}
}
fl.fl_state = SRDID; /* Set up to read sector IDs. */
fl_get_intlv = 1;
fl_intlv_ct =
fl_lk4_id =
fl_1st_ID =
fl_hi_ID = 0;
/*
* Now we try the rate to see if we can read sector IDs
*/
TryRate:
if (fl.fl_rate_set != i)
outb(FDCRATE, fl.fl_rate_set = i);
GetNextID:
flput(CMDRDID);
flput(fl.fl_unit); /* Always read side 0. */
break; /* Wait for ID to arrive. */
case SRDID:
T_HAL(0x40000, printf("SRDID "));
if ((fl.fl_ncmdstat < 7) /* Did we get an ID? */
|| ((fl.fl_cmdstat[0] & ST0_IC) != ST0_NT) ) {
if (fl_alt_rate == -1) { /* No, is there an alternate?*/
flstatus(); /* No, we can't go on. */
clrQ(bp->b_dev);
PUSH3(-1,0,1);
goto again;
} else {
fl.fl_fd[fl.fl_unit] = fdata[fl_alt_kind];
i = fl.fl_rate[fl.fl_unit] = fl_alt_rate;
fl_alt_rate = -1; /* Flag tried alternate. */
PUSH3(-1,0,2);
goto TryRate; /* Try alternate density. */
}
}
/*
* Test interleave
*/
if (fl_get_intlv) /* Looking for interleave? */
if (fl_lk4_id) { /* Yes; started yet? */
if (fl_lk4_id == fl.fl_cmdstat[5]) /* Yes. */
fl_get_intlv = 0; /* We have a hit.*/
else /* No hit yet, */
fl_intlv_ct++; /* count sector. */
} else if (fl.fl_cmdstat[5] < 5) {/* Can we start yet?*/
fl_intlv_ct = 1; /* Yes; count, */
fl_lk4_id = fl.fl_cmdstat[5] + 1;/* set ID */
} /* to find.*/
/*
* Look for highest ID on track
*/
PUSH3(fl.fl_cmdstat[5], fl_1st_ID, fl_hi_ID);
if (fl.fl_cmdstat[5] != fl_1st_ID) {
if (fl_1st_ID == 0)
fl_1st_ID = fl.fl_cmdstat[5];
if (fl.fl_cmdstat[5] > fl_hi_ID)
fl_hi_ID = fl.fl_cmdstat[5];
goto GetNextID;
}
/*
* Be sure we have the interleave
*/
if (fl_get_intlv) goto GetNextID;
/*
* So now we know the density and sectors/track
*/
fl.fl_dsk_chngd[fl.fl_unit] = 0;
fl.fl_fd[fl.fl_unit].fd_nspt = fl_hi_ID;
/*
* There is a problem with the approach used here -
* it assumes that once scan of a track starts, all
* sectors appear in physical order without any misses.
* Unfortunately, this is not always the case, especially
* with 1.44 M 3-1/2" drives.
*
* A workaround which fixes incorrect nspt reading appears
* below.
*/
if (fl.fl_fd[fl.fl_unit].fd_nspt > 15
&& fl.fl_fd[fl.fl_unit].fd_nspt < 18)
fl.fl_fd[fl.fl_unit].fd_nspt = 18;
/*
* We're (supposedly) sitting on track 2. We'll
* look at the last sector ID we've read. If it's 1,
* we need to do double-stepping.
*/
if (fl.fl_2step[fl.fl_unit] = (fl.fl_cmdstat[3] == 1)) {
fl.fl_fd[fl.fl_unit].fd_trks = FL_CYL_2STEP;
fl.fl_incal[fl.fl_unit] = 1;
} else /* Most 1.2M drives */
fl.fl_fd[fl.fl_unit].fd_trks = /* have 83 cyls! */
(fl.fl_type[fl.fl_unit] == 2)
? FL_CYL_HDHI : FL_CYL_HDLO;
/*
* We next test for one or two sides:
*/
if (fl.fl_rate[fl.fl_unit] == 0) { /* If diskette is */
fl.fl_fd[fl.fl_unit].fd_nhds = 2; /* high-density it*/
PUSH3(-1,0,4);
goto DiskEstablished; /* will have two */
} /* sides. */
/*
* If the diskette is requested by caller as low density
* we use the specified number of sides------
*/
if (fdata[fkind(bp->b_dev)].fd_nspt < 12) {
fl.fl_fd[fl.fl_unit].fd_nhds =
fdata[fkind(bp->b_dev)].fd_nhds;
PUSH3(-1,0,5);
goto DiskEstablished;
}
/*
* --otherwise we check to see:
*/
flput(CMDRDID); /* Just try to read ANY sector*/
flput(fl.fl_unit | 0x04); /* ID from side two. */
fl.fl_state = SSIDTST;
PUSH3(-1,0,6);
break;
case SSIDTST: /* If we succeeded, we have */
T_HAL(0x40000, printf("SSIDTST "));
/* 2 sides, else we have 1. */
fl.fl_fd[fl.fl_unit].fd_nhds = ((fl.fl_ncmdstat < 7)
|| ((fl.fl_cmdstat[0] & ST0_IC) != ST0_NT) ) ? 1 : 2;
/*
* So now we now know all about the diskette!
*/
DiskEstablished:
fl.fl_fd[fl.fl_unit].fd_size = fl.fl_fd[fl.fl_unit].fd_nhds
* fl.fl_fd[fl.fl_unit].fd_trks
* fl.fl_fd[fl.fl_unit].fd_nspt;
if (fl_disp) {
printf("fl%d: rate=%d, sctrs/trk=%d, hds=%d, cyls=%d,"
" size=%d, intlv=%d, stp=%d\n",
fl.fl_unit,
fl.fl_rate[fl.fl_unit],
fl.fl_fd[fl.fl_unit].fd_nspt,
fl.fl_fd[fl.fl_unit].fd_nhds,
fl.fl_fd[fl.fl_unit].fd_trks,
fl.fl_fd[fl.fl_unit].fd_size,
fl_intlv_ct,
fl.fl_2step[fl.fl_unit]+1);
POP3();
}
/*
* Finally, if the diskette drive has a change line
* we'll force it off by reading a sector on cylinder 2.
* Usually the testing for rate, the seek to cylinder 2
* and the soon-to-follow read or write would clear this,
* but there are some controllers that don't always clear
* it. The machine I've been testing on clears it sometimes
* and not other times. I hope there aren't machines even
* flakier than this!
*/
if ((frates[fl.fl_type[fl.fl_unit]].fl_hi_rate != -1)
&& (inb(FDCCHGL) & DSKCHGD)) {
dods = 1;
/* See note at def of FL_DSK_CH_PROB above */
if (FL_DSK_CH_PROB) {
if (jopen) {
jopen--;
} else
dods = 0;
}
if (dods) {
fl.fl_fcyl = fl.fl_incal[fl.fl_unit];
fl.fl_head = 0;
fl.fl_fsec = 1;
#ifdef _I386
fl.fl_addr = MAPIO(allocp.sr_segp->s_vmem,
((int)scratch_buffer - (int)allocp.sr_base));
#else
fl.fl_addr = vtop(scratch_buffer, sds);
#endif
fl_clrng_cd = 1;
goto Sought;
}
}
RateKnown:
/*
* Set data rate if changed.
*/
if (fl.fl_rate_set != (i = fl.fl_rate[fl.fl_unit]))
outb(FDCRATE, fl.fl_rate_set = i);
/*
* Next we must convert the ordinal block number to
* cylinder/head/sector form.
*/
fl.fl_fsec = (fl.fl_secn % fl.fl_fd[fl.fl_unit].fd_nspt) + 1;
/*
* Seek cylinder by cylinder (XENIX/DOS compatible).
*/
if (fl.fl_hbyh) {
fl.fl_head = fl.fl_secn / fl.fl_fd[fl.fl_unit].fd_nspt;
fl.fl_fcyl = fl.fl_head / fl.fl_fd[fl.fl_unit].fd_nhds;
fl.fl_head = fl.fl_head % fl.fl_fd[fl.fl_unit].fd_nhds;
}
/*
* Seek surface by surface.
*/
else {
fl.fl_fcyl = fl.fl_secn / fl.fl_fd[fl.fl_unit].fd_nspt;
fl.fl_head = fl.fl_fcyl / fl.fl_fd[fl.fl_unit].fd_trks;
fl.fl_fcyl = fl.fl_fcyl % fl.fl_fd[fl.fl_unit].fd_trks;
}
/* Don't seek unless we have to. */
if (fl.fl_fcyl == fl.fl_incal[fl.fl_unit])
goto Sought; /* Past tense of seek. */
fl.fl_incal[fl.fl_unit] = fl.fl_fcyl; /* Save new cylinder. */
flput(CMDSEEK);
flput((fl.fl_head<<2) | fl.fl_unit);
/* If disk is around 40 tracks*/
/* and drive is not 40 track- */
if ((fl.fl_fd[fl.fl_unit].fd_trks < 45)
&& (fl.fl_type[fl.fl_unit] != 1))
flput(fl.fl_fcyl << 1); /* -use double step. */
else
flput(fl.fl_fcyl); /* Single step. */
fl.fl_state = SHDLY;
break;
case SHDLY:
T_HAL(0x40000, printf("SHDLY "));
/*
* Delay for minimum 15 milliseconds after seek before w/fmt.
* 2 clock ticks would give 10-20 millisecond (100 Hz clock).
* 3 clock ticks gives 20-30 millisecond (100 Hz clock).
*/
if (bp->b_req != BREAD) {
timeout(&fltim, 3, fldelay, SRDWR);
fl.fl_state = SDELAY;
break;
}
/* no break */
case SRDWR:
T_HAL(0x40000, printf("SRDWR "));
Sought:
/*
* Disable watchdog timer while waiting to lock DMA controller.
*/
fl.fl_time[fl.fl_unit] = -1;
/*
* Next state will be DMA locked state.
*/
fl.fl_state = SLOCK;
/*
* If DMA controller locked by someone else, exit for now.
*/
if (dmalock(&fldmalck, flfsm, 0) != 0)
return;
case SLOCK:
T_HAL(0x40000, printf("SLOCK "));
/*
* Reset watchdog timer to restart timeout sequence.
*/
fl.fl_time[fl.fl_unit] = 0;
flcmd = CMDRDAT;
fl.fl_wflag = 0;
if (fl_clrng_cd == 0)
if (bp->b_req == BWRITE) {
fl.fl_wflag = 1;
flcmd = CMDWDAT;
}
else if (bp->b_req == BFLFMT) {
fl.fl_wflag = 1;
flcmd = CMDFMT;
#ifdef _I386
if(!dmaon(2, P2P(fl.fl_addr),bp->b_count,
fl.fl_wflag))
#else
if(dmaon(2, fl.fl_addr, bp->b_count,
fl.fl_wflag) == 0)
#endif
goto straddle;
else
goto command;
}
#ifdef _I386
if (dmaon(2, P2P(fl.fl_addr), 512, fl.fl_wflag) == 0)
#else
if (dmaon(2, fl.fl_addr, 512, fl.fl_wflag) == 0)
#endif
{
straddle:
devmsg(bp->b_dev, "fd: DMA page straddle at %x:%x",
fl.fl_addr);
dmaunlock(&fldmalck);
bp->b_flag |= BFERR;
fldone(bp);
goto again;
}
command:
dmago(2);
flput(flcmd);
flput((fl.fl_head<<2) | fl.fl_unit);
if (bp->b_req == BFLFMT) {
flput(fl.fl_fd[fl.fl_unit].fd_N); /* N */
flput(fl.fl_fd[fl.fl_unit].fd_nspt); /* SC */
flput(fl.fl_fd[fl.fl_unit].fd_FGPL); /* GPL */
flput(0xF6); /* D */
}
else {
flput(fl.fl_fcyl);
flput(fl.fl_head);
flput(fl.fl_fsec);
flput(fl.fl_fd[fl.fl_unit].fd_N); /* N */
flput(fl.fl_fd[fl.fl_unit].fd_nspt); /* EOT */
flput(fl.fl_fd[fl.fl_unit].fd_GPL[fl.fl_rate_set]);
/* GPL */
flput(0xFF); /* DTL */
}
fl.fl_state = SENDIO;
break;
case SENDIO:
T_HAL(0x40000, printf("SENDIO "));
fl.fl_time[fl.fl_unit] = 0;
dmaoff(2);
dmaunlock(&fldmalck);
if (fl_clrng_cd) {
fl.fl_state = SIDLE;
wakeup(&fl.fl_state);
goto again;
}
/*
* We now check for errors. If the error is a data
* CRC error, we KNOW we're on the correct track, and
* we just retry the read once before recalibrating.
* We recalibrate for all other errors.
*/
if ((fl.fl_cmdstat[0] & ST0_IC) != ST0_NT) {
if (++fl.fl_nerr < 5) {
if (fl.fl_cmdstat[2] & ST2_DD) {
if (fl.fl_nerr & 1)
goto SetSEEKState;
else
goto Ask4Recal;
} else {
Ask4Recal:
fl.fl_incal[fl.fl_unit] = -1;
SetSEEKState:
fl.fl_state = SSEEK;
}
} else {
flstatus(); /* Total failure; */
bp->b_flag |= BFERR; /* we give up. */
fldone(bp);
}
}
else if (--fl.fl_nsec == 0) {
bp->b_resid = 0;
fldone(bp);
}
else {
++fl.fl_secn;
fl.fl_addr += 512; /* 512 == fl.fl_fd.fd_nbps */
fl.fl_state = SSEEK;
}
/*
* Delay for minimum 1.5 msecs after writing before seek.
*/
if (fl.fl_wflag) {
timeout(&fltim, 2, fldelay, fl.fl_state);
fl.fl_state = SDELAY;
break;
}
goto again;
case SDELAY:
T_HAL(0x40000, printf("SDELAY "));
/*
* Ignore interrupts until timeout occurs.
*/
break;
default:
panic("fds");
}
}
/*
* Delay before initiating next operation.
* This allows the floppy motor to turn on,
* the head to settle before writing,
* the erase head to turn off after writing, etc.
*/
static
fldelay(state)
int state;
{
int s;
s = sphi();
if (fl.fl_state == SDELAY) {
fl.fl_state = state;
flfsm();
}
spl(s);
}
/*
* The flrate function returns the data rate for the flopen and flfsm routines.
*/
static int
flrate(dev)
register dev_t dev;
{
register int unit = funit(dev);
register int rate = frates[fl.fl_type[unit]].fl_hi_rate;
if ((rate == -1) || (fdata[fkind(dev)].fd_nspt < 15))
rate = frates[fl.fl_type[unit]].fl_lo_rate;
return(rate);
}
/*
* This routine is called by the
* clock handler every second. If the drive
* has been idle for a long time it turns off
* the motor and shuts off the timeouts.
*/
static
fltimeout()
{
register int unit;
register int mask;
register int s;
s = sphi();
/*
* Scan all drives, looking for motor timeouts.
*/
for (unit=0, mask=0x10; unit < MAXDRVS; unit++, mask <<= 1) {
/*
* Ignore drives which aren't spinning.
*/
if ((fl.fl_mstatus & mask) == 0)
continue;
/*
* If timer is disabled (i.e. we are waiting for the DMA
* controller), go on to the next drive.
*/
if (fl.fl_time[unit] < 0)
continue;
/*
* Leave recently accessed (in last 4 seconds) drives spinning.
*/
if (++fl.fl_time[unit] < MTIMER)
continue;
/*
* Timeout drives which have been inactive for 5 seconds.
*/
fl.fl_mstatus &= ~mask;
if (unit == fl.fl_selected_unit)
fl.fl_selected_unit = -1;
/*
* Not selected drive, or selected drive is idle.
*/
if ((unit != fl.fl_unit) || (fl.fl_state == SIDLE))
continue;
/*
* Active drive did not complete operation within 5 seconds.
* Attempt recovery.
*/
flrecov();
/*
* Initiate next block request.
*/
if (fl.fl_state == SIDLE)
flfsm();
}
/*
* Physically turn off drives which timed out.
*/
outb(FDCDOR, DORNMR | DORIEN | fl.fl_mstatus | fl.fl_unit);
/*
* Stop checking once all drives have been stopped.
*/
if (fl.fl_mstatus == 0)
drvl[FL_MAJOR].d_time = 0;
spl(s);
}
/*
* Remove all pending requests for a device from the queue
* (used after errors).
*/
static
clrQ(dev)
register int dev;
{
register BUF *bp, *bp2;
int s;
s = sphi();
while ((bp = fl.fl_actf) && (bp->b_dev == dev)) {
bp->b_flag |= BFERR; /* Strip BUFs from front */
fl.fl_actf = bp->b_actf; /* of queue. */
bdone(bp);
}
while (bp) {
fl.fl_actl = bp;
if ((bp2 = bp->b_actf) && (bp2->b_dev == dev)) {
bp2->b_flag |= BFERR; /* Strip BUFs from rest */
bp->b_actf = bp2->b_actf; /* rest of queue. */
bdone(bp2);
} else
bp = bp2;
}
fl.fl_state = SIDLE;
wakeup(&fl.fl_state);
spl(s);
}
/*
* The recovery routine resets and reprograms the floppy controller,
* and discards any queued requests on the current drive.
* This is required if the floppy door is open, or diskette is missing.
*/
static
flrecov()
{
register int x;
if (FL_DSK_CH_PROB)
jopen = 2;
/*
* Disable DMA transfer.
* Reset floppy controller.
*/
dmaoff(2);
/*
* Unlock the controller if locked by us.
*/
outb(FDCDOR, 0);
fl.fl_state = SIDLE;
wakeup(&fl.fl_state);
dmaunlock(&fldmalck); /* Ensures 14 clock cycles */
outb(FDCDOR, DORNMR | DORIEN);
fl.fl_mstatus = 0; /* No motors on */
fl.fl_selected_unit = -1; /* No unit selected */
/*
* Program floppy controller.
*/
flspecify(); /* Forces wait */
/*
* Program transfer bps.
*/
outb(FDCRATE, fl.fl_rate_set);
/*
* Drives are no longer in calibration.
*/
for (x = 0; x < MAXDRVS; x++)
fl.fl_incal[1] = -1;
/*
* Abort all block requests on current drive after 1st recov attempt.
*/
if (fl.fl_actf) {
printf("fd%d: <Door Open>\n", fl.fl_unit); /* Message */
clrQ(fl.fl_actf->b_dev); /* Dump pending reqs. */
fl.fl_dsk_chngd[fl.fl_unit] = 1; /* Make disk changed. */
}
/*
* Delay before setting controller state to idle.
* This gives time for spurious floppy interrupts to occur.
* NOTE: Can't call flfsm(), since it may call us (future revision).
*/
timeout(&fltim, HZ/4, fldelay, SIDLE);
fl.fl_state = SDELAY;
}
/*
* The interrupt routine gets all
* the status bytes the controller chip
* will give it, then issues a sense interrupt
* status command (which is necessary for a seek
* to complete!) and throws all of the status
* bytes away.
*/
static
flintr()
{
register int s;
s = sphi();
flsense();
if (fl.fl_state != SIDLE)
flfsm();
spl(s);
}
/*
* Fldone() returns current request to operating system.
*/
fldone(bp)
register BUF * bp;
{
fl.fl_actf = bp->b_actf;
fl.fl_state = SIDLE;
bdone(bp);
wakeup(&fl.fl_state);
}
/*
* Send Specify command and data bytes to FDC
*/
static
flspecify()
{
flput(CMDSPEC);
flput((fl_srt << 4) | fl_hut);
flput(fl_hlt << 1);
}
/*
* Flsense() issues a sense interrupt status command
* to restore the controller to a quiescent state.
*/
static
flsense()
{
flcmdstatus(); /* Get command status. */
flintstatus(); /* Get int status, just in case. */
}
/*
* Get status (if any) from last command
*/
static
flcmdstatus()
{
register int b;
register int n = 0; /* # of status bytes read */
register int i = 0; /* Timeout count */
register int s;
s = sphi();
/*
* Read all the status bytes the controller will give us.
*/
for (;;) {
while (((b=inb(FDCMSR)) & MSRRQM) == 0) {
if (--i == 0) {
printf("flintr: timeout\n");
break;
}
}
if ((b & MSRDIO) == 0)
break;
b = inb(FDCDAT);
if (n < sizeof(fl.fl_cmdstat))
fl.fl_cmdstat[n++] = b;
}
fl.fl_ncmdstat = n;
spl(s);
}
/*
* Get Inteerupt status
*/
static
flintstatus()
{
register int b;
register int n = 0; /* # of status bytes read */
register int i = 0; /* Timeout count */
register int s;
s = sphi();
/*
* Issue a sense interrupt command and stash result.
*/
flput(CMDSINT);
n = 0;
for (;;) {
while (((b=inb(FDCMSR)) & MSRRQM) == 0)
if (--i == 0) {
printf("flintsense: timeout\n");
break;
}
if ((b & MSRDIO) == 0)
break;
b = inb(FDCDAT);
if (n < sizeof(fl.fl_intstat))
fl.fl_intstat[n++] = b;
}
fl.fl_nintstat = n;
spl(s);
}
/*
* Get the drive status
*/
static void
fldrvstatus()
{
register int b;
register int n = 0; /* # of status bytes read */
register int i = 0; /* Timeout count */
register int s;
s = sphi();
fldrvselect(); /* Be sure drive is selected */
/*
* Issue a sense drive status command and stash result.
*/
flput(CMDSDRV);
flput(fl.fl_unit);
for (;;) {
spl(s);
if (SELF->p_ssig && nondsig()) { /* signal? */
u.u_error = EINTR;
break;
}
s = sphi();
while (((b=inb(FDCMSR)) & MSRRQM) == 0)
if (--i == 0) {
printf("fldrvsense: timeout\n");
break;
}
if ((b & MSRDIO) == 0)
break;
b = inb(FDCDAT);
if (n < sizeof(fl.fl_drvstat))
fl.fl_drvstat[n++] = b;
}
fl.fl_ndrvstat = n;
spl(s);
}
/*
* fldrvselect() selects the drive and turns its motor on
*/
static
fldrvselect()
{
fl.fl_time[fl.fl_unit] = 0; /* Start motor-on timeout. */
fl.fl_mstatus |= fl.fl_mask;
outb(FDCDOR, DORNMR | DORIEN | fl.fl_mstatus | fl.fl_unit);
fl.fl_selected_unit = fl.fl_unit; /* This unit is running. */
flsense(); /* Just in case --- */
}
/*
* Send a command byte to the
* NEC chip, first waiting until the chip
* says that it is ready. No timeout is
* performed; if the chip dies, we do too!
*/
static
flput(b)
int b;
{
register int i = 0;
while ((inb(FDCMSR) & (MSRRQM | MSRDIO)) != MSRRQM) {
if (--i == 0) {
printf("flput(%x): timeout\n",b);
return;
}
}
outb(FDCDAT, b);
}
/*
* Dissassemble the floppy error status for user reference.
*/
static
flstatus()
{
printf("fd%d: head=%u cyl=%u",
fl.fl_cmdstat[0] & 3,
fl.fl_head, fl.fl_fcyl);
/*
* Report on ST0 bits.
*/
if (fl.fl_ncmdstat >= 1) {
if (fl.fl_cmdstat[0] & ST0_NR)
printf(" <Not Ready>");
if (fl.fl_cmdstat[0] & ST0_EC)
printf(" <Equipment Check>");
}
/*
* Report on ST1 bits.
*/
if (fl.fl_ncmdstat >= 2) {
if (fl.fl_cmdstat[1] & ST1_MA)
printf(" <Missing Address Mark>");
if (fl.fl_cmdstat[1] & ST1_NW)
printf(" <Write Protected>");
if (fl.fl_cmdstat[1] & ST1_ND)
printf(" <No Data>");
if (fl.fl_cmdstat[1] & ST1_OR)
printf(" <Overrun>");
if (fl.fl_cmdstat[1] & ST1_DE)
printf(" <Data Error>");
if (fl.fl_cmdstat[1] & ST1_EN)
printf(" <End of Cyl>");
}
/*
* Report on ST2 bits.
*/
if (fl.fl_ncmdstat >= 3) {
if (fl.fl_cmdstat[2] & ST2_MD)
printf(" <Missing Data Address Mark>");
if (fl.fl_cmdstat[2] & ST2_BC)
printf(" <Bad Cylinder>");
if (fl.fl_cmdstat[2] & ST2_WC)
printf(" <Wrong Cylinder>");
if (fl.fl_cmdstat[2] & ST2_DD)
printf(" <Bad Data CRC>");
if (fl.fl_cmdstat[2] & ST2_CM)
printf(" <Data Deleted>");
}
printf("\n");
}
/* * * * * End of FL.C * * * * */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.