* TU58 DECtape II/DL11 device driver
* The TU58 is treated as a block device (only). Error detection and
* recovery is not very extensive, but sufficient to handle the most
* common errors. It is assumed that the TU58 will follow the RSP
* protocol exactly, very few protocol errors are checked for.
* To reduce interrupt latency, `options UUDMA' should be specified
* in the config file to make sure the `pseudo-DMA' code in locore.s
* will be compiled into the system. Otherwise overrun errors will
* occur frequently (these errors are not reported).
* - Add ioctl code to wind/rewind cassette
#include "../machine/pte.h"
#include "../vax/nexus.h"
#include "../vaxuba/ubavar.h"
#include "../vaxuba/ubareg.h"
#include "../vaxuba/uureg.h"
#define NTUBLK 512 /* number of blocks on a TU58 cassette */
#define WRV 01 /* bit in minor dev => write w. read verify */
#define NDPC 02 /* drives per controller */
#define NUX NDPC * NUU /* number of drives */
#define NUUQ 02 /* # of block which can be queued up */
#define UMASK 01 /* unit number mask */
#define UUIPL 0x14 /* ipl level to use */
struct packet uucmd
[NUU
]; /* a command sent to the TU58 */
struct packet uudata
[NUU
]; /* a command or data returned from TU58 */
struct buf uitab
[NUU
]; /* buffer queue headers */
* Driver soft carrier structure
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 */
#if defined(VAX750) || defined(VAX730)
char *tustates
[TUS_NSTATES
] = {
"INIT1", "INIT2", "IDLE", "SENDH", "SENDD", "SENDC", "SENDR",
"SENDW", "GETH", "GETD", "GETC", "GET", "WAIT", "RCVERR", "CHKERR"
#define UNIT(dev) (minor(dev)>>1)
u_char uunull
[2] = { 0, 0 }; /* nulls to send for initialization */
u_char uuinit
[2] = { TUF_INITF
, TUF_INITF
}; /* inits to send */
struct uba_device
*uudinfo
[NUU
];
int uuprobe(), uuattach(), uurintr(), uuxintr(), uuwatch();
u_short uustd
[] = { 0176500 };
struct uba_driver uudriver
=
{ uuprobe
, 0, uuattach
, 0, uustd
, "uu", uudinfo
};
static char uu_pcnt
[NUX
]; /* pee/vee counters, one per drive */
register int br
, cvec
; /* value result */
struct uudevice
*uuaddr
= (struct uudevice
*)reg
;
br
= 0; cvec
= br
; br
= cvec
;
cvec
-= 4; /* since we are using the xmitter intrpt */
return(sizeof (*uuaddr
));
register struct uba_device
*ui
;
register struct uba_device
*ui
;
register struct uu_softc
*uuc
;
register struct uudevice
*uuaddr
;
int ctlr
, unit
= UNIT(dev
), s
;
if (unit
>= NUX
|| (ui
= uudinfo
[ctlr
]) == 0 || ui
->ui_alive
== 0)
if (uuc
->tu_dopen
[unit
&UMASK
])
timeout(uuwatch
, (caddr_t
)0, hz
);
uuc
->tu_dopen
[unit
&UMASK
]++;
uuaddr
= (struct uudevice
*)ui
->ui_addr
;
* If the other device on this controller
* is already active, no need to initialize
if (uuc
->tu_dopen
[0] && uuc
->tu_dopen
[1])
* If the unit already initialized,
* just enable interrupts and return.
if (uuc
->tu_state
== TUS_IDLE
) {
* Must initialize, reset the cassette
* and wait for things to settle down.
sleep((caddr_t
)uuc
, PZERO
+1);
uitab
[ctlr
].b_active
= NULL
;
if (uuc
->tu_state
!= TUS_IDLE
) {
uuc
->tu_state
= TUS_INIT1
;
uuc
->tu_dopen
[unit
&UMASK
] = 0;
uuc
->tu_rcnt
= uuc
->tu_wcnt
= 0;
* Wait for all outstanding IO on this drive
* complete, before closing. If both drives on
* this controller are idle, mark the controller
register struct uu_softc
*uuc
= &uu_softc
[unit
/NDPC
];
struct buf
*bp
, *last
= NULL
;
struct uudevice
*uuaddr
= (struct uudevice
*)uudinfo
[unit
/NDPC
]->ui_addr
;
sleep(&uu_pcnt
[unit
], PRIBIO
);
* No more writes are pending, scan the
* buffer queue for oustanding reads from
for (bp
= uitab
[unit
/NDPC
].b_actf
; bp
; bp
= bp
->b_actf
) {
sleep((caddr_t
)last
, PRIBIO
);
uuc
->tu_dopen
[unit
&UMASK
] = 0;
if (!uuc
->tu_dopen
[0] && !uuc
->tu_dopen
[1]) {
register struct uu_softc
*uuc
= &uu_softc
[ctlr
];
register struct packet
*cmd
= &uucmd
[ctlr
];
struct uba_device
*ui
= uudinfo
[ctlr
];
register struct uudevice
*uuaddr
= (struct uudevice
*)ui
->ui_addr
;
uuc
->tu_state
= TUS_INIT1
;
uuc
->tu_wcnt
= sizeof (uunull
);
cmd
->pk_mcount
= sizeof (*cmd
) - 4;
uuaddr
->tcs
= UUCS_INTR
| UUCS_BREAK
;
uuxintr(ctlr
); /* start output */
* Strategy routine for block I/O
register struct buf
*uutab
;
int s
, unit
= UNIT(bp
->b_dev
);
if ((unit
> NUX
) || (bp
->b_blkno
>= NTUBLK
))
if (ui
== 0 || ui
->ui_alive
== 0)
uutab
= &uitab
[unit
/NDPC
]; /* one request queue per controller */
if ((bp
->b_flags
&B_READ
) == 0)
if (uutab
->b_actf
== NULL
)
uutab
->b_actl
->b_actf
= bp
;
if (uutab
->b_active
== 0)
register struct uba_device
*ui
;
register struct uu_softc
*uuc
;
int ctlr
= ui
->ui_unit
, s
;
if ((bp
= uitab
[ctlr
].b_actf
) == NULL
)
if (uuc
->tu_state
!= TUS_IDLE
) {
uitab
[ctlr
].b_errcnt
= 0;
uuc
->tu_addr
= bp
->b_un
.b_addr
;
uuc
->tu_count
= cmd
->pk_count
= bp
->b_bcount
;
cmd
->pk_block
= bp
->b_blkno
;
if (bp
->b_flags
&B_READ
) {
uuc
->tu_state
= TUS_SENDR
;
cmd
->pk_mod
= minor(bp
->b_dev
)&WRV
? TUMD_WRV
: 0;
uuc
->tu_state
= TUS_SENDW
;
cmd
->pk_unit
= UNIT(bp
->b_dev
)&UMASK
;
tuchk(*((short *)cmd
), (u_short
*)&cmd
->pk_op
, (int)cmd
->pk_mcount
);
uuc
->tu_wbptr
= (u_char
*)cmd
;
uuc
->tu_wcnt
= sizeof (*cmd
);
* TU58 receiver interrupt, handles whatever condition the
* pseudo DMA routine in locore is unable to handle,
* or, if UUDMA is undefined, handle all receiver interrupt
struct uba_device
*ui
= uudinfo
[ctlr
];
register struct uu_softc
*uuc
= &uu_softc
[ctlr
];
register struct uudevice
*uuaddr
= (struct uudevice
*)ui
->ui_addr
;
register struct buf
*uutab
= &uitab
[ctlr
];
struct packet
*data
, *cmd
;
uuc
->tu_state
= TUS_RCVERR
;
* Switch on the tu_state of the transfer.
* A data error occured in uudma
* (either overrun or break)
if ((c
& UURDB_ORUN
) == 0)
printf("uu%d: break received, transfer restarted\n",
printf("uu%d: data overrun, recovered\n",
* If we get an unexpected "continue",
* start all over again...
uuc
->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 */
data
->pk_flag
= TUF_DATA
;
data
->pk_mcount
= MIN(128, uuc
->tu_count
);
tuchk(*((short *)data
), (caddr_t
)uuc
->tu_addr
,
uuc
->tu_state
= TUS_SENDH
;
uuc
->tu_wbptr
= (u_char
*)data
;
case TUF_CMD
: /* sending us an END packet...error */
uuc
->tu_rbptr
= (u_char
*)data
;
uuc
->tu_rcnt
= sizeof (*data
) - 1;
*uuc
->tu_rbptr
++ = c
& UUDB_DMASK
;
default: /* something random...bad news */
uuc
->tu_state
= TUS_INIT1
;
if (c
!= TUF_CONT
&& c
!= TUF_INITF
)
* Got header, now get data; amount to
* fetch is included in packet.
* (data packets are handled entirely
if (data
->pk_flag
== TUF_DATA
)
uuc
->tu_rbptr
= (u_char
*)uuc
->tu_addr
;
uuc
->tu_rcnt
= data
->pk_mcount
;
uuc
->tu_state
= TUS_GETD
;
* Got the data, now fetch the checksum.
uuc
->tu_rbptr
= (u_char
*)&data
->pk_chksum
;
uuc
->tu_rcnt
= sizeof (data
->pk_chksum
);
uuc
->tu_state
= TUS_GETC
;
tuchk(*((short *)data
), (u_short
*)
(data
->pk_flag
== TUF_DATA
?
(u_short
*) uuc
->tu_addr
: (u_short
*)&data
->pk_op
),
if (data
->pk_flag
== TUF_DATA
) {
/* data packet, advance to next */
uuc
->tu_addr
+= data
->pk_mcount
;
uuc
->tu_count
-= data
->pk_mcount
;
uuc
->tu_state
= TUS_GETH
;
uuc
->tu_rbptr
= (u_char
*)data
; /* next packet */
} else if (data
->pk_flag
==TUF_CMD
&& data
->pk_op
==TUOP_END
) {
/* end packet, idle and reenable transmitter */
uuc
->tu_state
= TUS_IDLE
;
if ((bp
= uutab
->b_actf
) == NULL
) {
printf("uu%d: no bp, active %d\n",
data
->pk_unit
, uitab
[ctlr
].b_active
);
if (data
->pk_mod
> 1) { /* hard error */
printf("uu%d: hard error bn%d,", unit
,
printf(" pk_mod 0%o\n", data
->pk_mod
&0xff);
} else if (data
->pk_mod
) /* soft error */
uutab
->b_actf
= bp
->b_actf
;
bp
->b_resid
= uuc
->tu_count
;
if ((bp
->b_flags
&B_READ
) == 0)
* Neither data nor end: data was lost
* somehow, flush and restart the transfer.
printf("uu%d protocol error, state=", data
->pk_unit
);
printstate(uuc
->tu_state
);
printf(", op=%x, cnt=%d, block=%d\n",
cmd
->pk_op
, cmd
->pk_count
, cmd
->pk_block
);
if (bp
= uutab
->b_actf
) {
uutab
->b_actf
= bp
->b_actf
;
if ((bp
->b_flags
&B_READ
) == 0)
uuc
->tu_state
= TUS_INIT1
;
printf("uu%d receive state error, state=",
printstate(uuc
->tu_state
);
printf(", byte=%x\n", c
& 0xff);
uuc
->tu_state
= TUS_INIT1
;
* TU58 transmitter interrupt
register struct uu_softc
*uuc
= &uu_softc
[ctlr
];
register struct uudevice
*uuaddr
;
register struct packet
*data
;
struct uba_device
*ui
= uudinfo
[ctlr
];
uuaddr
= (struct uudevice
*) ui
->ui_addr
;
/* still stuff to send, send one byte */
while ((uuaddr
->tcs
& UUCS_READY
) == 0)
uuaddr
->tdb
= *uuc
->tu_wbptr
++;
* Last message byte was sent out.
* Switch on tu_state of transfer.
* Two nulls have been sent, remove break, and send inits
uuc
->tu_state
= TUS_INIT2
;
uuc
->tu_wcnt
= sizeof (uuinit
);
* Inits have been sent, wait for a continue msg.
c
= uuaddr
->rdb
; /* prevent overrun error */
* Read cmd packet sent, get ready for data
uuc
->tu_state
= TUS_GETH
;
uuc
->tu_rbptr
= (u_char
*)data
;
* Write cmd packet sent, wait for continue
uuc
->tu_state
= TUS_WAIT
;
if ((uuaddr
->rcs
&UUCS_INTR
) == 0) {
* Header sent, send data.
uuc
->tu_state
= TUS_SENDD
;
uuc
->tu_wbptr
= (u_char
*)uuc
->tu_addr
;
uuc
->tu_wcnt
= data
->pk_mcount
;
* Data sent, follow with checksum.
uuc
->tu_state
= TUS_SENDC
;
uuc
->tu_wbptr
= (u_char
*)&data
->pk_chksum
;
* Checksum sent, wait for continue.
* Update buffer address and count.
uuc
->tu_addr
+= data
->pk_mcount
;
uuc
->tu_count
-= data
->pk_mcount
;
uuc
->tu_state
= TUS_WAIT
;
* End of transmission, get ready for end packet.
uuc
->tu_rbptr
= (u_char
*)data
;
uuc
->tu_rcnt
= sizeof (*data
);
case TUS_IDLE
: /* stray interrupt? */
register struct uu_softc
*uuc
;
register struct uudevice
*uuaddr
;
for (ctlr
=0; ctlr
<NUU
; ctlr
++) {
if (uuc
->tu_dopen
[0] || uuc
->tu_dopen
[1])
* If no read is in progress
uuaddr
= (struct uudevice
*)ui
->ui_addr
;
printf("uu%d: read stalled\n", uudata
[ctlr
].pk_unit
);
printf("%X %X %X %X %X %X %X\n", uuc
->tu_rbptr
, uuc
->tu_rcnt
,
uuc
->tu_wbptr
, uuc
->tu_wcnt
, uuc
->tu_state
, uuc
->tu_addr
,
i
= uuaddr
->rdb
; /* dummy */
uuaddr
->rcs
= UUCS_INTR
; /* in case we were flushing */
uuc
->tu_state
= TUS_IDLE
;
if (++uutab
->b_errcnt
<= 1) {
if (bp
= uutab
->b_actf
) {
if ((bp
->b_flags
&B_READ
) == 0)
tu_vee(&uu_pcnt
[UNIT(bp
->b_dev
)]);
timeout(uuwatch
, (caddr_t
)0, hz
);
#if !defined(VAX750) && !defined(VAX730)
* 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 */
* Make sure this incredibly slow device
* doesn't eat up all the buffers in the
* system by putting the requesting process
* (remember: this device is 'single-user')
* to sleep if the write-behind queue grows
uuioctl(dev
, cmd
, data
, flag
)
* add code to wind/rewind cassette here
timeout(uustart
, (caddr_t
)ui
, hz
* 3);