* test driver with more than one controller
* test rewinds without hanging in driver
* what happens if you offline tape during rewind?
* test using file system on tape
* There is a ctsbuf per tape controller.
* It is used as the token to pass to the internal routines
* to execute tape ioctls.
* 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 ctsbuf.
* Raw tape operations use rtsbuf. The driver
* notices when rtsbuf 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 tsprobe(), tsslave(), tsattach(), tsdgo(), tsintr();
struct uba_ctlr
*tsminfo
[NTS
];
struct uba_device
*tsdinfo
[NTS
];
u_short tsstd
[] = { 0772520, 0 };
/*** PROBABLY DON'T NEED ALL THESE SINCE CONTROLLER == DRIVE ***/
struct uba_driver zsdriver
=
{ tsprobe
, tsslave
, tsattach
, tsdgo
, tsstd
, "ts", tsdinfo
, "ts", tsminfo
, 0 };
/* bits in minor device */
#define TSUNIT(dev) (minor(dev)&03)
#define INF (daddr_t)1000000L
* Software state per tape transport.
* Also contains hardware state in message packets.
* 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.
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 */
short sc_resid
; /* copy of last bc */
struct ts_cmd sc_cmd
; /* the command packet - ADDR MUST BE 0 MOD 4 */
struct ts_sts sc_sts
; /* status packet, for returned status */
struct ts_char sc_char
; /* characteristics packet */
u_short sc_uba
; /* Unibus addr of cmd pkt for tsdb */
struct ts_softc
*ts_ubaddr
; /* Unibus address of ts_softc */
* 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 ts 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
;
/* IT'S TOO HARD TO MAKE THIS THING INTERRUPT
JUST TO FIND ITS VECTOR */
* TS11 only supports one drive per controller;
* check for ui_slave == 0.
* DO WE REALLY NEED THIS ROUTINE???
if (ui
->ui_slave
) /* non-zero slave not allowed */
* Record attachment of the unit to the controller.
* SHOULD THIS ROUTINE DO ANYTHING???
* 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 ts_softc
*sc
;
if (tsunit
>=NTS
|| (sc
= &ts_softc
[tsunit
])->sc_openf
||
(ui
= tsdinfo
[tsunit
]) == 0 || ui
->ui_alive
== 0) {
tscommand(dev
, TS_SENSE
, 1);
if ((sc
->sc_sts
.s_xs0
&TS_ONL
) == 0 || ((flag
&(FREAD
|FWRITE
)) ==
FWRITE
&& (sc
->sc_sts
.s_xs0
&TS_WLK
))) {
* Not online 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 ts_softc
*sc
= &ts_softc
[TSUNIT(dev
)];
if (flag
== FWRITE
|| (flag
&FWRITE
) && sc
->sc_lastiow
) {
tscommand(dev
, TS_WEOF
, 1);
tscommand(dev
, TS_WEOF
, 1);
tscommand(dev
, TS_SREV
, 1);
if ((minor(dev
)&T_NOREWIND
) == 0)
* 0 count means don't hang waiting for rewind complete
* rather ctsbuf stays busy until the operation completes
* preventing further opens from completing by
* preventing a TS_SENSE from completing.
tscommand(dev
, TS_REW
, 0);
* Initialize the TS11. Set up Unibus mapping for command
* packets and set device characteristics.
register struct ts_softc
*sc
= &ts_softc
[unit
];
register struct uba_ctlr
*um
= tsminfo
[unit
];
register struct device
*addr
= (struct device
*)um
->um_addr
;
* Map the command and message packets into Unibus
* address space. We do all the command and message
* packets at once to minimize the amount of Unibus
ctsbuf
[unit
].b_un
.b_addr
= (caddr_t
)ts_softc
;
ctsbuf
[unit
].b_bcount
= sizeof(ts_softc
);
i
= ubasetup(um
->um_ubanum
, &ctsbuf
[unit
], 0);
ts_ubaddr
= (struct ts_softc
*)i
;
/* MAKE SURE WE DON'T GET UNIBUS ADDRESS ZERO */
printf("ts%d: zero ubaddr\n", unit
);
* Now initialize the TS11 controller.
* Set the characteristics.
if (addr
->tssr
& TS_NBA
) {
addr
->tssr
= 0; /* subsystem initialize */
i
= (int)&ts_ubaddr
[unit
].sc_cmd
; /* Unibus addr of cmd */
sc
->sc_uba
= (u_short
)(i
+ ((i
>>16)&3));
sc
->sc_char
.char_addr
= (int)&ts_ubaddr
[unit
].sc_sts
;
sc
->sc_char
.char_size
= sizeof(struct ts_sts
);
sc
->sc_char
.char_mode
= TS_ESS
;
sc
->sc_cmd
.c_cmd
= TS_ACK
| TS_SETCHR
;
sc
->sc_cmd
.c_addr
= (int)&ts_ubaddr
[unit
].sc_char
;
sc
->sc_cmd
.c_size
= sizeof(struct ts_char
);
* Execute a command on the tape drive
* a specified number of times.
tscommand(dev
, com
, count
)
bp
= &ctsbuf
[TSUNIT(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 tsunit
= TSUNIT(bp
->b_dev
);
register struct uba_ctlr
*um
;
* Put transfer at end of controller queue
um
= tsdinfo
[tsunit
]->ui_mi
;
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->av_forw
= bp
;
* If the controller is not busy, get
if (um
->um_tab
.b_active
== 0)
* Start activity on a ts controller.
register struct uba_ctlr
*um
;
register struct device
*addr
= (struct device
*)um
->um_addr
;
register struct ts_softc
*sc
;
register struct ts_cmd
*tc
;
register struct uba_device
*ui
;
* Start the controller if there is something for it to do.
if ((bp
= um
->um_tab
.b_actf
) == NULL
)
tsunit
= TSUNIT(bp
->b_dev
);
* Default is that last command was NOT a write command;
* if we do a write command we will notice this in tsintr().
if (sc
->sc_openf
< 0 || (addr
->tssr
&TS_OFL
)) {
* Have had a hard error on a non-raw tape
* or the tape unit is now unavailable
if (bp
== &ctsbuf
[TSUNIT(bp
->b_dev
)]) {
* Execute control operation with the specified count.
bp
->b_command
== TS_REW
? SREW
: SCOM
;
tc
->c_repcnt
= 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 tsphys 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
)) {
tc
->c_size
= bp
->b_bcount
;
if ((bp
->b_flags
&B_READ
) == 0)
um
->um_tab
.b_active
= SIO
;
tc
->c_cmd
= TS_ACK
| TS_CVC
| cmd
;
* 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
= TS_SFORW
;
tc
->c_repcnt
= dbtofsb(bp
->b_blkno
) - blkno
;
tc
->c_repcnt
= blkno
- dbtofsb(bp
->b_blkno
);
tc
->c_cmd
= TS_ACK
| TS_CVC
| 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.
um
->um_tab
.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
;
register struct ts_softc
*sc
= &ts_softc
[um
->um_ctlr
];
sc
->sc_cmd
.c_addr
= um
->um_ubinfo
& 0777777;
register struct uba_ctlr
*um
= tsminfo
[ts11
];
register struct device
*addr
;
register struct ts_softc
*sc
;
if ((bp
= um
->um_tab
.b_actf
) == NULL
)
tsunit
= TSUNIT(bp
->b_dev
);
addr
= (struct device
*)tsdinfo
[tsunit
]->ui_addr
;
* If last command was a rewind, and tape is still
* rewinding, wait for the rewind complete interrupt.
* SHOULD NEVER GET AN INTERRUPT IN THIS STATE.
if (um
->um_tab
.b_active
== SREW
) {
um
->um_tab
.b_active
= SCOM
;
if ((addr
->tssr
&TS_SSR
) == 0)
* An operation completed... record status
if ((bp
->b_flags
& B_READ
) == 0)
state
= um
->um_tab
.b_active
;
switch (addr
->tssr
& TS_TC
) {
case TS_UNREC
: /* unrecoverable */
case TS_FATAL
: /* fatal error */
case TS_ATTN
: /* attention (shouldn't happen) */
case TS_RECNM
: /* recoverable, no motion */
case TS_SUCC
: /* success termination */
printf("ts%d: success\n", TSUNIT(minor(bp
->b_dev
)));
case TS_ALERT
: /* tape status alert */
* If we hit the end of the tape file,
if (sc
->sc_sts
.s_xs0
& (TS_TMK
|TS_EOT
)) {
tsseteof(bp
); /* set blkno and nxrec */
state
= SCOM
; /* force completion */
* Stuff bc so it will be unstuffed correctly
sc
->sc_sts
.s_rbpcr
= bp
->b_bcount
;
* If we were reading raw tape and the record was too long
* or too short, then we don't consider this an error.
if (bp
== &rtsbuf
[TSUNIT(bp
->b_dev
)] && (bp
->b_flags
&B_READ
) &&
sc
->sc_sts
.s_xs0
&(TS_RLS
|TS_RLL
))
case TS_RECOV
: /* recoverable, tape moved */
* If this was an i/o operation retry up to 8 times.
if (++um
->um_tab
.b_errcnt
< 7) {
* Non-i/o errors on non-raw tape
if (sc
->sc_openf
>0 && bp
!= &rtsbuf
[TSUNIT(bp
->b_dev
)])
case TS_REJECT
: /* function reject */
if (state
== SIO
&& sc
->sc_sts
.s_xs0
& TS_WLE
)
printf("ts%d: write locked\n", TSUNIT(bp
->b_dev
));
if ((sc
->sc_sts
.s_xs0
& TS_ONL
) == 0)
printf("ts%d: offline\n", TSUNIT(bp
->b_dev
));
printf("ts%d: hard error bn%d xs0=%b\n", TSUNIT(bp
->b_dev
),
bp
->b_blkno
, sc
->sc_sts
.s_xs0
, TSXS0_BITS
);
* Advance tape control FSM.
* Read/write increments tape block number
* For forward/backward space record update current position.
if (bp
== &ctsbuf
[TSUNIT(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
um
->um_tab
.b_actf
= bp
->av_forw
;
bp
->b_resid
= sc
->sc_sts
.s_rbpcr
;
if (um
->um_tab
.b_actf
== 0)
register int tsunit
= TSUNIT(bp
->b_dev
);
register struct ts_softc
*sc
= &ts_softc
[tsunit
];
if (bp
== &ctsbuf
[TSUNIT(bp
->b_dev
)]) {
if (sc
->sc_blkno
> dbtofsb(bp
->b_blkno
)) {
sc
->sc_nxrec
= dbtofsb(bp
->b_blkno
) - sc
->sc_sts
.s_rbpcr
;
sc
->sc_blkno
= sc
->sc_nxrec
;
sc
->sc_blkno
= dbtofsb(bp
->b_blkno
) + sc
->sc_sts
.s_rbpcr
;
sc
->sc_nxrec
= sc
->sc_blkno
- 1;
sc
->sc_nxrec
= dbtofsb(bp
->b_blkno
);
physio(tsstrategy
, &rtsbuf
[TSUNIT(dev
)], dev
, B_READ
, minphys
);
physio(tsstrategy
, &rtsbuf
[TSUNIT(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 tsunit
= TSUNIT(dev
);
register struct ts_softc
*sc
;
register struct uba_device
*ui
;
if (tsunit
>= NTS
|| (ui
=tsdinfo
[tsunit
]) == 0 || ui
->ui_alive
== 0) {
a
= dbtofsb(u
.u_offset
>> 9);
register struct uba_ctlr
*um
;
for (ts11
= 0; ts11
< NTS
; ts11
++) {
if ((um
= tsminfo
[ts11
]) == 0 || um
->um_alive
== 0 ||
um
->um_tab
.b_actf
= um
->um_tab
.b_actl
= 0;
ts_softc
[ts11
].sc_openf
= -1;
printf("<%d>", (um
->um_ubinfo
>>28)&0xf);
tsioctl(dev
, cmd
, addr
, flag
)
int tsunit
= TSUNIT(dev
);
register struct ts_softc
*sc
= &ts_softc
[tsunit
];
register struct buf
*bp
= &ctsbuf
[TSUNIT(dev
)];
/* we depend of the values and order of the MT codes here */
{TS_WEOF
,TS_SFORW
,TS_SREV
,TS_SFORWF
,TS_SREVF
,TS_REW
,TS_OFFL
,TS_SENSE
};
case MTIOCTOP
: /* tape operation */
if (copyin((caddr_t
)addr
, (caddr_t
)&mtop
, sizeof(mtop
))) {
callcount
= mtop
.mt_count
;
case MTREW
: case MTOFFL
: case MTNOP
:
if (callcount
<= 0 || fcount
<= 0) {
while (--callcount
>= 0) {
tscommand(dev
, tsops
[mtop
.mt_op
], fcount
);
if ((mtop
.mt_op
== MTFSR
|| mtop
.mt_op
== MTBSR
) &&
if ((bp
->b_flags
&B_ERROR
) || sc
->sc_sts
.s_xs0
&TS_BOT
)
mtget
.mt_erreg
= sc
->sc_sts
.s_xs0
;
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(tsdinfo
[0], struct uba_device
*);
up
= phys(ui
->ui_hd
, struct uba_hd
*)->uh_physuba
;
addr
= (struct device
*)ui
->ui_physaddr
;
blk
= num
> DBSIZE
? DBSIZE
: num
;
tsdwrite(start
, blk
, addr
, up
);
tsdwrite(dbuf
, num
, addr
, up
)
register struct device
*addr
;
*(int *)io
++ = (dbuf
++ | (1<<UBAMR_DPSHIFT
) | UBAMR_MRV
);
addr
->tsbc
= -(num
*NBPG
);
addr
->tscs
= TS_WCOM
| TM_GO
;
register struct device
*addr
;
while ((s
& TS_SSR
) == 0);
addr
->tscs
= TS_WEOF
| TM_GO
;