V10/lsys/md/ctapecomet.c
/*
* driver for comet (and nebula?) console cassette
*/
#include "sys/param.h"
#include "sys/user.h"
#include "sys/buf.h"
#include "sys/conf.h"
/*
* registers -- all IPRs
*/
#define CSRS 0x1c /* receive status */
#define CSRD 0x1d /* receive data */
#define CSTS 0x1e /* transmit status */
#define CSTD 0x1f /* transmit data */
/*
* bits in status registers
*/
#define DONE 0200
#define IE 0100
#define BRK 01 /* transmit a break */
/*
* packet types
*/
#define DATA 01 /* data packet */
#define CTL 02 /* command */
#define INIT 04 /* init handshake */
#define CONT 020 /* continue */
#define STOP 023 /* stop (flow control) */
/*
* opcodes in control packets
*/
#define RESET 01 /* reset controller */
#define READ 02 /* read a block */
#define WRITE 03 /* write a block */
#define END 0100 /* response from tu58 */
/*
* switches
*/
#define MRSP 010
/*
* status codes in END packet
*/
#define SOK 0
#define SSOFT 1 /* ok but had to retry */
#define SEOT (-2) /* ran off end of tape */
#define SBBLK (-55) /* block number too big */
/*
* misc sizes
*/
#define TBSIZE 512 /* size of a block on the tape */
#define PMAX sizeof(struct dpkt) /* biggest packet size */
#define DMAX 128 /* max bytes of data per packet */
#define CCNT 10 /* count for CTL packets */
/*
* prototype control packet
*/
struct cpkt {
char cp_flag; /* CTL */
unsigned char cp_cnt; /* count == 012 */
char cp_op; /* op code */
char cp_mod; /* modifiers */
char cp_unit; /* unit number */
char cp_sw; /* switches (eg MRSP) */
u_short cp_seq; /* sequence number (always 0) */
u_short cp_bcnt; /* byte count */
u_short cp_bno; /* desired block number */
};
#define ce_sts cp_mod /* for END packet, success code */
#define ce_xsts cp_bno /* extended status */
/*
* data packet
*/
struct dpkt {
char cp_flag; /* DATA */
unsigned char cp_cnt; /* count of data bytes following */
char cd_data[DMAX];
};
typedef union packet {
struct cpkt C;
struct dpkt D;
char B[PMAX];
u_short W[PMAX / sizeof(u_short)];
} PACKET;
/*
* quick and dirty pseudo-clist
*/
#define CBDAT 512
#define QHI 400
#define QLO 200
struct cbuf {
int cb_cc;
unsigned char *cb_rptr;
unsigned char *cb_wptr;
unsigned char cb_data[CBDAT];
};
static struct cbuf ctuin, ctuout;
static int ctuflag;
int ctuopen(), ctuclose(), cturead(), ctuwrite();
struct cdevsw ctucdev = cdinit(ctuopen, ctuclose, cturead, ctuwrite, nodev);
#define TBUSY 01 /* output busy */
#define OPEN 02 /* device already open */
#define WAIT 04 /* someone waiting for input */
#define OSLEEP 010 /* someone waiting for output to drain */
#define DEBUG 0100000 /* debugging flag */
#define IPRI 28
#define OPRI 29
#define hibyte(x) (((u_char *)&x)[1])
#define lobyte(x) (((u_char *)&x)[0])
#define hiword(x) (((u_short *)&x)[1])
#define loword(x) (((u_short *)&x)[0])
ctuopen(dev, mode)
{
if (ctuflag & OPEN) {
u.u_error = EBUSY;
return;
}
ctuflag |= OPEN;
ctuinit();
}
ctuclose(dev)
{
mtpr(CSTS, 0);
mtpr(CSRS, 0);
ctuflag &=~ OPEN;
}
cturead(dev)
{
PACKET pkt;
register PACKET *pk = &pkt;
if (u.u_count & 01)
u.u_count--; /* tu58 prefers even byte count */
pk->C.cp_flag = CTL;
pk->C.cp_cnt = CCNT;
pk->C.cp_op = READ;
pk->C.cp_mod = 0;
pk->C.cp_unit = 0;
pk->C.cp_sw = MRSP;
pk->C.cp_seq = 0;
pk->C.cp_bcnt = u.u_count;
pk->C.cp_bno = Ltol(u.u_offset) / TBSIZE;
ctuput(pk);
for (;;) {
if (ctuget(pk) == 0) {
u.u_error = EIO;
break;
}
if (u.u_count == 0)
break;
if (pk->C.cp_flag != DATA)
break;
iomove(pk->D.cd_data, pk->C.cp_cnt, B_READ);
if (u.u_error)
break;
}
if (pk->C.cp_flag != CTL || pk->C.cp_op != END)
u.u_error = EIO;
if (u.u_error) {
ctuinit();
return;
}
if (pk->C.ce_sts != SOK
&& pk->C.ce_sts != SSOFT
&& pk->C.ce_sts != SEOT /* operation overlapped end of tape */
&& pk->C.ce_sts != SBBLK) { /* operation past end of block */
u.u_error = EIO;
printf("err on TU58: %o %o\n", pk->C.ce_sts & 0377, pk->C.ce_xsts);
return;
}
}
ctuwrite(dev)
{
PACKET pkt;
register PACKET *pk = &pkt;
if (u.u_count & 01)
u.u_count--;
pk->C.cp_flag = CTL;
pk->C.cp_cnt = CCNT;
pk->C.cp_op = WRITE;
pk->C.cp_mod = 0;
pk->C.cp_unit = 0;
pk->C.cp_sw = MRSP;
pk->C.cp_seq = 0;
pk->C.cp_bcnt = u.u_count;
pk->C.cp_bno = Ltol(u.u_offset) / TBSIZE;
ctuput(pk);
for (;;) {
if (ctuget(pk) == 0) {
u.u_error = EIO;
break;
}
if (pk->C.cp_flag != CONT)
break;
if (u.u_count == 0)
break;
pk->C.cp_cnt = min(u.u_count, DMAX);
iomove(pk->D.cd_data, pk->C.cp_cnt, B_WRITE);
if (u.u_error)
break;
pk->C.cp_flag = DATA;
ctuput(pk);
}
if (pk->C.cp_flag != CTL || pk->C.cp_op != END)
u.u_error = EIO;
if (u.u_error) {
ctuinit();
return;
}
if (pk->C.ce_sts != SOK
&& pk->C.ce_sts != SSOFT
&& pk->C.ce_sts != SEOT) {
u.u_error = EIO;
printf("err on TU58: %o %o\n", pk->C.ce_sts & 0377, pk->C.ce_xsts);
return;
}
}
/*
* send a packet to the tu58
*/
ctuput(pk)
register PACKET *pk;
{
register u_short *wp;
register char *cp;
register int n;
long sum;
if (ctusbc(pk->C.cp_flag)) {
ctputc(pk->C.cp_flag, &ctuout);
ctustart();
return;
}
while (ctuout.cb_cc >= QHI) {
ctuflag |= OSLEEP;
sleep((caddr_t)&ctuout, OPRI);
}
wp = pk->W;
sum = 0;
n = (pk->C.cp_cnt / sizeof(u_short)) + 1;
while (--n >= 0) {
sum += *wp++;
if (hiword(sum)) { /* wrap carry around */
sum++;
hiword(sum) = 0;
}
}
cp = pk->B;
n = pk->C.cp_cnt + 2;
while (--n >= 0)
ctputc(*cp++, &ctuout);
ctputc(lobyte(sum), &ctuout);
ctputc(hibyte(sum), &ctuout);
ctustart();
}
/*
* retrieve a packet from the tu58
* returns zero if packet is illegal (eg bad checksum)
* if no packet, wait for one
*/
ctuget(pk)
register PACKET *pk;
{
register u_short *wp;
register char *cp;
register int n;
long sum;
u_short xsum;
pk->C.cp_flag = ctuchr();
if (ctusbc(pk->C.cp_flag))
return (1);
pk->C.cp_cnt = ctuchr();
cp = &pk->B[2];
n = pk->C.cp_cnt;
if (n > PMAX)
return (0);
while (--n >= 0)
*cp++ = ctuchr();
lobyte(xsum) = ctuchr();
hibyte(xsum) = ctuchr();
wp = pk->W;
n = (pk->C.cp_cnt / sizeof(u_short)) + 1;
sum = 0;
while (--n >= 0) {
sum += *wp++;
if (hiword(sum)) { /* wrap carry around. ugh. */
sum++;
hiword(sum) = 0;
}
}
if (ctuflag & DEBUG)
printf("ctu %x\n", pk->C.cp_flag);
if (sum != xsum) {
printf("ctu: sum %x is %x\n", xsum, sum);
return (0);
}
return (1);
}
/*
* is this type a single byte packet?
*/
ctusbc(type)
register int type;
{
switch (type) {
case DATA:
case CTL:
return (0);
default:
return (1);
}
}
/*
* hard reset
* called on open or error
*/
ctuinit()
{
mtpr(CSRS, 0);
mtpr(CSTS, BRK);
DELAY(250000);
mtpr(CSTS, 0);
ctflushq(&ctuin);
ctflushq(&ctuout);
mtpr(CSRS, IE);
ctputc(INIT, &ctuout);
ctputc(INIT, &ctuout);
ctustart();
while (ctuchr() != CONT)
;
}
/*
* wait for one character from tu58
*/
ctuchr()
{
register int s;
s = spl7();
while (ctuin.cb_cc == 0) {
ctuflag |= WAIT;
sleep((caddr_t)&ctuin, IPRI);
}
splx(s);
return (ctgetc(&ctuin));
}
/*
* start output from queue
*/
ctustart()
{
register int c;
register int s;
s = spl7();
if (ctuflag & TBUSY)
return;
if ((c = ctgetc(&ctuout)) >= 0) {
ctuflag |= TBUSY;
mtpr(CSTD, c);
mtpr(CSTS, IE);
}
if (ctuflag & OSLEEP && ctuout.cb_cc < QLO) {
wakeup((caddr_t)&ctuout);
ctuflag &=~ OSLEEP;
}
splx(s);
}
/*
* transmitter interrupt
*/
ctu1int(dev)
{
ctuflag &=~ TBUSY;
ctustart();
}
/*
* receiver interrupt
*/
ctu0int(dev)
{
static char cont = CONT;
while (mfpr(CSRS) & DONE) {
ctputc(mfpr(CSRD), &ctuin);
ctuput((PACKET *)&cont);
if (ctuin.cb_cc > QHI) {
printf("ctu input overrun\n");
ctflushq(&ctuin);
}
}
if (ctuflag & WAIT) {
ctuflag &=~ WAIT;
wakeup((caddr_t)&ctuin);
}
}
/*
* fake clist routines
*/
ctflushq(q)
register struct cbuf *q;
{
register int s;
s = spl7();
q->cb_cc = 0;
q->cb_rptr = q->cb_wptr = q->cb_data;
splx(s);
}
int
ctgetc(q)
register struct cbuf *q;
{
register int s;
register int c;
s = spl7();
if (q->cb_cc <= 0)
c = -1;
else {
q->cb_cc--;
c = *q->cb_rptr++;
if (q->cb_rptr >= &q->cb_data[CBDAT])
q->cb_rptr = q->cb_data;
}
splx(s);
return (c);
}
ctputc(c, q)
char c;
register struct cbuf *q;
{
register int s;
s = spl7();
q->cb_cc++;
*q->cb_wptr++ = c;
if (q->cb_wptr >= &q->cb_data[CBDAT])
q->cb_wptr = q->cb_data;
splx(s);
}