* Copyright (c) 1982, 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
* @(#)rx.c 7.5 (Berkeley) 12/16/90
* RX02 floppy disk device driver
* - clean up the code for multisector transfers using
* a 'transfer in progress' flag
* - Test Deleted Data read/write
* - Test error handling/reporting and 'volume valid' stuff
* Note: If the drive subsystem is
* powered off at boot time, the controller won't interrupt!
#include "../include/pte.h"
#include "../include/cpu.h"
#include "../vax/nexus.h"
/* per-controller data */
int rxc_state
; /* controller state */
#define RXS_READ 1 /* read started */
#define RXS_EMPTY 2 /* empty started */
#define RXS_FILL 3 /* fill started */
#define RXS_WRITE 4 /* write started */
#define RXS_FORMAT 5 /* format started */
#define RXS_RDSTAT 6 /* status read started */
#define RXS_RDERR 7 /* error read started */
#define RXS_IDLE 8 /* device is idle */
u_short rxc_rxcs
; /* extended error status */
int rxc_tocnt
; /* for watchdog routine */
#define RX_MAXTIMEOUT 30 /* # seconds to wait before giving up */
struct buf rrxbuf
[NRX
]; /* buffers for raw I/O */
struct buf erxbuf
[NRX
]; /* buffers for reading error status */
struct buf rxutab
[NRX
]; /* per drive buffers */
int sc_flags
; /* drive status flags */
#define RXF_DIRECT 0x01 /* if set: use direct sector mapping */
#define RXF_TRKONE 0x02 /* if set: start mapping on track 1 */
#define RXF_DBLDEN 0x04 /* use double density */
#define RXF_DEVTYPE 0x07 /* mapping flags */
#define RXF_LOCK 0x10 /* exclusive use */
#define RXF_DDMK 0x20 /* deleted-data mark detected */
#define RXF_USEWDDS 0x40 /* write deleted-data sector */
#define RXF_FORMAT 0x80 /* format in progress */
#define RXF_BAD 0x100 /* drive bad, cannot be used */
int sc_csbits
; /* constant bits for CS register */
int sc_open
; /* count number of opens */
int sc_offset
; /* raw mode kludge to avoid restricting */
/* single sector transfers to start on */
/* DEV_BSIZE boundaries */
* The rest of this structure is used to
* store temporaries while simulating multi
caddr_t sc_uaddr
; /* unibus base address */
long sc_bcnt
; /* total transfer count */
long sc_resid
; /* no. of bytes left to transfer */
short rxxt
[4]; /* error code dump from controller */
/* End of per-drive data */
struct uba_device
*rxdinfo
[NRX
];
struct uba_ctlr
*rxminfo
[NFX
];
int rxprobe(), rxslave(), rxattach(), rxdgo(), rxintr(), rxwatch(), rxphys();
u_short rxstd
[] = { 0177170, 0177150, 0 };
struct uba_driver fxdriver
=
{ rxprobe
, rxslave
, rxattach
, rxdgo
, rxstd
, "rx", rxdinfo
, "fx", rxminfo
};
#define RXUNIT(dev) (minor(dev)>>3)
#define MASKREG(reg) (reg&0xffff)
/* constants related to floppy data capacity */
#define RXSECS 2002 /* # sectors on a floppy */
#define DDSTATE (sc->sc_csbits&RX_DDEN)
#define NBPS (DDSTATE ? 256 : 128) /* # bytes per sector */
#define RXSIZE (DDSTATE ? 512512 : 256256) /* # bytes per disk */
#define SECMASK (DDSTATE ? 0xff : 0x7f) /* shifted-out bits of offset */
#define B_CTRL 0x80000000 /* control (format) request */
#define B_RDSTAT 0x40000000 /* read drive status (open) */
register int br
, cvec
; /* value-result */
struct rxdevice
*rxaddr
= (struct rxdevice
*)reg
;
br
= 0; cvec
= br
; br
= cvec
;
return (sizeof (*rxaddr
));
return (ui
->ui_slave
== 0 || ui
->ui_slave
== 1);
register int unit
= RXUNIT(dev
);
register struct rx_softc
*sc
;
register struct uba_device
*ui
;
if (unit
>= NRX
|| (ui
= rxdinfo
[unit
]) == 0 || ui
->ui_alive
== 0)
if (sc
->sc_open
== 0 && sc
->sc_csbits
== 0) {
struct buf
*bp
= &erxbuf
[unit
];
* lock the device while an open
sc
->sc_flags
= (minor(dev
) & RXF_DEVTYPE
) | RXF_LOCK
;
sc
->sc_csbits
|= ui
->ui_slave
== 0 ? RX_DRV0
: RX_DRV1
;
bp
->b_flags
= B_RDSTAT
| B_BUSY
;
* read device status to determine if
* a floppy is present in the drive and
if (bp
->b_flags
& B_ERROR
) {
sc
->sc_flags
&= ~RXF_LOCK
;
rxc
= &rx_ctlr
[ui
->ui_mi
->um_ctlr
];
timeout(rxwatch
, (caddr_t
)0, hz
); /* start watchdog */
printf("rxopen: csbits=0x%x\n", sc
->sc_csbits
);
sc
->sc_flags
&= ~RXF_LOCK
;
if (sc
->sc_flags
& RXF_LOCK
)
register struct rx_softc
*sc
= &rx_softc
[RXUNIT(dev
)];
printf("rxclose: dev=0x%x, sc_open=%d\n", dev
, sc
->sc_open
);
int s
, unit
= RXUNIT(bp
->b_dev
);
if (ui
== 0 || ui
->ui_alive
== 0)
if (bp
->b_blkno
< 0 || dbtob(bp
->b_blkno
) > RXSIZE
)
if (sc
->sc_flags
& RXF_BAD
) {
printf("rxstrat: bp=0x%x, fl=0x%x, un=%d, bl=%d, cnt=%d\n",
bp
, bp
->b_flags
, unit
, bp
->b_blkno
, bp
->b_bcount
);
bp
->b_cylin
= bp
->b_blkno
; /* don't care to calculate trackno */
if (bp
->b_actf
&& bp
->b_active
== 0)
* Put this unit on the ready queue for the controller
register struct uba_device
*ui
;
struct buf
*dp
= &rxutab
[ui
->ui_unit
];
struct uba_ctlr
*um
= ui
->ui_mi
;
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
* Sector mapping routine.
* Two independent sets of choices are available:
* (a) The first logical sector may either be on track 1 or track 0.
* (b) The sectors on a track may either be taken in 2-for-1 interleaved
* This gives a total of four possible mapping schemes.
* Physical tracks on the RX02 are numbered 0-76. Physical sectors on
* each track are numbered 1-26.
* When interleaving is used, sectors on the first logical track are
* taken in the order 1, 3, 5, ..., 25, 2, 4, 6, ..., 26. A skew of
* six sectors per track is also used (to allow time for the heads to
* move); hence, the sectors on the second logical track are taken in
* the order 7, 9, 11, ..., 25, 1, 3, 5, 8, 10, 12, ..., 26, 2, 4, 6;
* the third logical track starts with sector 13; and so on.
* When the mapping starts with track 1, track 0 is the last logical
* track, and this track is always handled directly (without inter-
* leaving), even when the rest of the disk is interleaved. (This is
* still compatible with DEC RT-11, which does not use track 0 at all.)
rxmap(bp
, psector
, ptrack
)
register int lt
, ls
, ptoff
;
struct rx_softc
*sc
= &rx_softc
[RXUNIT(bp
->b_dev
)];
ls
= (dbtob(bp
->b_blkno
) + (sc
->sc_offset
- sc
->sc_resid
)) / NBPS
;
* The "physical track offset" (ptoff) takes the
* starting physical track (0 or 1) and the desired
* interleaving into account. If lt+ptoff >= 77,
* then interleaving is not performed.
if (sc
->sc_flags
& RXF_DIRECT
)
if (sc
->sc_flags
& RXF_TRKONE
)
ls
= ((ls
<< 1) + (ls
>= 13) + (6*lt
)) % 26;
*ptrack
= (lt
+ ptoff
) % 77;
* Controller start routine.
* Start a new transfer or continue a multisector
* transfer. If this is a new transfer (dp->b_active == 1)
* save the start address of the data buffer and the total
* byte count in the soft control structure. These are
* restored into the buffer structure when the transfer has
* been completed, before calling 'iodone'.
register struct uba_ctlr
*um
;
register struct rxdevice
*rxaddr
;
register struct rx_ctlr
*rxc
;
register struct rx_softc
*sc
;
if ((dp
= um
->um_tab
.b_actf
) == NULL
)
if ((bp
= dp
->b_actf
) == NULL
) {
um
->um_tab
.b_actf
= dp
->b_forw
;
unit
= RXUNIT(bp
->b_dev
);
if (sc
->sc_flags
& RXF_BAD
) {
sc
->sc_resid
= bp
->b_bcount
;
sc
->sc_uaddr
= bp
->b_un
.b_addr
;
sc
->sc_bcnt
= bp
->b_bcount
;
sc
->sc_offset
+= sc
->sc_bcnt
;
rxaddr
= (struct rxdevice
*)um
->um_addr
;
rxc
= &rx_ctlr
[um
->um_ctlr
];
bp
->b_bcount
= sc
->sc_resid
;
if (rxaddr
->rxcs
== 0x800) {
* 'Volume valid'? (check if the
* drive unit has been powered down)
while((rxaddr
->rxcs
&RX_DONE
) == 0)
if (bp
->b_flags
& B_CTRL
) { /* format */
rxc
->rxc_state
= RXS_FORMAT
;
rxaddr
->rxcs
= RX_FORMAT
| sc
->sc_csbits
;
while ((rxaddr
->rxcs
&RX_TREQ
) == 0)
if (bp
->b_flags
& B_RDSTAT
) { /* read drive status */
rxc
->rxc_state
= RXS_RDSTAT
;
rxaddr
->rxcs
= RX_RDSTAT
| sc
->sc_csbits
;
if (bp
->b_flags
& B_READ
) {
rxmap(bp
, §or
, &track
); /* read */
printf("read tr=%d, sc=%d", track
, sector
);
rxc
->rxc_state
= RXS_READ
;
rxaddr
->rxcs
= RX_READ
| sc
->sc_csbits
;
while ((rxaddr
->rxcs
&RX_TREQ
) == 0)
rxaddr
->rxdb
= (u_short
)sector
;
while ((rxaddr
->rxcs
&RX_TREQ
) == 0)
rxaddr
->rxdb
= (u_short
)track
;
rxc
->rxc_state
= RXS_FILL
; /* write */
(void) ubago(rxdinfo
[unit
]);
register struct rxdevice
*rxaddr
= (struct rxdevice
*)um
->um_addr
;
int ubinfo
= um
->um_ubinfo
;
struct buf
*bp
= um
->um_tab
.b_actf
->b_actf
;
struct rx_softc
*sc
= &rx_softc
[RXUNIT(bp
->b_dev
)];
struct rx_ctlr
*rxc
= &rx_ctlr
[um
->um_ctlr
];
rxaddr
->rxcs
= um
->um_cmd
| ((ubinfo
& 0x30000) >> 4) | sc
->sc_csbits
;
if (rxc
->rxc_state
!= RXS_RDERR
) {
while ((rxaddr
->rxcs
&RX_TREQ
) == 0)
rxaddr
->rxdb
= (u_short
) bp
->b_bcount
>> 1;
while ((rxaddr
->rxcs
&RX_TREQ
) == 0)
rxaddr
->rxdb
= (u_short
) ubinfo
;
struct uba_ctlr
*um
= rxminfo
[ctlr
];
register struct rxdevice
*rxaddr
;
register struct buf
*bp
, *dp
;
register struct rx_softc
*sc
;
if (!um
->um_tab
.b_active
)
unit
= RXUNIT(bp
->b_dev
);
rxaddr
= (struct rxdevice
*)um
->um_addr
;
rxc
= &rx_ctlr
[um
->um_ctlr
];
printf("rxint: dev=%x, st=%d, cs=0x%x, db=0x%x\n",
bp
->b_dev
, rxc
->rxc_state
, rxaddr
->rxcs
, rxaddr
->rxdb
);
if ((rxaddr
->rxcs
& RX_ERR
) &&
(rxc
->rxc_state
!= RXS_RDSTAT
) && (rxc
->rxc_state
!= RXS_RDERR
))
switch (rxc
->rxc_state
) {
* Incomplete commands. Perform next step
* and return. Note that b_active is set on
* entrance and, therefore, also on exit.
if (rxaddr
->rxdb
& RXES_DDMARK
)
sc
->sc_flags
|= RXF_DDMK
;
sc
->sc_flags
&= ~RXF_DDMK
;
rxc
->rxc_state
= RXS_EMPTY
;
rxc
->rxc_state
= RXS_WRITE
;
if (sc
->sc_flags
& RXF_USEWDDS
) {
rxaddr
->rxcs
= RX_WDDS
| sc
->sc_csbits
;
sc
->sc_flags
&= ~RXF_USEWDDS
;
rxaddr
->rxcs
= RX_WRITE
| sc
->sc_csbits
;
rxmap(bp
, §or
, &track
);
while ((rxaddr
->rxcs
&RX_TREQ
) == 0)
while ((rxaddr
->rxcs
&RX_TREQ
) == 0)
* Possibly completed command.
if (bp
->b_flags
& B_RDSTAT
) {
if ((rxaddr
->rxdb
&RXES_READY
) == 0) {
sc
->sc_csbits
|= rxaddr
->rxdb
&RXES_DBLDEN
?
if (rxaddr
->rxdb
&RXES_READY
)
rxmap(bp
, §or
, &track
);
printf("rx%d: hard error, trk %d psec %d ",
printf("cs=%b, db=%b, err=", MASKREG(er
->rxcs
),
RXCS_BITS
, MASKREG(er
->rxdb
), RXES_BITS
);
printf("%x, %x, %x, %x\n", MASKREG(er
->rxxt
[0]),
MASKREG(er
->rxxt
[1]), MASKREG(er
->rxxt
[2]),
printf("rx%d: state %d (reset)\n", unit
, rxc
->rxc_state
);
* (a) Give up now if a format (ioctl) was in progress, if a
* density error was detected, or if the drive went offline
* (b) Retry up to nine times if a CRC (data) error was detected,
* then give up if the error persists.
* (c) In all other cases, reinitialize the drive and try the
* operation once more before giving up.
if (rxc
->rxc_state
== RXS_FORMAT
|| (rxaddr
->rxdb
&RXES_DENERR
))
if (rxaddr
->rxdb
& RXES_CRCERR
) {
if (++um
->um_tab
.b_errcnt
>= 10)
um
->um_tab
.b_errcnt
+= 9;
if (um
->um_tab
.b_errcnt
>= 10)
/* no way to get an interrupt for "init done", so just wait */
while ((rxaddr
->rxcs
&RX_DONE
) == 0)
/* if someone opened the drive: give up */
if ((rxaddr
->rxdb
&RXES_READY
) == 0)
* In case we already have UNIBUS resources, give
* them back since we reallocate things in rxstart.
* ALL errors are considered fatal and will abort the
* transfer and purge the i/o request queue
sc
->sc_resid
= 0; /* make sure the transfer is terminated */
rxc
->rxc_state
= RXS_RDSTAT
;
rxaddr
->rxcs
= RX_RDSTAT
| sc
->sc_csbits
;
* A hard error (other than not ready) has occurred.
* Read the extended error status information.
* Before doing this, save the current CS and DB register values,
* because the read error status operation may modify them.
* Insert buffer with request at the head of the queue.
bp
->b_un
.b_addr
= (caddr_t
)er
->rxxt
;
bp
->b_bcount
= sizeof (er
->rxxt
);
bp
->b_flags
&= ~(B_DIRTY
|B_UAREA
|B_PHYS
|B_PAGET
);
rxc
->rxc_state
= RXS_RDERR
;
if ((sc
->sc_resid
-= NBPS
) > 0) {
bp
->b_un
.b_addr
= sc
->sc_uaddr
;
bp
->b_bcount
= sc
->sc_bcnt
;
dp
->b_actf
= bp
->av_forw
;
rxc
->rxc_state
= RXS_IDLE
;
um
->um_tab
.b_actf
= dp
->b_forw
;
printf(".. bp=%x, new=%x\n", bp
, dp
->b_actf
);
* If this unit has more work to do,
register struct uba_device
*ui
;
register struct uba_ctlr
*um
;
register struct rx_softc
*sc
;
if (ui
== 0 || ui
->ui_alive
== 0)
if ((sc
->sc_open
== 0) && (rxutab
[i
].b_active
== 0)) {
rxc
= &rx_ctlr
[um
->um_ctlr
];
if (++rxc
->rxc_tocnt
>= RX_MAXTIMEOUT
) {
if (um
->um_tab
.b_active
) {
printf("rx%d: timeout\n", i
);/* for debugging */
timeout(rxwatch
, (caddr_t
)0, hz
);
register struct uba_ctlr
*um
;
register struct rxdevice
*rxaddr
;
for (ctlr
= 0; ctlr
< NFX
; ctlr
++) {
if ((um
= rxminfo
[ctlr
]) == 0 || um
->um_ubanum
!= uban
||
printf("<%d>", UBAI_BDP(um
->um_ubinfo
));
rx_ctlr
[ctlr
].rxc_state
= RXS_IDLE
;
rxaddr
= (struct rxdevice
*)um
->um_addr
;
while ((rxaddr
->rxcs
&RX_DONE
) == 0)
struct rx_softc
*sc
= &rx_softc
[unit
];
if (uio
->uio_offset
+ uio
->uio_resid
> RXSIZE
)
if (uio
->uio_offset
< 0 || (uio
->uio_offset
& SECMASK
) != 0)
sc
->sc_offset
= uio
->uio_offset
% DEV_BSIZE
;
return (physio(rxstrategy
, &rrxbuf
[unit
], dev
, B_READ
, minphys
, uio
));
struct rx_softc
*sc
= &rx_softc
[unit
];
if (uio
->uio_offset
+ uio
->uio_resid
> RXSIZE
)
if (uio
->uio_offset
< 0 || (uio
->uio_offset
& SECMASK
) != 0)
sc
->sc_offset
= uio
->uio_offset
% DEV_BSIZE
;
return(physio(rxstrategy
, &rrxbuf
[unit
], dev
, B_WRITE
, minphys
, uio
));
* processes four kinds of requests:
* (1) Set density (i.e., format the diskette) according to
* that specified data parameter
* (2) Arrange for the next sector to be written with a deleted-
* (3) Report whether the last sector read had a deleted-data mark
* (4) Report the density of the diskette in the indicated drive
* (since the density it automatically determined by the driver,
* this is the only way to let an application program know the
* Requests relating to deleted-data marks can be handled right here.
* A "set density" (format) request, however, must additionally be
* processed through "rxstart", just like a read or write request.
rxioctl(dev
, cmd
, data
, flag
)
struct rx_softc
*sc
= &rx_softc
[unit
];
sc
->sc_csbits
|= RX_DDEN
;
sc
->sc_csbits
&= ~RX_DDEN
;
sc
->sc_flags
|= RXF_USEWDDS
;
*(int *)data
= sc
->sc_flags
& RXF_DDMK
;
*(int *)data
= sc
->sc_csbits
& RX_DDEN
;
* Initiate a format command.
struct rx_softc
*sc
= &rx_softc
[unit
];
bp
->b_flags
= B_BUSY
| B_CTRL
;
sc
->sc_flags
= RXF_FORMAT
| RXF_LOCK
;
if (bp
->b_flags
& B_ERROR
)
sc
->sc_flags
&= ~RXF_LOCK
;
* A permanent hard error condition has occured,
register struct uba_ctlr
*um
;
register struct buf
*bp
, *dp
;
dp
->b_actf
= bp
->av_forw
;