/* tu.c 81/11/11 4.1 */ #if defined(VAX750) || defined(VAX7ZZ) /* * TU58 DECtape II driver * * This driver controls the console TU58s on a VAX-11/750 or VAX-11/7ZZ. * It could be easily modified for a Unibus TU58. The TU58 * is treated as a block device (only). Error detection and * recovery is almost non-existant. It is assumed that the * TU58 will follow the RSP protocol exactly, very few protocol * errors are checked for. It is assumed that the 750 uses standard * RSP while the 7ZZ uses Modified RSP (MRSP). At the time when 750's * are converted to MRSP (by replacing EPROMS in the TU58), the tests * based on MRSP can be removed. */ #define NTU ((cpu == VAX_750) ? 1 : 2) #define MRSP (cpu != VAX_750) #include "../h/param.h" #include "../h/systm.h" #include "../h/buf.h" #include "../h/conf.h" #include "../h/dir.h" #include "../h/user.h" #include "../h/mtpr.h" #include "../h/cpu.h" #define printd if(tudebug) printf #ifdef printd int tudebug; /* printd */ #endif printd #define NTUBLK 512 /* number of blocks on a TU58 cassette */ #define TUIPL ((cpu == VAX_750) ? 0x17 : 0x14) /* * Device register bits */ #define READY 0200 /* transmitter ready */ #define DONE 0200 /* receiver done */ #define IE 0100 /* interrupt enable */ #define BREAK 1 /* send break */ /* * Structure of a command packet */ struct packet { u_char pk_flag; /* indicates packet type (cmd, data, etc.) */ u_char pk_mcount; /* length of packet (bytes) */ u_char pk_op; /* operation to perform (read, write, etc.) */ u_char pk_mod; /* modifier for op or returned status */ u_char pk_unit; /* unit number */ u_char pk_sw; /* switches */ u_short pk_seq; /* sequence number, always zero */ u_short pk_count; /* requested byte count for read or write */ u_short pk_block; /* block number for read, write, or seek */ u_short pk_chksum; /* checksum, by words with end around carry */ }; struct packet tucmd; /* a command sent to the TU58 */ struct packet tudata; /* a command or data returned from TU58 */ /* * State information */ struct tu { u_char *rbptr; /* pointer to buffer for read */ int rcnt; /* how much to read */ u_char *wbptr; /* pointer to buffer for write */ int wcnt; /* how much to write */ int state; /* current state of tansfer operation */ int flag; /* read in progress flag */ char *addr; /* real buffer data address */ int count; /* real requested count */ int serrs; /* count of soft errors */ int cerrs; /* count of checksum errors */ int herrs; /* count of hard errors */ } tu; /* * States */ #define INIT1 0 /* sending nulls */ #define INIT2 1 /* sending inits */ #define IDLE 2 /* initialized, no transfer in progress */ #define SENDH 3 /* sending header */ #define SENDD 4 /* sending data */ #define SENDC 5 /* sending checksum */ #define SENDR 6 /* sending read command packet */ #define SENDW 7 /* sending write command packet */ #define GETH 8 /* reading header */ #define GETD 9 /* reading data */ #define GETC 10 /* reading checksum */ #define GET 11 /* reading an entire packet */ #define WAIT 12 /* waiting for continue */ /* * Packet Flags */ #define TUF_DATA 1 /* data packet */ #define TUF_CMD 2 /* command packet */ #define TUF_INITF 4 /* initialize */ #define TUF_CONT 020 /* continue */ #define TUF_XOFF 023 /* flow control */ /* * Op Codes */ #define TUOP_INIT 1 /* initialize */ #define TUOP_READ 2 /* read block */ #define TUOP_WRITE 3 /* write block */ #define TUOP_SEEK 5 /* seek to block */ #define TUOP_DIAGNOSE 7 /* run micro-diagnostics */ #define TUOP_END 0100 /* end packet */ /* * Switches */ #define TUSW_MRSP 010 /* use Modified RSP */ u_char tunull[2] = { 0, 0 }; /* nulls to send for initialization */ u_char tuinit[2] = { TUF_INITF, TUF_INITF }; /* inits to send */ int tutimer = 0; struct buf tutab; /* I/O queue header */ /* * Open the TU58 */ tuopen(dev, flag) { extern int tuwatch(); if (minor(dev) >= NTU) { u.u_error = ENXIO; return; } if (tutimer == 0) { tutimer++; timeout(tuwatch, (caddr_t)0, hz); } splx(TUIPL); if (tu.state != IDLE) { tureset(); sleep((caddr_t)&tu, PZERO); tutab.b_active = NULL; if (tu.state != IDLE) { /* couldn't initialize */ u.u_error = ENXIO; tu.state = INIT1; tu.rcnt = tu.wcnt = 0; mtpr(CSTS, 0); mtpr(CSRS, 0); } } else mtpr(CSRS, IE); spl0(); } /* * Close the TU58 */ tuclose(dev) { if (tutab.b_active == 0) { mtpr(CSRS, 0); tutimer = 0; } if (tu.serrs + tu.cerrs + tu.herrs != 0) { /* any errors ? */ uprintf("tu%d: %d soft errors, %d chksum errors, %d hard errors\n", minor(dev), tu.serrs, tu.cerrs, tu.herrs); tu.serrs = tu.cerrs = tu.herrs = 0; } } /* * Reset the TU58 */ tureset() { tu.state = INIT1; tu.wbptr = tunull; tu.wcnt = sizeof tunull; tucmd.pk_flag = TUF_CMD; tucmd.pk_mcount = sizeof tucmd - 4; tucmd.pk_mod = 0; tucmd.pk_seq = 0; tucmd.pk_sw = MRSP ? TUSW_MRSP : 0; tutab.b_active++; mtpr(CSRS, 0); mtpr(CSTS, IE|BREAK); tuxintr(); /* start output */ return; } /* * Strategy routine for block I/O */ tustrategy(bp) register struct buf *bp; { if (bp->b_blkno >= NTUBLK) { /* block number out of range? */ bp->b_flags |= B_ERROR; iodone(bp); return; } bp->av_forw = NULL; splx(TUIPL); if (tutab.b_actf == NULL) tutab.b_actf = bp; else tutab.b_actl->av_forw = bp; tutab.b_actl = bp; if (tutab.b_active == NULL) tustart(); spl0(); } /* * Start the transfer */ tustart() { register struct buf *bp; top: if ((bp = tutab.b_actf) == NULL) return; if (tu.state != IDLE) { tureset(); return; } tutab.b_active++; tutab.b_errcnt = 0; tucmd.pk_op = bp->b_flags&B_READ ? TUOP_READ : TUOP_WRITE; tucmd.pk_unit = minor(bp->b_dev); tucmd.pk_count = tu.count = bp->b_bcount; tucmd.pk_block = bp->b_blkno; tucmd.pk_chksum = tuchk(*((short *)&tucmd), &tucmd.pk_op, tucmd.pk_mcount); tu.state = bp->b_flags&B_READ ? SENDR : SENDW; tu.addr = bp->b_un.b_addr; tu.count = bp->b_bcount; tu.wbptr = (u_char *)&tucmd; tu.wcnt = sizeof tucmd; tuxintr(); } /* * TU58 receiver interrupt */ turintr() { register struct buf *bp; register int c; c = mfpr(CSRD)&0xff; /* get the char, clear the interrupt */ if (MRSP) { while ((mfpr(CSTS)&READY) == 0) ; mtpr(CSTD, TUF_CONT); /* ACK */ } if (tu.rcnt) { /* still waiting for data? */ *tu.rbptr++ = c; /* yup, put it there */ if (--tu.rcnt) /* decrement count, any left? */ return; /* get some more */ } /* * We got all the data we were expecting for now, * switch on the state of the transfer. */ switch(tu.state) { case INIT2: if (c == TUF_CONT) /* did we get the expected continue? */ tu.state = IDLE; else tu.state = INIT1; /* bad news... */ tu.flag = 0; wakeup((caddr_t)&tu); tustart(); break; case WAIT: /* waiting for continue */ if (c != TUF_CONT) { tu.state = INIT1; /* bad news... */ break; } tu.flag = 0; tudata.pk_flag = TUF_DATA; tudata.pk_mcount = min(128, tu.count); tudata.pk_chksum = tuchk(*((short *)&tudata), tu.addr, tudata.pk_mcount); tu.state = SENDH; tu.wbptr = (u_char *)&tudata; tu.wcnt = 2; tuxintr(); break; case GETH: /* got header, get data */ if (tudata.pk_flag == TUF_DATA) /* is it a data message? */ tu.rbptr = (u_char *)tu.addr; /* yes, put it in buffer */ tu.rcnt = tudata.pk_mcount; /* amount to get */ tu.state = GETD; break; case GETD: /* got data, get checksum */ tu.rbptr = (u_char *)&tudata.pk_chksum; tu.rcnt = sizeof tudata.pk_chksum; tu.state = GETC; break; case GET: case GETC: /* got entire packet */ #ifdef notdef if (tuchk(*((short *)&tudata), tudata.pk_flag == TUF_DATA ? tu.addr : &tudata.pk_op, tudata.pk_mcount) != tudata.pk_chksum) tu.cerrs++; #endif if (tudata.pk_flag == TUF_DATA) { /* was it a data packet? */ tu.addr += tudata.pk_mcount; /* update buf addr */ tu.count -= tudata.pk_mcount; /* and byte count */ tu.state = GETH; tu.rbptr = (u_char *)&tudata; /* next packet */ tu.rcnt = 2; } else /* was it an end packet? */ if (tudata.pk_flag == TUF_CMD && tudata.pk_op == TUOP_END) { tu.state = IDLE; /* all done reading */ tu.flag = 0; mtpr(CSTS, IE); /* reenable transmitter */ printd("ON "); if ((bp = tutab.b_actf) == NULL) { printf("tu: no bp!\n"); printf("active %d\n", tutab.b_active); tustart(); return; } if (tudata.pk_mod < 0) { /* hard error */ bp->b_flags |= B_ERROR; tu.herrs++; harderr(bp, "tu"); printf(" pk_mod %d\n", -tudata.pk_mod); } else if (tudata.pk_mod > 0) /* soft error */ tu.serrs++; tutab.b_active = NULL; tutab.b_actf = bp->av_forw; bp->b_resid = tu.count; iodone(bp); tustart(); } else { printf("neither data nor end: %o %o\n", tudata.pk_flag&0xff, tudata.pk_op&0xff); mtpr(CSRS, 0); /* flush the rest */ tu.state = INIT1; } break; case IDLE: case INIT1: break; default: /* what are we doing here??? */ if (c == TUF_INITF) { printf("TU protocol error, state %d\n", tu.state); printf("%o %d %d\n", tucmd.pk_op, tucmd.pk_count, tucmd.pk_block); tutab.b_active = NULL; if (bp = tutab.b_actf) { bp->b_flags |= B_ERROR; tutab.b_actf = bp->av_forw; iodone(bp); } tu.state = INIT1; } else { printf("TU receive state error %d %o\n", tu.state, c); /* tu.state = INIT1; */ wakeup((caddr_t)&tu); } } } /* * TU58 transmitter interrupt */ tuxintr() { top: if (tu.wcnt) { /* still stuff to send out? */ while ((mfpr(CSTS) & READY) == 0) ; mtpr(CSTD, *tu.wbptr++); /* yup, send another byte */ tu.wcnt--; /* decrement count */ return; } /* * Last message byte was sent out. * Switch on state of transfer. */ printd("tuxintr: state %d\n", tu.state); switch(tu.state) { case INIT1: /* two nulls sent, remove break, send inits */ mtpr(CSTS, IE); printd("ON2 "); tu.state = INIT2; tu.wbptr = tuinit; tu.wcnt = sizeof tuinit; goto top; case INIT2: /* inits sent, wait for continue */ mfpr(CSRD); mtpr(CSRS, IE); tu.flag = 1; break; case IDLE: /* stray interrupt? */ break; case SENDR: /* read cmd packet sent, get ready for data */ tu.state = GETH; tu.rbptr = (u_char *)&tudata; tu.rcnt = 2; tu.flag = 1; mtpr(CSTS, 0); /* disable transmitter interrupts */ printd("OFF "); break; case SENDW: /* write cmd packet sent, wait for continue */ tu.state = WAIT; tu.flag = 1; if ((mfpr(CSRS)&IE) == 0) { printf("NO IE\n"); mtpr(CSRS, IE); } break; case SENDH: /* header sent, send data */ tu.state = SENDD; tu.wbptr = (u_char *)tu.addr; tu.wcnt = tudata.pk_mcount; goto top; case SENDD: /* data sent, send checksum */ tu.state = SENDC; tu.wbptr = (u_char *)&tudata.pk_chksum; tu.wcnt = sizeof tudata.pk_chksum; goto top; case SENDC: /* checksum sent, wait for continue */ tu.addr += tudata.pk_mcount; /* update buffer address */ tu.count -= tudata.pk_mcount; /* and count */ if (tu.count == 0) { /* all done? */ tu.state = GET; /* set up to get end packet */ tu.rbptr = (u_char *)&tudata; tu.rcnt = sizeof tudata; tu.flag = 1; mtpr(CSTS, 0); printd("OFF2 "); } else { tu.state = WAIT; /* wait for continue */ tu.flag = 1; } break; default: /* random interrupt, probably from MRSP ACK */ break; } printd(" new state %d\n", tu.state); } /* * Compute checksum TU58 fashion * * *** WARNING *** * This procedure is not in C because * it has to be fast and it is hard to * do add-carry in C. Sorry. */ tuchk(word0, wp, n) register int word0; /* r11 */ register char *wp; /* r10 */ register int n; /* r9 */ { asm("loop:"); 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("ok:"); asm(" movl r11,r0"); /* return sum */ } tuwatch() { register int s; register struct buf *bp; if (tutimer == 0) { tu.flag = 0; return; } if (tu.flag) tu.flag++; if (tu.flag > 40) { printf("tu: read stalled\n"); printf("%X %X %X %X %X %X %X %X\n", tu.rbptr, tu.rcnt, tu.wbptr, tu.wcnt, tu.state, tu.flag, tu.addr, tu.count); tu.flag = 0; s = splx(TUIPL); mfpr(CSRD); mtpr(CSRS, IE); /* in case we were flushing */ mtpr(CSTS, IE); tu.state = IDLE; if (tutab.b_active) { if (++tutab.b_errcnt > 1) { if (bp = tutab.b_actf) { bp->b_flags |= B_ERROR; iodone(bp); } } else tustart(); } else wakeup((caddr_t)&tu); splx(s); } timeout(tuwatch, (caddr_t)0, hz); } #endif