* test driver with more than one slave
* test driver with more than one controller
* what happens if you offline tape during rewind?
* test using file system on tape
* There is a ctmbuf per tape controller.
* It is used as the token to pass to the internal routines
* to execute tape ioctls, and also acts as a lock on the slaves
* on the controller, since there is only one per controller.
* In particular, when the tape is rewinding on close we release
* the user process but any further attempts to use the tape drive
* before the rewind completes will hang waiting for ctmbuf.
* Raw tape operations use rtmbuf. The driver
* notices when rtmbuf is being used and allows the user
* program to continue after errors and read records
* not of the standard length (BSIZE).
* Driver unibus interface routines and variables.
int tmprobe(), tmslave(), tmattach(), tmdgo(), tmintr();
struct uba_ctlr
*tmminfo
[NTM
];
struct uba_device
*tedinfo
[NTE
];
u_short tmstd
[] = { 0772520, 0 };
struct uba_driver tmdriver
=
{ tmprobe
, tmslave
, tmattach
, tmdgo
, tmstd
, "te", tedinfo
, "tm", tmminfo
, 0 };
/* bits in minor device */
#define TEUNIT(dev) (minor(dev)&03)
#define TMUNIT(dev) (tetotm[TEUNIT(dev)])
#define INF (daddr_t)1000000L
* Software state per tape transport.
* 1. A tape drive is a unique-open device; we refuse opens when it is already.
* 2. We keep track of the current position on a block tape and seek
* before operations by forward/back spacing if necessary.
* 3. We remember if the last operation was a write on a tape, so if a tape
* is open read write and the last thing done is a write we can
* write a standard end of tape mark (two eofs).
* 4. We remember the status registers after the last command, using
* then internally and returning them to the SENSE ioctl.
* 5. We remember the last density the tape was used at. If it is
* not a BOT when we start using it and we are writing, we don't
* let the density be changed.
char sc_openf
; /* lock against multiple opens */
char sc_lastiow
; /* last op was a write */
daddr_t sc_blkno
; /* block number, for block device tape */
daddr_t sc_nxrec
; /* position of end of tape, if known */
u_short sc_erreg
; /* copy of last erreg */
u_short sc_dsreg
; /* copy of last dsreg */
short sc_resid
; /* copy of last bc */
short sc_lastcmd
; /* last command to handle direction changes */
u_short sc_dens
; /* prototype command with density info */
int tmgapsdcnt
; /* DEBUG */
* States for um->um_tab.b_active, the per controller state flag.
* This is used to sequence control in the driver.
#define SSEEK 1 /* seeking */
#define SIO 2 /* doing seq i/o */
#define SCOM 3 /* sending control command */
#define SREW 4 /* sending a drive rewind */
* Determine if there is a controller for
* a tm at address reg. Our goal is to make the
register int br
, cvec
; /* must be r11,r10; value-result */
br
= 0; cvec
= br
; br
= cvec
;
((struct device
*)reg
)->tmcs
= TM_IE
;
* If this is a tm11, it ought to have interrupted
* by now, if it isn't (ie: it is a ts04) then we just
* hope that it didn't interrupt, so autoconf will ignore it.
* Just in case, we will reference one
* of the more distant registers, and hope for a machine
* check, or similar disaster if this is a ts.
* Note: on an 11/780, badaddr will just generate
* a uba error for a ts; but our caller will notice that
* so we won't check for it.
if (badaddr((caddr_t
)&((struct device
*)reg
)->tmrd
, 2))
* Due to a design flaw, we cannot ascertain if the tape
* exists or not unless it is on line - ie: unless a tape is
* mounted. This is too servere a restriction to bear,
* so all units are assumed to exist.
* Record attachment of the unit to the controller.
* Tetotm is used in TMUNIT to index the ctmbuf and rtmbuf
* arrays given a te unit number.
tetotm
[ui
->ui_unit
] = ui
->ui_mi
->um_ctlr
;
* Open the device. Tapes are unique open
* devices, so we refuse if it is already open.
* We also check that a tape is available, and
* don't block waiting here; if you want to wait
* for a tape you should timeout in user code.
register struct uba_device
*ui
;
register struct te_softc
*sc
;
if (teunit
>=NTE
|| (sc
= &te_softc
[teunit
])->sc_openf
||
(ui
= tedinfo
[teunit
]) == 0 || ui
->ui_alive
== 0) {
dens
= TM_IE
| TM_GO
| (ui
->ui_slave
<< 8);
if ((minor(dev
) & T_1600BPI
) == 0)
tmcommand(dev
, TM_SENSE
, 1);
if (sc
->sc_erreg
&TMER_SDWN
) {
sleep((caddr_t
)&lbolt
, PZERO
+1);
if ((sc
->sc_erreg
&(TMER_SELR
|TMER_TUR
)) != (TMER_SELR
|TMER_TUR
) ||
(flag
&FWRITE
) && (sc
->sc_erreg
&TMER_WRL
) ||
(sc
->sc_erreg
&TMER_BOT
) == 0 && (flag
&FWRITE
) &&
* Not online or density switch in mid-tape or write locked.
sc
->sc_blkno
= (daddr_t
)0;
* If tape was open for writing or last operation was
* a write, then write two EOF's and backspace over the last one.
* Unless this is a non-rewinding special file, rewind the tape.
* Make the tape available to others.
register struct te_softc
*sc
= &te_softc
[TEUNIT(dev
)];
if (flag
== FWRITE
|| (flag
&FWRITE
) && sc
->sc_lastiow
) {
tmcommand(dev
, TM_WEOF
, 1);
tmcommand(dev
, TM_WEOF
, 1);
tmcommand(dev
, TM_SREV
, 1);
if ((minor(dev
)&T_NOREWIND
) == 0)
* 0 count means don't hang waiting for rewind complete
* rather ctmbuf stays busy until the operation completes
* preventing further opens from completing by
* preventing a TM_SENSE from completing.
tmcommand(dev
, TM_REW
, 0);
* Execute a command on the tape drive
* a specified number of times.
tmcommand(dev
, com
, count
)
bp
= &ctmbuf
[TMUNIT(dev
)];
while (bp
->b_flags
&B_BUSY
) {
* This special check is because B_BUSY never
* gets cleared in the non-waiting rewind case.
if (bp
->b_repcnt
== 0 && (bp
->b_flags
&B_DONE
))
sleep((caddr_t
)bp
, PRIBIO
);
bp
->b_flags
= B_BUSY
|B_READ
;
* In case of rewind from close, don't wait.
* This is the only case where count can be 0.
if (bp
->b_flags
&B_WANTED
)
* Queue a tape operation.
int teunit
= TEUNIT(bp
->b_dev
);
register struct uba_ctlr
*um
;
* Put transfer at end of unit queue
if (dp
->b_actf
== NULL
) {
* Transport not already active...
* put at end of controller queue.
um
= tedinfo
[teunit
]->ui_mi
;
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, get
if (um
->um_tab
.b_active
== 0)
* Start activity on a tm controller.
register struct uba_ctlr
*um
;
register struct buf
*bp
, *dp
;
register struct device
*addr
= (struct device
*)um
->um_addr
;
register struct te_softc
*sc
;
register struct uba_device
*ui
;
* Look for an idle transport on the controller.
if ((dp
= um
->um_tab
.b_actf
) == NULL
)
if ((bp
= dp
->b_actf
) == NULL
) {
um
->um_tab
.b_actf
= dp
->b_forw
;
teunit
= TEUNIT(bp
->b_dev
);
* Record pre-transfer status (e.g. for TM_SENSE)
addr
= (struct device
*)um
->um_addr
;
addr
->tmcs
= (ui
->ui_slave
<< 8);
sc
->sc_dsreg
= addr
->tmcs
;
sc
->sc_erreg
= addr
->tmer
;
sc
->sc_resid
= addr
->tmbc
;
* Default is that last command was NOT a write command;
* if we do a write command we will notice this in tmintr().
if (sc
->sc_openf
< 0 || (addr
->tmcs
&TM_CUR
) == 0) {
* Have had a hard error on a non-raw tape
* or the tape unit is now unavailable
if (bp
== &ctmbuf
[TMUNIT(bp
->b_dev
)]) {
* Execute control operation with the specified count.
if (bp
->b_command
== TM_SENSE
)
bp
->b_command
== TM_REW
? SREW
: SCOM
;
if (bp
->b_command
== TM_SFORW
|| bp
->b_command
== TM_SREV
)
addr
->tmbc
= bp
->b_repcnt
;
* The following checks handle boundary cases for operation
* on non-raw tapes. On raw tapes the initialization of
* sc->sc_nxrec by tmphys causes them to be skipped normally
* (except in the case of retries).
if (dbtofsb(bp
->b_blkno
) > sc
->sc_nxrec
) {
* Can't read past known end-of-file.
if (dbtofsb(bp
->b_blkno
) == sc
->sc_nxrec
&&
* Reading at end of file returns 0 bytes.
bp
->b_resid
= bp
->b_bcount
;
if ((bp
->b_flags
&B_READ
) == 0)
sc
->sc_nxrec
= dbtofsb(bp
->b_blkno
) + 1;
* If the data transfer command is in the correct place,
* set up all the registers except the csr, and give
* control over to the UNIBUS adapter routines, to
* wait for resources to start the i/o.
if ((blkno
= sc
->sc_blkno
) == dbtofsb(bp
->b_blkno
)) {
addr
->tmbc
= -bp
->b_bcount
;
if ((bp
->b_flags
&B_READ
) == 0) {
um
->um_tab
.b_active
= SIO
;
um
->um_cmd
= sc
->sc_dens
|cmd
;
if (tmreverseop(sc
->sc_lastcmd
))
while (addr
->tmer
& TMER_SDWN
)
sc
->sc_lastcmd
= TM_RCOM
; /* will serve */
* Tape positioned incorrectly;
* set to seek forwards or backwards to the correct spot.
* This happens for raw tapes only on error retries.
um
->um_tab
.b_active
= SSEEK
;
if (blkno
< dbtofsb(bp
->b_blkno
)) {
bp
->b_command
= TM_SFORW
;
addr
->tmbc
= blkno
- dbtofsb(bp
->b_blkno
);
addr
->tmbc
= dbtofsb(bp
->b_blkno
) - blkno
;
* It is strictly necessary to wait for the tape
* to stop before changing directions, but the TC11
if (tmreverseop(sc
->sc_lastcmd
) != tmreverseop(bp
->b_command
))
while (addr
->tmer
& TM_SDWN
)
sc
->sc_lastcmd
= bp
->b_command
;
addr
->tmcs
= (sc
->sc_dens
| bp
->b_command
);
* Done with this operation due to error or
* the fact that it doesn't do anything.
* Release UBA resources (if any), dequeue
* the transfer and continue processing this slave.
dp
->b_actf
= bp
->av_forw
;
* The UNIBUS resources we needed have been
* allocated to us; start the device.
register struct uba_ctlr
*um
;
register struct device
*addr
= (struct device
*)um
->um_addr
;
addr
->tmba
= um
->um_ubinfo
;
addr
->tmcs
= um
->um_cmd
| ((um
->um_ubinfo
>> 12) & 0x30);
register struct uba_ctlr
*um
= tmminfo
[tm11
];
register struct device
*addr
;
register struct te_softc
*sc
;
if ((dp
= um
->um_tab
.b_actf
) == NULL
)
teunit
= TEUNIT(bp
->b_dev
);
addr
= (struct device
*)tedinfo
[teunit
]->ui_addr
;
* If last command was a rewind, and tape is still
* rewinding, wait for the rewind complete interrupt.
if (um
->um_tab
.b_active
== SREW
) {
um
->um_tab
.b_active
= SCOM
;
* An operation completed... record status
sc
->sc_dsreg
= addr
->tmcs
;
sc
->sc_erreg
= addr
->tmer
;
sc
->sc_resid
= addr
->tmbc
;
if ((bp
->b_flags
& B_READ
) == 0)
state
= um
->um_tab
.b_active
;
while (addr
->tmer
& TMER_SDWN
)
; /* await settle down */
* If we hit the end of the tape file, update our position.
if (addr
->tmer
&TMER_EOF
) {
tmseteof(bp
); /* set blkno and nxrec */
state
= SCOM
; /* force completion */
* Stuff bc so it will be unstuffed correctly
addr
->tmbc
= -bp
->b_bcount
;
* If we were reading raw tape and the only error was that the
* record was too long, then we don't consider this an error.
if (bp
== &rtmbuf
[TMUNIT(bp
->b_dev
)] && (bp
->b_flags
&B_READ
) &&
(addr
->tmer
&(TMER_HARD
|TMER_SOFT
)) == TMER_RLE
)
* If error is not hard, and this was an i/o operation
if ((addr
->tmer
&TMER_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
!= &rtmbuf
[TMUNIT(bp
->b_dev
)])
printf("te%d: hard error bn%d er=%b\n", minor(bp
->b_dev
)&03,
bp
->b_blkno
, sc
->sc_erreg
, TMER_BITS
);
* Advance tape control FSM.
* Read/write increments tape block number
* For forward/backward space record update current position.
if (bp
== &ctmbuf
[TMUNIT(bp
->b_dev
)])
sc
->sc_blkno
-= bp
->b_repcnt
;
sc
->sc_blkno
+= bp
->b_repcnt
;
sc
->sc_blkno
= dbtofsb(bp
->b_blkno
);
* Reset error count and remove
dp
->b_actf
= bp
->av_forw
;
bp
->b_resid
= -addr
->tmbc
;
* 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)
register int teunit
= TEUNIT(bp
->b_dev
);
register struct device
*addr
=
(struct device
*)tedinfo
[teunit
]->ui_addr
;
register struct te_softc
*sc
= &te_softc
[teunit
];
if (bp
== &ctmbuf
[TMUNIT(bp
->b_dev
)]) {
if (sc
->sc_blkno
> dbtofsb(bp
->b_blkno
)) {
sc
->sc_nxrec
= dbtofsb(bp
->b_blkno
) - addr
->tmbc
;
sc
->sc_blkno
= sc
->sc_nxrec
;
sc
->sc_blkno
= dbtofsb(bp
->b_blkno
) + addr
->tmbc
;
sc
->sc_nxrec
= sc
->sc_blkno
- 1;
sc
->sc_nxrec
= dbtofsb(bp
->b_blkno
);
physio(tmstrategy
, &rtmbuf
[TMUNIT(dev
)], dev
, B_READ
, minphys
);
physio(tmstrategy
, &rtmbuf
[TMUNIT(dev
)], dev
, B_WRITE
, minphys
);
* Check that a raw device exists.
* If it does, set up sc_blkno and sc_nxrec
* so that the tape will appear positioned correctly.
register int teunit
= TEUNIT(dev
);
register struct te_softc
*sc
;
register struct uba_device
*ui
;
if (teunit
>= NTE
|| (ui
=tedinfo
[teunit
]) == 0 || ui
->ui_alive
== 0) {
a
= dbtofsb(u
.u_offset
>> 9);
register struct uba_ctlr
*um
;
register struct uba_device
*ui
;
for (tm11
= 0; tm11
< NTM
; tm11
++) {
if ((um
= tmminfo
[tm11
]) == 0 || um
->um_alive
== 0 ||
um
->um_tab
.b_actf
= um
->um_tab
.b_actl
= 0;
printf("<%d>", (um
->um_ubinfo
>>28)&0xf);
((struct device
*)(um
->um_addr
))->tmcs
= TM_DCLR
;
for (teunit
= 0; teunit
< NTE
; teunit
++) {
if ((ui
= tedinfo
[teunit
]) == 0 || ui
->ui_mi
!= um
||
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
te_softc
[teunit
].sc_openf
= -1;
tmioctl(dev
, cmd
, addr
, flag
)
int teunit
= TEUNIT(dev
);
register struct te_softc
*sc
= &te_softc
[teunit
];
register struct buf
*bp
= &ctmbuf
[TMUNIT(dev
)];
/* we depend of the values and order of the MT codes here */
{TM_WEOF
,TM_SFORW
,TM_SREV
,TM_SFORW
,TM_SREV
,TM_REW
,TM_OFFL
,TM_SENSE
};
case MTIOCTOP
: /* tape operation */
if (copyin((caddr_t
)addr
, (caddr_t
)&mtop
, sizeof(mtop
))) {
callcount
= mtop
.mt_count
;
callcount
= mtop
.mt_count
;
case MTREW
: case MTOFFL
: case MTNOP
:
if (callcount
<= 0 || fcount
<= 0) {
while (--callcount
>= 0) {
tmcommand(dev
, tmops
[mtop
.mt_op
], fcount
);
if ((mtop
.mt_op
== MTFSR
|| mtop
.mt_op
== MTBSR
) &&
if ((bp
->b_flags
&B_ERROR
) || sc
->sc_erreg
&TMER_BOT
)
mtget
.mt_dsreg
= sc
->sc_dsreg
;
mtget
.mt_erreg
= sc
->sc_erreg
;
mtget
.mt_resid
= sc
->sc_resid
;
if (copyout((caddr_t
)&mtget
, addr
, sizeof(mtget
)))
register struct uba_device
*ui
;
register struct uba_regs
*up
;
register struct device
*addr
;
#define phys(a,b) ((b)((int)(a)&0x7fffffff))
ui
= phys(tedinfo
[0], struct uba_device
*);
up
= phys(ui
->ui_hd
, struct uba_hd
*)->uh_physuba
;
addr
= (struct device
*)ui
->ui_physaddr
;
addr
->tmcs
= TM_DCLR
| TM_GO
;
blk
= num
> DBSIZE
? DBSIZE
: num
;
tmdwrite(start
, blk
, addr
, up
);
addr
->tmcs
= TM_REW
| TM_GO
;
tmdwrite(dbuf
, num
, addr
, up
)
register struct device
*addr
;
*(int *)io
++ = (dbuf
++ | (1<<UBAMR_DPSHIFT
) | UBAMR_MRV
);
addr
->tmbc
= -(num
*NBPG
);
addr
->tmcs
= TM_WCOM
| TM_GO
;
register struct device
*addr
;
while ((s
& TM_CUR
) == 0);
addr
->tmcs
= TM_WEOF
| TM_GO
;