* Copyright (c) 1982 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
* @(#)ut.c 6.8 (Berkeley) %G%
* System Industries Model 9700 Tape Drive
* emulates a TU45 on the UNIBUS
* check out attention processing
* try reset code and dump code
#include "../machine/pte.h"
struct buf rutbuf
[NUT
]; /* bufs for raw i/o */
struct buf cutbuf
[NUT
]; /* bufs for control operations */
struct buf tjutab
[NTJ
]; /* bufs for slave queue headers */
struct uba_ctlr
*utminfo
[NUT
];
struct uba_device
*tjdinfo
[NTJ
];
int utprobe(), utslave(), utattach(), utdgo(), utintr(), uttimer();
u_short utstd
[] = { 0772440, 0 };
struct uba_driver utdriver
=
{ utprobe
, utslave
, utattach
, utdgo
, utstd
, "tj", tjdinfo
, "ut", utminfo
, 0 };
#define MASKREG(reg) ((reg)&0xffff)
/* bits in minor device */
#define TJUNIT(dev) (minor(dev)&03)
short utdens
[] = { UT_NRZI
, UT_PE
, UT_GCR
, UT_NRZI
};
/* slave to controller mapping table */
#define UTUNIT(dev) (tjtout[TJUNIT(dev)])
#define INF (daddr_t)1000000L /* a block number that wont exist */
char sc_openf
; /* exclusive open */
char sc_lastiow
; /* last I/O operation was a write */
daddr_t sc_blkno
; /* next block to transfer */
daddr_t sc_nxrec
; /* next record on tape */
u_short sc_erreg
; /* image of uter */
u_short sc_dsreg
; /* image of utds */
u_short sc_resid
; /* residual from transfer */
u_short sc_dens
; /* sticky selected density */
daddr_t sc_timo
; /* time until timeout expires */
short sc_tact
; /* timeout is active flag */
struct tty
*sc_ttyp
; /* record user's tty for errors */
* Internal per/slave states found in sc_state
#define SSEEK 1 /* seeking */
#define SIO 2 /* doing sequential I/O */
#define SCOM 3 /* sending a control command */
#define SREW 4 /* doing a rewind op */
#define SERASE 5 /* erase inter-record gap */
#define SERASED 6 /* erased inter-record gap */
* The SI documentation says you must set the RDY bit
* (even though it's read-only) to force an interrupt.
((struct utdevice
*) reg
)->utcs1
= UT_IE
|UT_NOP
|UT_RDY
;
return (sizeof (struct utdevice
));
* A real TU45 would support the slave present bit
* int the drive type register, but this thing doesn't,
* so there's no way to determine if a slave is present or not.
tjtout
[ui
->ui_unit
] = ui
->ui_mi
->um_ctlr
;
* Open the device with exclusive access.
register int tjunit
= TJUNIT(dev
);
register struct uba_device
*ui
;
register struct tj_softc
*sc
;
if (tjunit
>= NTJ
|| (ui
= tjdinfo
[tjunit
]) == 0 || ui
->ui_alive
== 0)
if ((sc
= &tj_softc
[tjunit
])->sc_openf
)
utdens
[(minor(dev
)&(T_1600BPI
|T_6250BPI
))>>3]|
PDP11FMT
|(ui
->ui_slave
&07);
utcommand(dev
, UT_SENSE
, 1);
if (sc
->sc_dsreg
&UTDS_PIP
) {
sleep((caddr_t
)&lbolt
, PZERO
+1);
if ((sc
->sc_dsreg
&UTDS_MOL
) == 0) {
uprintf("tj%d: not online\n", tjunit
);
if ((flag
&FWRITE
) && (sc
->sc_dsreg
&UTDS_WRL
)) {
uprintf("tj%d: no write ring\n", tjunit
);
if ((sc
->sc_dsreg
&UTDS_BOT
) == 0 && (flag
&FWRITE
) &&
uprintf("tj%d: can't change density in mid-tape\n", tjunit
);
sc
->sc_blkno
= (daddr_t
)0;
* For 6250 bpi take exclusive use of the UNIBUS.
ui
->ui_driver
->ud_xclu
= (dens
&(T_1600BPI
|T_6250BPI
)) == T_6250BPI
;
timeout(uttimer
, (caddr_t
)dev
, 5*hz
);
register struct tj_softc
*sc
= &tj_softc
[TJUNIT(dev
)];
if (flag
== FWRITE
|| ((flag
&FWRITE
) && sc
->sc_lastiow
)) {
utcommand(dev
, UT_WEOF
, 1);
utcommand(dev
, UT_WEOF
, 1);
utcommand(dev
, UT_SREV
, 1);
if ((minor(dev
)&T_NOREWIND
) == 0)
utcommand(dev
, UT_REW
, 0);
utcommand(dev
, com
, count
)
bp
= &cutbuf
[UTUNIT(dev
)];
while (bp
->b_flags
&B_BUSY
) {
if(bp
->b_repcnt
== 0 && (bp
->b_flags
&B_DONE
))
sleep((caddr_t
)bp
, PRIBIO
);
bp
->b_flags
= B_BUSY
|B_READ
;
if (bp
->b_flags
&B_WANTED
)
* Queue a tape operation.
int tjunit
= TJUNIT(bp
->b_dev
);
register struct uba_ctlr
*um
;
* Put transfer at end of unit queue
um
= tjdinfo
[tjunit
]->ui_mi
;
if (dp
->b_actf
== NULL
) {
* Transport not active, so...
* put at end of controller queue
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
dp
->b_actl
->av_forw
= bp
;
* If the controller is not busy, set it going.
if (um
->um_tab
.b_state
== 0)
register struct uba_ctlr
*um
;
register struct utdevice
*addr
;
register struct buf
*bp
, *dp
;
register struct tj_softc
*sc
;
* Scan controller queue looking for units with
* transaction queues to dispatch
if ((dp
= um
->um_tab
.b_actf
) == NULL
)
if ((bp
= dp
->b_actf
) == NULL
) {
um
->um_tab
.b_actf
= dp
->b_forw
;
addr
= (struct utdevice
*)um
->um_addr
;
tjunit
= TJUNIT(bp
->b_dev
);
/* note slave select, density, and format were merged on open */
addr
->uttc
= sc
->sc_dens
;
sc
->sc_dsreg
= addr
->utds
;
sc
->sc_erreg
= addr
->uter
;
sc
->sc_resid
= MASKREG(addr
->utfc
);
* Default is that last command was NOT a write command;
* if we do a write command we will notice this in utintr().
if (sc
->sc_openf
< 0 || (addr
->utds
&UTDS_MOL
) == 0) {
* Have had a hard error on a non-raw tape
* or the tape unit is now unavailable
if (bp
== &cutbuf
[UTUNIT(bp
->b_dev
)]) {
* Execute a control operation with the specified
if (bp
->b_command
== UT_SENSE
)
if (bp
->b_command
== UT_SFORW
&& (addr
->utds
& UTDS_EOT
)) {
bp
->b_resid
= bp
->b_bcount
;
* Set next state; handle timeouts
if (bp
->b_command
== UT_REW
) {
um
->um_tab
.b_state
= SREW
;
um
->um_tab
.b_state
= SCOM
;
sc
->sc_timo
= imin(imax(10*(int)-bp
->b_repcnt
,60),5*60);
/* NOTE: this depends on the ut command values */
if (bp
->b_command
>= UT_SFORW
&& bp
->b_command
<= UT_SREVF
)
addr
->utfc
= -bp
->b_repcnt
;
* The following checks boundary conditions for operations
* on non-raw tapes. On raw tapes the initialization of
* sc->sc_nxrec by utphys causes them to be skipped normally
* (except in the case of retries).
if (bdbtofsb(bp
->b_blkno
) > sc
->sc_nxrec
) {
/* can't read past end of file */
if (bdbtofsb(bp
->b_blkno
) == sc
->sc_nxrec
&& (bp
->b_flags
&B_READ
)) {
/* read at eof returns 0 count */
bp
->b_resid
= bp
->b_bcount
;
if ((bp
->b_flags
&B_READ
) == 0)
sc
->sc_nxrec
= bdbtofsb(bp
->b_blkno
)+1;
* If the tape is correctly positioned, set up all the
* registers but the csr, and give control over to the
* UNIBUS adaptor routines, to wait for resources to
if ((blkno
= sc
->sc_blkno
) == bdbtofsb(bp
->b_blkno
)) {
addr
->utwc
= -(((bp
->b_bcount
)+1)>>1);
addr
->utfc
= -bp
->b_bcount
;
if ((bp
->b_flags
&B_READ
) == 0) {
* On write error retries erase the
* inter-record gap before rewriting.
if (um
->um_tab
.b_errcnt
) {
if (um
->um_tab
.b_state
!= SERASED
) {
um
->um_tab
.b_state
= SERASE
;
addr
->utcs1
= UT_ERASE
|UT_IE
|UT_GO
;
if (addr
->utds
& UTDS_EOT
) {
bp
->b_resid
= bp
->b_bcount
;
um
->um_tab
.b_state
= SIO
;
* Tape positioned incorrectly; seek forwards or
* backwards to the correct spot. This happens for
* raw tapes only on error retries.
um
->um_tab
.b_state
= SSEEK
;
if (blkno
< bdbtofsb(bp
->b_blkno
)) {
addr
->utfc
= blkno
- bdbtofsb(bp
->b_blkno
);
bp
->b_command
= UT_SFORW
;
addr
->utfc
= bdbtofsb(bp
->b_blkno
) - blkno
;
sc
->sc_timo
= imin(imax(10 * -addr
->utfc
, 60), 5*60);
* Perform the command setup in bp.
addr
->utcs1
= bp
->b_command
|UT_IE
|UT_GO
;
* Advance to the next command in the slave queue,
* posting notice and releasing resources as needed.
dp
->b_actf
= bp
->av_forw
;
* Start operation on controller --
* UNIBUS resources have been allocated.
register struct uba_ctlr
*um
;
register struct utdevice
*addr
= (struct utdevice
*)um
->um_addr
;
addr
->utba
= (u_short
) um
->um_ubinfo
;
addr
->utcs1
= um
->um_cmd
|((um
->um_ubinfo
>>8)&0x300)|UT_IE
|UT_GO
;
register struct uba_ctlr
*um
= utminfo
[ut11
];
register struct utdevice
*addr
;
register struct tj_softc
*sc
;
u_short tjunit
, cs2
, cs1
;
if ((dp
= um
->um_tab
.b_actf
) == NULL
)
tjunit
= TJUNIT(bp
->b_dev
);
addr
= (struct utdevice
*)tjdinfo
[tjunit
]->ui_addr
;
sc
->sc_dsreg
= addr
->utds
;
sc
->sc_erreg
= addr
->uter
;
sc
->sc_resid
= MASKREG(addr
->utfc
);
if ((bp
->b_flags
&B_READ
) == 0)
state
= um
->um_tab
.b_state
;
if ((addr
->utds
&UTDS_ERR
) || (addr
->utcs1
&UT_TRE
)) {
* To clear the ERR bit, we must issue a drive clear
* command, and to clear the TRE bit we must set the
if ((cs1
= addr
->utcs1
)&UT_TRE
)
addr
->utcs2
|= UTCS2_CLR
;
/* is this dangerous ?? */
while ((addr
->utcs1
&UT_RDY
) == 0)
addr
->utcs1
= UT_CLEAR
|UT_GO
;
* If we were reading at 1600 or 6250 bpi and the error
* was corrected, then don't consider this an error.
if (sc
->sc_erreg
& UTER_COR
&& (bp
->b_flags
& B_READ
) &&
(addr
->uttc
& UTTC_DEN
) != UT_NRZI
) {
"ut%d: soft error bn%d cs1=%b er=%b cs2=%b ds=%b\n",
tjunit
, bp
->b_blkno
, cs1
, UT_BITS
, sc
->sc_erreg
,
UTER_BITS
, cs2
, UTCS2_BITS
, sc
->sc_dsreg
, UTDS_BITS
);
sc
->sc_erreg
&= ~UTER_COR
;
* If we were reading from a raw tape and the only error
* was that the record was too long, then we don't consider
if (bp
== &rutbuf
[UTUNIT(bp
->b_dev
)] && (bp
->b_flags
&B_READ
) &&
sc
->sc_erreg
&= ~UTER_FCE
;
* Fix up errors which occur due to backspacing
* "over" the front of the tape.
if ((sc
->sc_dsreg
& UTDS_BOT
) && bp
->b_command
== UT_SREV
&&
((sc
->sc_erreg
&= ~(UTER_NEF
|UTER_FCE
)) == 0))
* Retry soft errors up to 8 times
if ((sc
->sc_erreg
&UTER_HARD
) == 0 && state
== SIO
) {
if (++um
->um_tab
.b_errcnt
< 7) {
* Hard or non-I/O errors on non-raw tape
if (sc
->sc_openf
> 0 && bp
!= &rutbuf
[UTUNIT(bp
->b_dev
)])
* Couldn't recover error.
"ut%d: hard error bn%d cs1=%b er=%b cs2=%b ds=%b\n",
tjunit
, bp
->b_blkno
, cs1
, UT_BITS
, sc
->sc_erreg
,
UTER_BITS
, cs2
, UTCS2_BITS
, sc
->sc_dsreg
, UTDS_BITS
);
* If we hit a tape mark update our position.
if (sc
->sc_dsreg
& UTDS_TM
&& bp
->b_flags
& B_READ
) {
if (bp
== &cutbuf
[UTUNIT(bp
->b_dev
)]) {
if (sc
->sc_blkno
> bdbtofsb(bp
->b_blkno
)) {
bdbtofsb(bp
->b_blkno
) - addr
->utfc
;
sc
->sc_blkno
= sc
->sc_nxrec
;
bdbtofsb(bp
->b_blkno
) + addr
->utfc
;
sc
->sc_nxrec
= sc
->sc_blkno
-1;
sc
->sc_nxrec
= bdbtofsb(bp
->b_blkno
);
* Note: if we get a tape mark on a read, the
* frame count register will be zero, so b_resid
* will be calculated correctly below.
* Advance tape control FSM.
case SIO
: /* read/write increments tape block # */
case SCOM
: /* motion commands update current position */
if (bp
== &cutbuf
[UTUNIT(bp
->b_dev
)])
switch ((int)bp
->b_command
) {
sc
->sc_blkno
-= bp
->b_repcnt
;
sc
->sc_blkno
+= bp
->b_repcnt
;
addr
->utcs1
= UT_CLEAR
|UT_GO
;
sc
->sc_blkno
= bdbtofsb(bp
->b_blkno
);
* Completed erase of the inter-record gap due to a
* write error; now retry the write operation.
um
->um_tab
.b_state
= SERASED
;
case SREW
: /* clear attention bit */
addr
->utcs1
= UT_CLEAR
|UT_GO
;
printf("bad state %d\n", state
);
* Reset error count and remove
dp
->b_actf
= bp
->av_forw
;
* For read command, frame count register contains
* actual length of tape record. Otherwise, it
* holds negative residual count.
if (state
== SIO
&& um
->um_cmd
== UT_RCOM
) {
if (bp
->b_bcount
> MASKREG(addr
->utfc
))
bp
->b_resid
= bp
->b_bcount
- MASKREG(addr
->utfc
);
bp
->b_resid
= MASKREG(-addr
->utfc
);
* Circulate slave to end of controller queue
* to give other slaves a chance
um
->um_tab
.b_actf
= dp
->b_forw
;
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
if (um
->um_tab
.b_actf
== 0)
* Watchdog timer routine.
register struct tj_softc
*sc
= &tj_softc
[TJUNIT(dev
)];
if (sc
->sc_timo
!= INF
&& (sc
->sc_timo
-= 5) < 0) {
printf("tj%d: lost interrupt\n", TJUNIT(dev
));
timeout(uttimer
, (caddr_t
)dev
, 5*hz
);
* Raw interface for a read
errno
= utphys(dev
, uio
);
return (physio(utstrategy
, &rutbuf
[UTUNIT(dev
)], dev
, B_READ
, minphys
, uio
));
* Raw interface for a write
errno
= utphys(dev
, uio
);
return (physio(utstrategy
, &rutbuf
[UTUNIT(dev
)], dev
, B_WRITE
, minphys
, uio
));
* Check for valid device number dev and update our notion
* of where we are on the tape
register int tjunit
= TJUNIT(dev
);
register struct tj_softc
*sc
;
register struct uba_device
*ui
;
if (tjunit
>= NTJ
|| (ui
=tjdinfo
[tjunit
]) == 0 || ui
->ui_alive
== 0)
sc
->sc_blkno
= bdbtofsb(uio
->uio_offset
>>9);
sc
->sc_nxrec
= sc
->sc_blkno
+1;
utioctl(dev
, cmd
, data
, flag
)
register struct tj_softc
*sc
= &tj_softc
[TJUNIT(dev
)];
register struct buf
*bp
= &cutbuf
[UTUNIT(dev
)];
/* we depend of the values and order of the MT codes here */
{UT_WEOF
,UT_SFORWF
,UT_SREVF
,UT_SFORW
,UT_SREV
,UT_REW
,UT_REWOFFL
,UT_SENSE
};
mtop
= (struct mtop
*)data
;
callcount
= mtop
->mt_count
;
case MTREW
: case MTOFFL
: case MTNOP
:
if (callcount
<= 0 || fcount
<= 0)
while (--callcount
>= 0) {
utcommand(dev
, utops
[mtop
->mt_op
], fcount
);
if ((bp
->b_flags
&B_ERROR
) || (sc
->sc_dsreg
&UTDS_BOT
))
mtget
= (struct mtget
*)data
;
mtget
->mt_dsreg
= sc
->sc_dsreg
;
mtget
->mt_erreg
= sc
->sc_erreg
;
mtget
->mt_resid
= sc
->sc_resid
;
mtget
->mt_type
= MT_ISUT
;
register struct uba_ctlr
*um
;
register struct uba_device
*ui
;
for (ut11
= 0; ut11
< NUT
; ut11
++) {
if ((um
= utminfo
[ut11
]) == 0 || um
->um_alive
== 0 ||
um
->um_tab
.b_actf
= um
->um_tab
.b_actl
= 0;
printf("<%d>", (um
->um_ubinfo
>>28)&0xf);
((struct utdevice
*)(um
->um_addr
))->utcs1
= UT_CLEAR
|UT_GO
;
((struct utdevice
*)(um
->um_addr
))->utcs2
|= UTCS2_CLR
;
for (tjunit
= 0; tjunit
< NTJ
; tjunit
++) {
if ((ui
= tjdinfo
[tjunit
]) == 0 || ui
->ui_mi
!= um
||
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
if (tj_softc
[tjunit
].sc_openf
> 0)
tj_softc
[tjunit
].sc_openf
= -1;
* Do a stand-alone core dump to tape --
* from here down, routines are used only in dump context
register struct uba_device
*ui
;
register struct uba_regs
*up
;
register struct utdevice
*addr
;
#define phys(a,b) ((b)((int)(a)&0x7fffffff))
ui
= phys(tjdinfo
[0], struct uba_device
*);
up
= phys(ui
->ui_hd
, struct uba_hd
*)->uh_physuba
;
addr
= (struct utdevice
*)ui
->ui_physaddr
;
* Be sure to set the appropriate density here. We use
* 6250, but maybe it should be done at 1600 to insure the
* tape can be read by most any other tape drive available.
addr
->uttc
= UT_GCR
|PDP11FMT
; /* implicit slave 0 or-ed in */
addr
->utcs1
= UT_CLEAR
|UT_GO
;
blk
= num
> DBSIZE
? DBSIZE
: num
;
utdwrite(start
, blk
, addr
, up
);
if ((addr
->utds
&UTDS_ERR
) || (addr
->utcs1
&UT_TRE
))
if ((addr
->utds
&UTDS_ERR
) || (addr
->utcs1
&UT_TRE
))
addr
->utcs1
= UT_REW
|UT_GO
;
utdwrite(dbuf
, num
, addr
, up
)
register struct utdevice
*addr
;
*(int *)io
++ = (dbuf
++ | (1<<UBAMR_DPSHIFT
) | UBAMR_MRV
);
addr
->utwc
= -((num
*NBPG
)>>1);
addr
->utfc
= -(num
*NBPG
);
addr
->utcs1
= UT_WCOM
|UT_GO
;
while ((s
&UTDS_DRY
) == 0);
addr
->utcs1
= UT_WEOF
|UT_GO
;