89a1773a0b16918adb12f51869fdcb05908e2ba3
int tmgapsdcnt
; /* DEBUG */
* THIS DRIVER HAS NOT BEEN TESTED WITH MORE THAN ONE TRANSPORT.
#define DELAY(N) { register int d = N; while (--d > 0); }
int tmprobe(), tmslave(), tmattach(), tmdgo(), tmintr();
struct uba_minfo
*tmminfo
[NTM
];
struct uba_dinfo
*tmdinfo
[NTE
];
struct uba_dinfo
*tmip
[NTM
][4];
u_short tmstd
[] = { 0772520, 0 };
struct uba_driver tmdriver
=
{ tmprobe
, tmslave
, tmattach
, tmdgo
, tmstd
, "te", tmdinfo
, "tm", tmminfo
, 0 };
/* bits in minor device */
#define TMUNIT(dev) (minor(dev)&03)
#define INF (daddr_t)1000000L
* Software state per tape transport.
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
; /* desired block position */
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 */
* States for um->um_tab.b_active, the
* per controller state flag.
#define SSEEK 1 /* seeking */
#define SIO 2 /* doing seq i/o */
#define SCOM 3 /* sending control command */
#define SREW 4 /* sending a drive rewind */
/* WE CURRENTLY HANDLE REWINDS PRIMITIVELY, BUSYING OUT THE CONTROLLER */
/* DURING THE REWIND... IF WE EVER GET TWO TRANSPORTS, WE CAN DEBUG MORE */
/* SOPHISTICATED LOGIC... THIS SIMPLE CODE AT LEAST MAY WORK. */
* Determine if there is a controller for
* a tm at address reg. Our goal is to make the
br
= 0; br
= cvec
; cvec
= br
;
((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(&((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 port.
tmip
[ui
->ui_ctlr
][ui
->ui_slave
] = ui
;
* 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.
register struct uba_dinfo
*ui
;
register struct tm_softc
*sc
;
if (unit
>=NTE
|| (sc
= &tm_softc
[unit
])->sc_openf
||
(ui
= tmdinfo
[unit
]) == 0 || ui
->ui_alive
== 0) {
tmcommand(dev
, TM_SENSE
, 1);
if ((sc
->sc_erreg
&(TM_SELR
|TM_TUR
)) != (TM_SELR
|TM_TUR
)) {
uprintf("tape not online\n");
if ((flag
&(FREAD
|FWRITE
)) == FWRITE
&& sc
->sc_erreg
&TM_WRL
) {
uprintf("tape write protected\n");
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 tm_softc
*sc
= &tm_softc
[TMUNIT(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)
tmcommand(dev
, TM_REW
, 1);
* 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
) {
sleep((caddr_t
)bp
, PRIBIO
);
bp
->b_flags
= B_BUSY
|B_READ
;
if (bp
->b_flags
&B_WANTED
)
* Decipher a tape operation and do what is needed
* to see that it happens.
int unit
= TMUNIT(bp
->b_dev
);
register struct uba_minfo
*um
;
register struct tm_softc
*sc
= &tm_softc
[unit
];
* Put transfer at end of unit queue
if (dp
->b_actf
== NULL
) {
* Transport not already active...
* put at end of controller queue.
um
= tmdinfo
[unit
]->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_minfo
*um
;
register struct buf
*bp
, *dp
;
register struct device
*addr
= (struct device
*)um
->um_addr
;
register struct tm_softc
*sc
;
register struct uba_dinfo
*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
;
unit
= TMUNIT(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 this (non-raw) tape,
* or the tape unit is now unavailable (e.g. taken off
* If operation is not a control operation,
* check for boundary conditions.
if (bp
!= &ctmbuf
[unit
]) {
if (dbtofsb(bp
->b_blkno
) > sc
->sc_nxrec
) {
bp
->b_error
= ENXIO
; /* past EOF */
if (dbtofsb(bp
->b_blkno
) == sc
->sc_nxrec
&&
bp
->b_resid
= bp
->b_bcount
;
if ((bp
->b_flags
&B_READ
) == 0)
sc
->sc_nxrec
= dbtofsb(bp
->b_blkno
) + 1;
* Set up the command, and then if this is a mt ioctl,
* do the operation using, for TM_SFORW and TM_SREV, the specified
cmd
= TM_IE
| TM_GO
| (ui
->ui_slave
<< 8);
if ((minor(bp
->b_dev
) & T_1600BPI
) == 0)
if (bp
== &ctmbuf
[unit
]) {
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
;
* 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
;
if (tmreverseop(sc->sc_lastcmd))
while (addr->tmer & TM_SDWN)
sc
->sc_lastcmd
= TM_RCOM
; /* will serve */
* Block tape positioned incorrectly;
* seek forwards or backwards to the correct spot.
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
;
if (tmreverseop(sc->sc_lastcmd) != tmreverseop(bp->b_command))
while (addr->tmer & TM_SDWN)
sc
->sc_lastcmd
= bp
->b_command
;
addr
->tmcs
= (cmd
| 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_minfo
*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_minfo
*um
= tmminfo
[tm11
];
register struct device
*addr
= (struct device
*)tmdinfo
[tm11
]->ui_addr
;
register struct tm_softc
*sc
;
* 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
if ((dp
= um
->um_tab
.b_actf
) == NULL
)
unit
= TMUNIT(bp
->b_dev
);
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
& TM_SDWN
)
; /* await settle down */
* If we hit the end of the tape update our position.
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 and the only error was that the
* record was to long, then we don't consider this an error.
if ((bp
->b_flags
&B_READ
) &&
(addr
->tmer
&(TM_HARD
|TM_SOFT
)) == TM_RLE
)
* If error is not hard, and this was an i/o operation
if ((addr
->tmer
&TM_HARD
)==0 && state
==SIO
) {
if (++um
->um_tab
.b_errcnt
< 7) {
/* SHOULD CHECK THAT RECOVERY WORKS IN THIS CASE */
/* AND THEN ONLY PRINT IF errcnt==7 */
if((addr
->tmer
&TM_SOFT
) == TM_NXM
)
printf("TM UBA late error\n");
* Hard or non-i/o errors on non-raw tape
if (sc
->sc_openf
>0 && bp
!= &rtmbuf
[unit
])
printf("tm%d er=%b\n", dkunit(bp
),
sc
->sc_erreg
, TMEREG_BITS
);
* Advance tape control FSM.
* Read/write increments tape block number
* Unless special operation, op completed.
* Operation on block device...
* iterate operations which don't repeat
* for themselves in the hardware; for forward/
* backward space record update the current position.
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 unit
= TMUNIT(bp
->b_dev
);
register struct device
*addr
=
(struct device
*)tmdinfo
[unit
]->ui_addr
;
register struct tm_softc
*sc
= &tm_softc
[unit
];
if (bp
== &ctmbuf
[unit
]) {
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
);
register struct tm_softc
*sc
= &tm_softc
[TMUNIT(dev
)];
a
= dbtofsb(u
.u_offset
>> 9);
register struct uba_minfo
*um
;
register struct uba_dinfo
*ui
;
for (tm11
= 0; tm11
< NTM
; tm11
++) {
if ((um
= tmminfo
[tm11
]) == 0 || um
->um_alive
== 0 ||
DELAY(2000000); /* time to self test */
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 (unit
= 0; unit
< NTE
; unit
++) {
if ((ui
= tmdinfo
[unit
]) == 0)
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
tm_softc
[unit
].sc_openf
= -1;
tmioctl(dev
, cmd
, addr
, flag
)
register struct tm_softc
*sc
= &tm_softc
[unit
];
register struct buf
*bp
= &ctmbuf
[unit
];
/* 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
&TM_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_dinfo
*ui
;
register struct uba_regs
*up
;
register struct device
*addr
;
#define phys(a,b) ((b)((int)(a)&0x7fffffff))
ui
= phys(tmdinfo
[0], struct uba_dinfo
*);
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<<UBA_DPSHIFT
) | UBA_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
;