* Copyright (c) 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Portions Copyright (c) 1993, 1994 by
* jc@irbs.UUCP (John Capo)
* vak@zebub.msk.su (Serge Vakulenko)
* ache@astral.msk.su (Andrew A. Chernov)
* joerg_wunsch@uriah.sax.de (Joerg Wunsch)
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 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
* from: @(#)fd.c 7.4 (Berkeley) 5/25/91
* $Id: fd.c,v 1.7 1994/05/22 09:39:34 j Exp $
#include <machine/ioctl_fd.h>
#include <sys/disklabel.h>
#include "i386/isa/isa.h"
#include "i386/isa/isa_device.h"
#include "i386/isa/fdreg.h"
#include "i386/isa/fdc.h"
#include "i386/isa/rtc.h"
/* misuse a flag to identify format operation */
* this biotab field doubles as a field for the physical unit number
#define id_physid id_scsiid
#define NUMDENS (NUMTYPES - 6)
/* These defines (-1) must match index for fd_types */
#define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */
#define NO_TYPE 0 /* must match NO_TYPE in ft.c */
struct fd_type fd_types
[NUMTYPES
] =
{ 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS
,2,0x0C,2 }, /* 1.72M in HD 3.5in */
{ 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS
,2,0x6C,1 }, /* 1.48M in HD 3.5in */
{ 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS
,2,0x6C,1 }, /* 1.44M in HD 3.5in */
{ 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS
,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */
{ 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS
,2,0x2E,1 }, /* 820K in HD 3.5in */
{ 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS
,2,0x2E,1 }, /* 800K in HD 3.5in */
{ 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS
,2,0x50,1 }, /* 720K in HD 3.5in */
{ 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS
,2,0x50,1 }, /* 360K in DD 5.25in */
{ 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS
,2,0x02,2 }, /* 1.48M in HD 5.25in */
{ 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS
,2,0x02,2 }, /* 1.44M in HD 5.25in */
{ 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS
,2,0x2E,1 }, /* 820K in HD 5.25in */
{ 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS
,2,0x2E,1 }, /* 800K in HD 5.25in */
{ 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS
,2,0x50,1 }, /* 720K in HD 5.25in */
{ 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS
,2,0x50,1 }, /* 360K in HD 5.25in */
#define DRVS_PER_CTLR 2 /* 2 floppies */
/***********************************************************************\
* Per controller structure. *
\***********************************************************************/
struct fdc_data fdc_data
[NFDC
];
/***********************************************************************\
* N per controller (DRVS_PER_CTLR) *
\***********************************************************************/
struct fdc_data
*fdc
; /* pointer to controller structure */
int fdsu
; /* this units number on this controller */
int type
; /* Drive type (FD_1440...) */
struct fd_type
*ft
; /* pointer to the type descriptor */
#define FD_OPEN 0x01 /* it's open */
#define FD_ACTIVE 0x02 /* it's active */
#define FD_MOTOR 0x04 /* motor should be on */
#define FD_MOTOR_WAIT 0x08 /* motor coming up */
int track
; /* where we think the head is */
int options
; /* user configurable options, see ioctl_fd.h */
/***********************************************************************\
* Throughout this file the following conventions will be used: *
* fd is a pointer to the fd_data struct for the drive in question *
* fdc is a pointer to the fdc_data struct for the controller *
* fdu is the floppy drive unit number *
* fdcu is the floppy controller unit number *
* fdsu is the floppy drive unit number on that controller. (sub-unit) *
\***********************************************************************/
int ftintr(/* ftu_t */ int ftu
);
int ftclose(/* dev_t */ int, int);
void ftstrategy(struct buf
*);
int ftioctl(/* dev_t */ int, int, caddr_t
, int, struct proc
*);
int ftdump(/* dev_t */ int);
int ftsize(/* dev_t */ int);
int ftattach(struct isa_device
*, struct isa_device
*);
/* autoconfig functions */
static int fdprobe(struct isa_device
*);
static int fdattach(struct isa_device
*);
int fdsize (/* dev_t */ int);
int Fdopen(/* dev_t */int, int);
int fdclose(/* dev_t */int, int);
void fdstrategy(struct buf
*);
int fdioctl(/* dev_t */ int, int, caddr_t
, int, struct proc
*);
/* needed for ft driver, thus exported */
int out_fdc(fdcu_t
, int);
static void set_motor(fdcu_t
, int, int);
static void fd_turnoff(caddr_t arg1
, int arg2
);
static void fd_motor_on(caddr_t arg1
, int arg2
);
static void fd_turnon(fdu_t
);
static void fdc_reset(fdc_p
);
static void fdstart(fdcu_t
);
static void fd_timeout(caddr_t
, int);
static void fd_pseudointr(caddr_t
, int);
static int fdstate(fdcu_t
, fdc_p
);
static int retrier(fdcu_t
);
static int fdformat(/* dev_t */ int, struct fd_formb
*, struct proc
*);
/* CAUTION: fd_debug causes huge amounts of logging output */
#define TRACE0(arg) if(fd_debug) printf(arg)
#define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2)
#define TRACE1(arg1, arg2)
/****************************************************************************/
/* autoconfiguration stuff */
/****************************************************************************/
struct isa_driver fdcdriver
= {
fdprobe
, fdattach
, "fdc",
* probe for existance of controller
fdcu_t fdcu
= dev
->id_unit
;
if(fdc_data
[fdcu
].flags
& FDC_ATTACHED
)
printf("fdc: same unit (%d) used multiple times\n", fdcu
);
fdc_data
[fdcu
].baseport
= dev
->id_iobase
;
/* First - lets reset the floppy controller */
outb(dev
->id_iobase
+FDOUT
, 0);
outb(dev
->id_iobase
+FDOUT
, FDO_FRST
);
/* see if it can handle a command */
if (out_fdc(fdcu
, NE7CMD_SPECIFY
) < 0)
out_fdc(fdcu
, NE7_SPEC_1(3, 240));
out_fdc(fdcu
, NE7_SPEC_2(2, 0));
* wire controller into system, look for floppy units
fdcu_t fdcu
= dev
->id_unit
;
fdc_p fdc
= fdc_data
+ fdcu
;
fdc
->flags
|= FDC_ATTACHED
;
fdc
->dmachan
= dev
->id_drq
;
/* reset controller, turn motor off, clear fdout mirror reg */
outb(fdc
->baseport
+ FDOUT
, ((fdc
->fdout
= 0)));
/* check for each floppy drive */
for (fdup
= isa_biotab_fdc
; fdup
->id_driver
!= 0; fdup
++) {
if (fdup
->id_iobase
!= dev
->id_iobase
)
/* look up what bios thinks we have */
case 0: fdt
= (rtcin(RTC_FDISKETTE
) & 0xf0);
case 1: fdt
= ((rtcin(RTC_FDISKETTE
) << 4) & 0xf0);
default: fdt
= RTCFDT_NONE
;
|| (fdsu
>= DRVS_PER_CTLR
)) {
/* If BIOS says no floppy, or > 2nd device */
/* Probe for and attach a floppy tape. */
if (fdsu
< DRVS_PER_CTLR
)
set_motor(fdcu
, fdsu
, TURNON
);
spinwait(1000); /* 1 sec */
out_fdc(fdcu
, NE7CMD_SEEK
); /* seek some steps... */
spinwait(300); /* ...wait a moment... */
out_fdc(fdcu
, NE7CMD_SENSEI
); /* make controller happy */
out_fdc(fdcu
, NE7CMD_RECAL
); /* ...and go back to 0 */
spinwait(1000); /* a second be enough for full stroke seek */
/* anything responding */
out_fdc(fdcu
, NE7CMD_SENSEI
);
set_motor(fdcu
, fdsu
, TURNOFF
);
if (st0
& NE7_ST0_EC
) /* no track 0 -> no drive present */
printf(" [%d: fd%d: ", fdsu
, fdu
);
/****************************************************************************/
/* motor control stuff */
/* remember to not deselect the drive we're working on */
/****************************************************************************/
set_motor(fdcu
, fdsu
, turnon
)
int fdout
= fdc_data
[fdcu
].fdout
;
fdout
|= (FDO_MOEN0
<< fdsu
) + fdsu
;
fdout
&= ~(FDO_MOEN0
<< fdsu
);
&& (fdout
& (FDO_MOEN0
+FDO_MOEN1
+FDO_MOEN2
+FDO_MOEN3
)) == 0)
/* gonna turn off the last drive, put FDC to bed */
fdout
&= ~ (FDO_FRST
|FDO_FDMAEN
);
/* make sure controller is selected and specified */
if((fdout
& (FDO_FRST
|FDO_FDMAEN
)) == 0)
fdout
|= (FDO_FRST
|FDO_FDMAEN
);
outb(fdc_data
[fdcu
].baseport
+FDOUT
, fdout
);
fdc_data
[fdcu
].fdout
= fdout
;
TRACE1("[0x%x->FDOUT]", fdout
);
out_fdc(fdcu
, NE7CMD_SPECIFY
);
out_fdc(fdcu
, NE7_SPEC_1(3, 240));
out_fdc(fdcu
, NE7_SPEC_2(2, 0));
fd_turnoff(caddr_t arg1
, int arg2
)
set_motor(fd
->fdc
->fdcu
, fd
->fdsu
, TURNOFF
);
fd_motor_on(caddr_t arg1
, int arg2
)
fd
->flags
&= ~FD_MOTOR_WAIT
;
if((fd
->fdc
->fd
== fd
) && (fd
->fdc
->state
== MOTORWAIT
))
if(!(fd
->flags
& FD_MOTOR
))
fd
->flags
|= (FD_MOTOR
+ FD_MOTOR_WAIT
);
set_motor(fd
->fdc
->fdcu
, fd
->fdsu
, TURNON
);
timeout(fd_motor_on
, (caddr_t
)fdu
, hz
); /* in 1 sec its ok */
/* Try a reset, keep motor on */
outb(fdc
->baseport
+ FDOUT
, fdc
->fdout
& ~(FDO_FRST
|FDO_FDMAEN
));
TRACE1("[0x%x->FDOUT]", fdc
->fdout
& ~(FDO_FRST
|FDO_FDMAEN
));
/* enable FDC, but defer interrupts a moment */
outb(fdc
->baseport
+ FDOUT
, fdc
->fdout
& ~FDO_FDMAEN
);
TRACE1("[0x%x->FDOUT]", fdc
->fdout
& ~FDO_FDMAEN
);
outb(fdc
->baseport
+ FDOUT
, fdc
->fdout
);
TRACE1("[0x%x->FDOUT]", fdc
->fdout
);
out_fdc(fdcu
, NE7CMD_SPECIFY
);
out_fdc(fdcu
, NE7_SPEC_1(3, 240));
out_fdc(fdcu
, NE7_SPEC_2(2, 0));
/****************************************************************************/
/****************************************************************************/
int baseport
= fdc_data
[fdcu
].baseport
;
while ((i
= inb(baseport
+FDSTS
) & (NE7_DIO
|NE7_RQM
))
!= (NE7_DIO
|NE7_RQM
) && j
-- > 0)
if (i
== NE7_RQM
) return -1;
i
= inb(baseport
+FDDATA
);
TRACE1("[FDDATA->0x%x]", (unsigned char)i
);
return inb(baseport
+FDDATA
);
int baseport
= fdc_data
[fdcu
].baseport
;
/* Check that the direction bit is set */
while ((inb(baseport
+FDSTS
) & NE7_DIO
) && i
-- > 0);
if (i
<= 0) return (-1); /* Floppy timed out */
/* Check that the floppy controller is ready for a command */
while ((inb(baseport
+FDSTS
) & NE7_RQM
) == 0 && i
-- > 0);
if (i
<= 0) return (-1); /* Floppy timed out */
/* Send the command and return */
outb(baseport
+FDDATA
, x
);
TRACE1("[0x%x->FDDATA]", x
);
/****************************************************************************/
/****************************************************************************/
fdu_t fdu
= FDUNIT(minor(dev
));
int type
= FDTYPE(minor(dev
));
/* check for a tape open */
return(ftopen(dev
, flags
));
if ((fdc
== NULL
) || (fd_data
[fdu
].type
== NO_TYPE
))
type
= fd_data
[fdu
].type
;
if (type
!= fd_data
[fdu
].type
) {
switch (fd_data
[fdu
].type
) {
fd_data
[fdu
].ft
= fd_types
+ type
- 1;
fd_data
[fdu
].flags
|= FD_OPEN
;
fdu_t fdu
= FDUNIT(minor(dev
));
int type
= FDTYPE(minor(dev
));
return ftclose(dev
, flags
);
fd_data
[fdu
].flags
&= ~FD_OPEN
;
fd_data
[fdu
].options
&= ~FDOPT_NORETRY
;
/****************************************************************************/
/****************************************************************************/
fdstrategy(struct buf
*bp
)
fdu
= FDUNIT(minor(bp
->b_dev
));
fdblk
= 128 << (fd
->ft
->secsize
);
if (FDTYPE(minor(bp
->b_dev
)) & F_TAPE_TYPE
) {
/* ft tapes do not (yet) support strategy i/o */
/* check for controller already busy with tape */
if (fdc
->flags
& FDC_TAPE_BUSY
) {
if (!(bp
->b_flags
& B_FORMAT
)) {
if ((fdu
>= NFD
) || (bp
->b_blkno
< 0)) {
printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n",
fdu
, bp
->b_blkno
, bp
->b_bcount
);
if ((bp
->b_bcount
% fdblk
) != 0) {
* Set up block calculations.
blknum
= (unsigned long) bp
->b_blkno
* DEV_BSIZE
/fdblk
;
if (blknum
+ (bp
->b_bcount
/ fdblk
) > nblocks
) {
bp
->b_resid
= bp
->b_bcount
;
bp
->b_cylin
= blknum
/ (fd
->ft
->sectrac
* fd
->ft
->heads
);
untimeout(fd_turnoff
, (caddr_t
)fdu
); /* a good idea */
/***************************************************************\
* We have just queued something.. if the controller is not busy *
* then simulate the case where it has just finished a command *
* So that it (the interrupt routine) looks on the queue for more*
* work to do and picks up what we just added. *
* If the controller is already busy, we need do nothing, as it *
* will pick up our work when the present work completes *
\***************************************************************/
if(fdc_data
[fdcu
].state
== DEVIDLE
)
fd_timeout(caddr_t arg1
, int arg2
)
fdcu_t fdcu
= (fdcu_t
)arg1
;
fdu_t fdu
= fdc_data
[fdcu
].fdu
;
int baseport
= fdc_data
[fdcu
].baseport
;
dp
= &fdc_data
[fdcu
].head
;
* Due to IBM's brain-dead design, the FDC has a faked ready
* signal, hardwired to ready == true. Thus, any command
* issued if there's no diskette in the drive will _never_
* complete, and must be aborted by resetting the FDC.
TRACE1("fd%d[fd_timeout()]", fdu
);
/* See if the controller is still busy (patiently awaiting data) */
if(((inb(baseport
+ FDSTS
)) & (NE7_CB
|NE7_RQM
)) == NE7_CB
)
TRACE1("[FDSTS->0x%x]", inb(baseport
+ FDSTS
));
/* yup, it is; kill it now */
fdc_reset(&fdc_data
[fdcu
]);
printf("fd%d: Operation timeout\n", fdu
);
fdc_data
[fdcu
].status
[0] = NE7_ST0_IC_RC
;
fdc_data
[fdcu
].state
= IOTIMEDOUT
;
if( fdc_data
[fdcu
].retry
< 6)
fdc_data
[fdcu
].retry
= 6;
fdc_data
[fdcu
].fd
= (fd_p
) 0;
fdc_data
[fdcu
].state
= DEVIDLE
;
/* just ensure it has the right spl */
fd_pseudointr(caddr_t arg1
, int arg2
)
fdcu_t fdcu
= (fdcu_t
)arg1
;
/***********************************************************************\
* keep calling the state machine until it returns a 0 *
* ALWAYS called at SPLBIO *
\***********************************************************************/
fdc_p fdc
= fdc_data
+ fdcu
;
if (fdc
->flags
& FDC_TAPE_BUSY
)
while(fdstate(fdcu
, fdc
))
/***********************************************************************\
* The controller state machine. *
* if it returns a non zero value, it should be called again immediatly *
\***********************************************************************/
int read
, format
, head
, sec
= 0, i
= 0, sectrac
, st0
, cyl
, st3
;
register struct buf
*dp
, *bp
;
struct fd_formb
*finfo
= NULL
;
/***********************************************\
* nothing left for this controller to do *
* Force into the IDLE state, *
\***********************************************/
printf("unexpected valid fd pointer (fdu = %d)\n",
TRACE1("[fdc%d IDLE]", fdcu
);
fdu
= FDUNIT(minor(bp
->b_dev
));
fdblk
= 128 << fd
->ft
->secsize
;
if (fdc
->fd
&& (fd
!= fdc
->fd
))
printf("confused fd pointers\n");
read
= bp
->b_flags
& B_READ
;
format
= bp
->b_flags
& B_FORMAT
;
finfo
= (struct fd_formb
*)bp
->b_un
.b_addr
;
TRACE1("[%s]", fdstates
[fdc
->state
]);
TRACE1("(0x%x)", fd
->flags
);
untimeout(fd_turnoff
, (caddr_t
)fdu
);
timeout(fd_turnoff
, (caddr_t
)fdu
, 4 * hz
);
case FINDWORK
: /* we have found new work */
outb(fdc
->baseport
+FDCTL
, fd
->ft
->trans
);
TRACE1("[0x%x->FDCTL]", fd
->ft
->trans
);
/*******************************************************\
* If the next drive has a motor startup pending, then *
* it will start up in it's own good time *
\*******************************************************/
if(fd
->flags
& FD_MOTOR_WAIT
)
return(0); /* come back later */
/*******************************************************\
* Maybe if it's not starting, it SHOULD be starting *
\*******************************************************/
if (!(fd
->flags
& FD_MOTOR
))
else /* at least make sure we are selected */
set_motor(fdcu
, fd
->fdsu
, TURNON
);
if (bp
->b_cylin
== fd
->track
)
fdc
->state
= SEEKCOMPLETE
;
out_fdc(fdcu
, NE7CMD_SEEK
); /* Seek function */
out_fdc(fdcu
, fd
->fdsu
); /* Drive number */
out_fdc(fdcu
, bp
->b_cylin
* fd
->ft
->steptrac
);
return(0); /* will return later */
/* allow heads to settle */
timeout(fd_pseudointr
, (caddr_t
)fdcu
, hz
/ 32);
fdc
->state
= SEEKCOMPLETE
;
return(0); /* will return later */
case SEEKCOMPLETE
: /* SEEK DONE, START DMA */
/* Make sure seek really happened*/
int descyl
= bp
->b_cylin
* fd
->ft
->steptrac
;
out_fdc(fdcu
, NE7CMD_SENSEI
);
* if this was a "ready changed" interrupt,
* fetch status again (can happen after
* enabling controller from reset state)
} while ((st0
& NE7_ST0_IC
) == NE7_ST0_IC_RC
);
* seek to cyl 0 requested; make sure we are
out_fdc(fdcu
, NE7CMD_SENSED
);
if ((st3
& NE7_ST3_T0
) == 0) {
"fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n",
"fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
fdu
, descyl
, cyl
, st0
, NE7_ST0BITS
);
fd
->skip
= (char *)&(finfo
->fd_formb_cylno(0))
isa_dmastart(bp
->b_flags
, bp
->b_un
.b_addr
+fd
->skip
,
format
? bp
->b_bcount
: fdblk
, fdc
->dmachan
);
blknum
= (unsigned long)bp
->b_blkno
*DEV_BSIZE
/fdblk
sectrac
= fd
->ft
->sectrac
;
sec
= blknum
% (sectrac
* fd
->ft
->heads
);
fd
->hddrv
= ((head
&1)<<2)+fdu
;
/* make sure the drive is writable */
out_fdc(fdcu
, NE7CMD_SENSED
);
* in order to force the current operation
* to fail, we will have to fake an FDC
* error - all error handling is done
fdc
->status
[0] = NE7_ST0_IC_AT
;
fdc
->status
[1] = NE7_ST1_NW
;
fdc
->status
[3] = fd
->track
;
fdc
->retry
= 8; /* break out immediately */
fdc
->state
= IOTIMEDOUT
; /* not really... */
out_fdc(fdcu
, NE7CMD_FORMAT
);
out_fdc(fdcu
, head
<< 2 | fdu
);
out_fdc(fdcu
, finfo
->fd_formb_secshift
);
out_fdc(fdcu
, finfo
->fd_formb_nsecs
);
out_fdc(fdcu
, finfo
->fd_formb_gaplen
);
out_fdc(fdcu
, finfo
->fd_formb_fillbyte
);
out_fdc(fdcu
, NE7CMD_READ
); /* READ */
out_fdc(fdcu
, NE7CMD_WRITE
); /* WRITE */
out_fdc(fdcu
, head
<< 2 | fdu
); /* head & unit */
out_fdc(fdcu
, fd
->track
); /* track */
out_fdc(fdcu
, sec
); /* sector XXX +1? */
out_fdc(fdcu
, fd
->ft
->secsize
); /* sector size */
out_fdc(fdcu
, sectrac
); /* sectors/track */
out_fdc(fdcu
, fd
->ft
->gap
); /* gap size */
out_fdc(fdcu
, fd
->ft
->datalen
); /* data length */
timeout(fd_timeout
, (caddr_t
)fdcu
, hz
);
return(0); /* will return later */
case IOCOMPLETE
: /* IO DONE, post-analyze */
untimeout(fd_timeout
, (caddr_t
)fdcu
);
fdc
->status
[i
] = in_fdc(fdcu
);
isa_dmadone(bp
->b_flags
, bp
->b_un
.b_addr
+fd
->skip
,
format
? bp
->b_bcount
: fdblk
, fdc
->dmachan
);
if (fdc
->status
[0] & NE7_ST0_IC
)
if ((fdc
->status
[0] & NE7_ST0_IC
) == NE7_ST0_IC_AT
&& fdc
->status
[1] & NE7_ST1_OR
) {
* DMA overrun. Someone hogged the bus
* and didn't release it in time for the
* Just restart it, don't increment retry
fdc
->state
= SEEKCOMPLETE
;
else if((fdc
->status
[0] & NE7_ST0_IC
) == NE7_ST0_IC_IV
fdc
->retry
= 6; /* force a reset */
else if((fdc
->status
[0] & NE7_ST0_IC
) == NE7_ST0_IC_AT
&& fdc
->status
[2] & NE7_ST2_WC
fdc
->retry
= 3; /* force recalibrate */
if (!format
&& fd
->skip
< bp
->b_bcount
)
/* set up next transfer */
blknum
= (unsigned long)bp
->b_blkno
*DEV_BSIZE
/fdblk
(blknum
/ (fd
->ft
->sectrac
* fd
->ft
->heads
));
dp
->b_actf
= bp
->av_forw
;
out_fdc(fdcu
, NE7CMD_RECAL
); /* Recalibrate Function */
return(0); /* will return later */
/* allow heads to settle */
timeout(fd_pseudointr
, (caddr_t
)fdcu
, hz
/ 32);
fdc
->state
= RECALCOMPLETE
;
return(0); /* will return later */
out_fdc(fdcu
, NE7CMD_SENSEI
);
* if this was a "ready changed" interrupt,
* fetch status again (can happen after
* enabling controller from reset state)
} while ((st0
& NE7_ST0_IC
) == NE7_ST0_IC_RC
);
if ((st0
& NE7_ST0_IC
) != NE7_ST0_IC_NT
|| cyl
!= 0)
printf("fd%d: recal failed ST0 %b cyl %d\n", fdu
,
if(fdc
->retry
< 3) fdc
->retry
= 3;
/* Seek (probably) necessary */
return(1); /* will return immediatly */
if(fd
->flags
& FD_MOTOR_WAIT
)
return(0); /* time's not up yet */
* since the controller was off, it has lost its
* idea about the current track it were; thus,
* recalibrate the bastard
return(1); /* will return immediatly */
printf("Unexpected FD int->");
out_fdc(fdcu
, NE7CMD_SENSEI
);
printf("ST0 = %x, PCN = %x\n", st0
, cyl
);
out_fdc(fdcu
, NE7CMD_READID
);
fdc
->status
[i
] = in_fdc(fdcu
);
printf("intr status :%lx %lx %lx %lx %lx %lx %lx\n",
printf("FDC timed out\n");
return(1); /* Come back immediatly to new state */
fdc_p fdc
= fdc_data
+ fdcu
;
register struct buf
*dp
, *bp
;
if(fd_data
[FDUNIT(minor(bp
->b_dev
))].options
& FDOPT_NORETRY
)
fdc
->state
= SEEKCOMPLETE
;
dev_t sav_b_dev
= bp
->b_dev
;
bp
->b_dev
= makedev(major(bp
->b_dev
),
(FDUNIT(minor(bp
->b_dev
))<<3)|3);
diskerr(bp
, "fd", "hard error", LOG_PRINTF
,
fdc
->fd
->skip
/ DEV_BSIZE
,
(struct disklabel
*)NULL
);
printf(" (ST0 %b ", fdc
->status
[0], NE7_ST0BITS
);
printf(" ST1 %b ", fdc
->status
[1], NE7_ST1BITS
);
printf(" ST2 %b ", fdc
->status
[2], NE7_ST2BITS
);
printf("cyl %d hd %d sec %d)\n",
fdc
->status
[3], fdc
->status
[4], fdc
->status
[5]);
bp
->b_resid
= bp
->b_bcount
- fdc
->fd
->skip
;
dp
->b_actf
= bp
->av_forw
;
/* XXX abort current command, if any. */
fdu
= FDUNIT(minor(dev
));
fdblk
= 128 << fd
->ft
->secsize
;
/* set up a buffer header for fdstrategy() */
bp
= (struct buf
*)malloc(sizeof(struct buf
), M_TEMP
, M_NOWAIT
);
bzero((void *)bp
, sizeof(struct buf
));
bp
->b_flags
= B_BUSY
| B_PHYS
| B_FORMAT
;
* calculate a fake blkno, so fdstrategy() would initiate a
* seek to the requested cylinder
bp
->b_blkno
= (finfo
->cyl
* (fd
->ft
->sectrac
* fd
->ft
->heads
)
+ finfo
->head
* fd
->ft
->sectrac
) * fdblk
/ DEV_BSIZE
;
bp
->b_bcount
= sizeof(struct fd_idfield_data
) * finfo
->fd_formb_nsecs
;
bp
->b_un
.b_addr
= (caddr_t
)finfo
;
/* ...and wait for it to complete */
while(!(bp
->b_flags
& B_DONE
))
rv
= tsleep((caddr_t
)bp
, PRIBIO
, "fdform", 20 * hz
);
if(bp
->b_flags
& B_ERROR
)
* TODO: Think about allocating buffer off stack.
* Don't pass uncast 0's and NULL's to read/write/setdisklabel().
* Watch out for NetBSD's different *disklabel() interface.
fdioctl(dev
, cmd
, addr
, flag
, p
)
fdu_t fdu
= FDUNIT(minor(dev
));
int type
= FDTYPE(minor(dev
));
/* check for a tape ioctl */
return ftioctl(dev
, cmd
, addr
, flag
, p
);
fdblk
= 128 << fd
->ft
->secsize
;
bzero(buffer
, sizeof (buffer
));
dl
= (struct disklabel
*)buffer
;
fdt
= fd_data
[FDUNIT(minor(dev
))].ft
;
dl
->d_secpercyl
= fdt
->size
/ fdt
->tracks
;
dl
->d_type
= DTYPE_FLOPPY
;
if (readdisklabel(dev
, fdstrategy
, dl
, NULL
, 0, 0) == NULL
)
*(struct disklabel
*)addr
= *dl
;
if ((flag
& FWRITE
) == 0)
if ((flag
& FWRITE
) == 0)
if ((flag
& FWRITE
) == 0)
dl
= (struct disklabel
*)addr
;
setdisklabel ((struct disklabel
*)buffer
, dl
, 0, NULL
)))
error
= writedisklabel(dev
, fdstrategy
,
(struct disklabel
*)buffer
, NULL
);
error
= EBADF
; /* must be opened for writing */
else if(((struct fd_formb
*)addr
)->format_version
!=
error
= EINVAL
; /* wrong version of formatting prog */
error
= fdformat(dev
, (struct fd_formb
*)addr
, p
);
case FD_GTYPE
: /* get drive type */
*(struct fd_type
*)addr
= *fd_data
[FDUNIT(minor(dev
))].ft
;
case FD_STYPE
: /* set drive type */
/* this is considered harmful; only allow for superuser */
if(suser(p
->p_ucred
, &p
->p_acflag
) != 0)
*fd_data
[FDUNIT(minor(dev
))].ft
= *(struct fd_type
*)addr
;
case FD_GOPTS
: /* get drive options */
*(int *)addr
= fd_data
[FDUNIT(minor(dev
))].options
;
case FD_SOPTS
: /* set drive options */
fd_data
[FDUNIT(minor(dev
))].options
= *(int *)addr
;
* Hello emacs, these are the
* c-continued-statement-offset: 8
* c-continued-brace-offset: 0
* c-brace-imaginary-offset: 0
* c++-access-specifier-offset: -8
* c++-empty-arglist-indent: 8