#if defined(VAX750) || defined(VAX730)
* TU58 DECtape II device driver
* TU58 console cassette driver (for VAX-11/750 or VAX-11/730).
* The TU58 is treated as a block device (only). Error detection and
* recovery is not extensive, but sufficient for most situations. It is
* assumed that the TU58 will follow the RSP (or MRSP) protocol exactly,
* very few protocol errors are checked for. It is also assumed that
* the 730 uses Modified RSP (MRSP), while the 750 may use either RSP
* or MRSP depending on whether defined(MRSP) is true or not.
* In the case of a 750 without MRSP, the only way for the CPU to
* keep up with the tu58 is to lock out virtually everything else.
* NOTE: Reading large amounts of data from the tu58 is likely
* to crash your system if you are running multiuser.
* ******FOR SINGLE USER USE ONLY*****
#define printd if(tudebug) printf
int tudebug
; /* printd */
#define NTU ((cpu == VAX_750) ? 1 : 2)
#define DNUM 01 /* mask for drive number (should match NTU) */
#define NTUBLK 512 /* number of blocks on a TU58 cassette */
#define WRV 02 /* bit in minor dev => write w. read verify */
#define NTUQ 2 /* # of blocks which can be queued up */
#define TUIPL ((cpu == VAX_750) ? 0x17 : 0x14)
#define MRSP (cpu != VAX_750)
u_char
*tu_rbptr
; /* pointer to buffer for read */
int tu_rcnt
; /* how much to read */
u_char
*tu_wbptr
; /* pointer to buffer for write */
int tu_wcnt
; /* how much to write */
int tu_state
; /* current state of tansfer operation */
int tu_flag
; /* read in progress flag */
char *tu_addr
; /* real buffer data address */
int tu_count
; /* real requested count */
int tu_serrs
; /* count of soft errors */
int tu_cerrs
; /* count of checksum errors */
int tu_herrs
; /* count of hard errors */
char tu_dopen
[2]; /* drive is open */
#define READY 0200 /* transmitter ready */
#define DONE 0200 /* receiver done */
#define IE 0100 /* interrupt enable */
#define BREAK 1 /* send break */
struct packet tucmd
; /* a command sent to the TU58 */
struct packet tudata
; /* a command or data returned from TU58 */
char *tustates
[TUS_NSTATES
] = {
"INIT1", "INIT2", "IDLE", "SENDH", "SENDD", "SENDC", "SENDR",
"SENDW", "GETH", "GETD", "GETC", "GET", "WAIT", "RCVERR", "CHKERR"
u_char tunull
[2] = { 0, 0 }; /* nulls to send for initialization */
u_char tuinit
[2] = { TUF_INITF
, TUF_INITF
}; /* inits to send */
static char tu_pcnt
[2]; /* pee/vee counters */
struct buf tutab
; /* I/O queue header */
if ((minor(dev
)&DNUM
) >= NTU
)
if (tu
.tu_dopen
[minor(dev
)&DNUM
])
timeout(tuwatch
, (caddr_t
)0, hz
);
tu
.tu_dopen
[minor(dev
)&DNUM
]++;
* If the cassette's already initialized,
* just enable interrupts and return.
if (tu
.tu_state
== TUS_IDLE
) {
* Must initialize, reset the cassette
* and wait for things to settle down.
sleep((caddr_t
)&tu
, PZERO
+1);
if (tu
.tu_state
!= TUS_IDLE
) {
tu
.tu_dopen
[minor(dev
)&DNUM
] = 0;
tu
.tu_rcnt
= tu
.tu_wcnt
= 0;
* Close the TU58, but make sure all
* outstanding i/o is complete first..
int s
, unit
= minor(dev
);
struct buf
*bp
, *last
= NULL
;
sleep(&tu_pcnt
[unit
], PRIBIO
);
* No more writes are pending, scan the
* buffer queue for oustanding reads from
for (bp
= tutab
.b_actf
; bp
; bp
= bp
->b_actf
) {
sleep((caddr_t
)last
, PRIBIO
);
tu
.tu_dopen
[unit
&DNUM
] = 0;
if (!tu
.tu_dopen
[0] && !tu
.tu_dopen
[1]) {
tu
.tu_wcnt
= sizeof (tunull
);
tucmd
.pk_mcount
= sizeof (tucmd
) - 4;
tucmd
.pk_sw
= MRSP
? TUSW_MRSP
: 0;
tuxintr(); /* start output */
* Strategy routine for block I/O
if (bp
->b_blkno
>= NTUBLK
) {
if ((bp
->b_flags
&B_READ
) == 0)
tu_pee(&tu_pcnt
[minor(bp
->b_dev
)&DNUM
]);
if (tutab
.b_actf
== NULL
)
tutab
.b_actl
->av_forw
= bp
;
if (tutab
.b_active
== NULL
)
if ((bp
= tutab
.b_actf
) == NULL
)
if (tu
.tu_state
!= TUS_IDLE
) {
tucmd
.pk_op
= bp
->b_flags
&B_READ
? TUOP_READ
: TUOP_WRITE
;
tucmd
.pk_mod
= ((bp
->b_flags
&B_READ
) == 0 && (minor(bp
->b_dev
)&WRV
)) ?
tucmd
.pk_unit
= (minor(bp
->b_dev
)&DNUM
);
tucmd
.pk_sw
= MRSP
? TUSW_MRSP
: 0;
tucmd
.pk_count
= tu
.tu_count
= bp
->b_bcount
;
tucmd
.pk_block
= bp
->b_blkno
;
tuchk(*((short *)&tucmd
), (u_short
*)&tucmd
.pk_op
,
tu
.tu_state
= bp
->b_flags
&B_READ
? TUS_SENDR
: TUS_SENDW
;
tu
.tu_addr
= bp
->b_un
.b_addr
;
tu
.tu_wbptr
= (u_char
*)&tucmd
;
tu
.tu_wcnt
= sizeof (tucmd
);
* TU58 receiver interrupt
while ((mfpr(CSTS
)&READY
) == 0)
mtpr(CSTD
, TUF_CONT
); /* ACK */
* Switch on the state of the transfer.
* Probably an overrun error,
* cannot happen if MRSP is used
mtpr(CSRS
, 0); /* flush */
printf("overrun error, transfer restarted\n"); /* DEBUG */
* If we get an unexpected "continue",
* start all over again...
tu
.tu_state
= c
== TUF_CONT
? TUS_IDLE
: TUS_INIT1
;
* Only transition from this state
* is on a "continue", so if we don't
* get it, reset the world.
case TUS_WAIT
: /* waiting for continue */
case TUF_CONT
: /* got the expected continue */
tudata
.pk_flag
= TUF_DATA
;
tudata
.pk_mcount
= MIN(128, tu
.tu_count
);
tuchk(*((short *)&tudata
), (caddr_t
)tu
.tu_addr
,
tu
.tu_wbptr
= (u_char
*)&tudata
;
case TUF_CMD
: /* sending us an END packet...error */
tu
.tu_rbptr
= (u_char
*) &tudata
;
tu
.tu_rcnt
= sizeof (tudata
) - 1;
default: /* something random...bad news */
if (c
!= TUF_CONT
&& c
!= TUF_INITF
)
* Got header, now get data; amount to
* fetch is included in packet.
if (MRSP
&& (tudata
.pk_flag
== TUF_DATA
))
tu
.tu_rbptr
= (u_char
*)tu
.tu_addr
;
tu
.tu_rcnt
= tudata
.pk_mcount
;
* Got the data, now fetch the checksum.
tu
.tu_rbptr
= (u_char
*)&tudata
.pk_chksum
;
tu
.tu_rcnt
= sizeof (tudata
.pk_chksum
);
case TUS_CHKERR
: /* from tudma only */
* The checksum has already been calculated and
* verified in the pseudo DMA routine
tuchk(*((short *)&tudata
), (u_short
*)
(tudata
.pk_flag
== TUF_DATA
?
(u_short
*) tu
.tu_addr
: (u_short
*)&tudata
.pk_op
),
if (tudata
.pk_flag
== TUF_DATA
) {
/* data packet, advance to next */
tu
.tu_addr
+= tudata
.pk_mcount
;
tu
.tu_count
-= tudata
.pk_mcount
;
tu
.tu_rbptr
= (u_char
*)&tudata
; /* next packet */
} else if (tudata
.pk_flag
==TUF_CMD
&& tudata
.pk_op
==TUOP_END
) {
/* end packet, idle and reenable transmitter */
if ((bp
= tutab
.b_actf
) == NULL
) {
printf("tu%d: no bp, active %d\n",
tudata
.pk_unit
, tutab
.b_active
);
if (tudata
.pk_mod
> 1) { /* hard error */
printf("tu%d: hard error bn%d,",
minor(bp
->b_dev
)&DNUM
, bp
->b_blkno
);
printf(" pk_mod %o\n", tudata
.pk_mod
&0377);
} else if (tudata
.pk_mod
!= 0) /* soft error */
tutab
.b_actf
= bp
->av_forw
;
bp
->b_resid
= tu
.tu_count
;
if ((bp
->b_flags
&B_READ
) == 0)
tu_vee(&tu_pcnt
[minor(bp
->b_dev
)&DNUM
]);
* Neither data nor end: data was lost
* somehow, restart the transfer
mtpr(CSRS
, 0); /* flush the rest */
printf("tu%d protocol error, state=",
printf(", op=%x, cnt=%d, block=%d\n",
tucmd
.pk_op
, tucmd
.pk_count
, tucmd
.pk_block
);
tutab
.b_actf
= bp
->av_forw
;
if ((bp
->b_flags
&B_READ
) == 0)
tu_vee(&tu_pcnt
[minor(bp
->b_dev
)&DNUM
]);
printf("tu%d: receive state error, state=",
printf(", byte=%x\n", c
& 0xff);
* TU58 transmitter interrupt
/* still stuff to send, send one byte */
while ((mfpr(CSTS
) & READY
) == 0)
mtpr(CSTD
, *tu
.tu_wbptr
++);
* Last message byte was sent out.
* Switch on state of transfer.
printf("tuxintr: state=");
* Two nulls have been sent, remove break, and send inits
tu
.tu_wcnt
= sizeof (tuinit
);
* Inits have been sent, wait for a continue msg.
* Read cmd packet sent, get ready for data
tu
.tu_rbptr
= (u_char
*)&tudata
;
mtpr(CSTS
, 0); /* disable transmitter interrupts */
* Write cmd packet sent, wait for continue
if ((mfpr(CSRS
)&IE
) == 0) {
* Header sent, send data.
tu
.tu_wbptr
= (u_char
*)tu
.tu_addr
;
tu
.tu_wcnt
= tudata
.pk_mcount
;
* Data sent, follow with checksum.
tu
.tu_wbptr
= (u_char
*)&tudata
.pk_chksum
;
tu
.tu_wcnt
= sizeof tudata
.pk_chksum
;
* Checksum sent, wait for continue.
* Updata buffer address and count.
tu
.tu_addr
+= tudata
.pk_mcount
;
tu
.tu_count
-= tudata
.pk_mcount
;
* End of transmission, get ready for end packet.
tu
.tu_rbptr
= (u_char
*)&tudata
;
tu
.tu_rcnt
= sizeof (tudata
);
* Random interrupt, probably from MRSP ACK
printd(" new tu_state=");
* Compute checksum TU58 fashion
register unsigned short *cp
;
temp
= *cp
++; /* temp, only because vax cc won't *r++ */
word
+= *(unsigned char *)cp
;
while (word
& 0xffff0000)
word
= (word
& 0xffff) + ((word
>> 16) & 0xffff);
register int word0
; /* r11 */
register char *wp
; /* r10 */
asm(" addw2 (r10)+,r11"); /* add a word to sum */
asm(" adwc $0,r11"); /* add in carry, end-around */
asm(" acbl $2,$-2,r9,loop"); /* done yet? */
asm(" blbc r9,ok"); /* odd byte count? */
asm(" movzbw (r10),r10"); /* yes, get last byte */
asm(" addw2 r10,r11"); /* add it in */
asm(" adwc $0,r11"); /* and the carry */
asm(" movl r11,r0"); /* return sum */
if (tu
.tu_flag
== 0) { /* if no read in progress - skip */
timeout(tuwatch
, (caddr_t
)0, hz
);
if (tu
.tu_flag
++ <= 40) {
timeout(tuwatch
, (caddr_t
)0, hz
);
printf("tu%d: read stalled\n", tudata
.pk_unit
);
printf("%X %X %X %X %X %X %X %X\n", tu
.tu_rbptr
, tu
.tu_rcnt
,
tu
.tu_wbptr
, tu
.tu_wcnt
, tu
.tu_state
, tu
.tu_flag
,
tu
.tu_addr
, tu
.tu_count
);
mtpr(CSRS
, IE
); /* in case we were flushing */
if (++tutab
.b_errcnt
<= 1) {
if ((bp
->b_flags
&B_READ
) == 0)
tu_vee(&tu_pcnt
[minor(bp
->b_dev
)&DNUM
]);
timeout(tuwatch
, (caddr_t
)0, hz
);
timeout(tustart
, (caddr_t
)0, hz
* 3);