+/* cy.c 7.1 86/01/12 */
+/* cy.c Tahoe version Mar 1983. */
+
+/*
+ * Cypher tape driver. Stand alone version.
+ *
+ */
+#include "../machine/pte.h"
+#include "../machine/mtpr.h"
+
+#include "param.h"
+#include "inode.h"
+#include "fs.h"
+
+#include "saio.h"
+#include "cyvar.h"
+
+long cystd[] = { 0xf4000, 0 };
+
+struct scp /* SYSTEM CONFIGURATION POINTER */
+{
+ char sysbus ; /* width of system buss 0=8;1=16 */
+ char nu1 ;
+ char pt_scb[4] ; /* pointer to ->SYSTEM CONFIGURATION BLOCK */
+};
+
+struct scp * SCP; /* absolute address - jumpered on the controller */
+ /* set to 0xC06 for Tahoe */
+
+struct scb /* SYSTEM CONFIGUREATION BLOCK */
+{
+ char sysblk[1] ; /* 0x03 fixed value code */
+ char nu2[1] ;
+ char pt_ccb[4] ; /* pointer to ->CHANNEL CONTROL BLOCK */
+}scb;
+
+struct ccb /* CHANNEL CONTROL BLOCK */
+{
+ char ccw[1] ; /* 0x11 normal; 0x09 clear non_vect interrupt */
+ char gate[1] ; /* This is "the" GATE */
+ char pt_tpb[4] ; /* pointer to ->TAPE OPERATION BLOCK or MOVE BLOCK */
+}ccb;
+
+struct tpb /* TAPE OPERATIONS PARAMETER BLOCK */
+{
+ long cmd ; /* COMMAND (input) */
+ char control[2] ; /* CONTROL (input) */
+ short count ; /* RETURN COUNT (output) */
+ short size ; /* BUFFER SIZE (input/output) */
+ short rec_over ; /* RECORDS/OVERRUN (input/output) */
+ char pt_data[4] ; /* pointer to ->SOURCE/DEST (input) */
+ char status[2] ; /* STATUS (output) */
+ char pt_link[4] ; /* pointer to ->INTERRUPT/PARAMETER BLOCK (input) */
+} tpb;
+
+struct tpb cycool /* tape parameter block to clear interrupts */
+= {
+ 0L, /* command */
+ 0,0, /* control */
+ 0, /* count */
+ 0, /* size */
+ 0, /* rec_over */
+ 0,0,0,0, /* pt_data */
+ 0,0, /* status */
+ 0,0,0,0 /* pt_link */
+} ;
+int cyblksiz = 1024; /* foreign tape size as found in open routine */
+long cyblock; /* next block number for i/o */
+/*
+ * Reset the controller.
+ */
+cyopen(io)
+ register struct iob *io;
+{
+ register ctlradr;
+
+ ctlradr = cystd[0] + (int)IOBASE;
+ SCP = (struct scp *)0xc06; /* absolute - for setup */
+ TM_RESET(ctlradr,0xff); /* reset the controller */
+ /*
+ * Initialize the system configuration pointer
+ */
+ SCP->sysbus = 1; /* system width = 16 bits. */
+ /* initialize the pointer to the system configuration block */
+ set_pointer((long)&scb.sysblk[0],(char *)SCP->pt_scb);
+ /*
+ * Initialize the system configuration block.
+ */
+ scb.sysblk[0] = 0x3; /* fixed value */
+ /* initialize the pointer to the channel control block */
+ set_pointer((long)&ccb.ccw[0],(char *)scb.pt_ccb);
+ /*
+ * Initialize the channel control block.
+ */
+ ccb.ccw[0] = 0x11; /* normal interrupts */
+ /* initialize the pointer to the tape parameter block */
+ set_pointer((long)&tpb,(char *)ccb.pt_tpb);
+ /*
+ * set the command to be NO_OP.
+ */
+ tpb.cmd = NO_OP;
+ tpb.control[0] = CW_BL; /* TPB not used on first attention */
+ tpb.control[1] = CW_16bits;
+ ccb.gate[0] = GATE_CLOSED;
+ TM_ATTENTION(ctlradr, 0xff); /* execute! */
+ cywait(10*1000);
+ /*
+ * set the command to be CONFIGURE.
+ */
+ tpb.cmd = CONFIG;
+ tpb.control[0] = CW_BL; /* NO interrupt on completion */
+ tpb.control[1] = CW_16bits;
+ tpb.status[0] = tpb.status[1] = 0;
+ ccb.gate[0] = GATE_CLOSED;
+ TM_ATTENTION(ctlradr, 0xff); /* execute! */
+ cywait(10*1000);
+ uncache (&tpb.status[1]) ;
+ if (tpb.status[1] & CS_ERm) {
+ printf("Cypher initialization error!\n");
+ cy_decode_error(tpb.status[1]&CS_ERm);
+ _stop("");
+ }
+ if(cycmd(io,REWD_TA) == -1)
+ _stop("Rewind failed!\n");
+ while(io->i_boff > 0) {
+ if(cycmd(io,SPAC_FM) == -1)
+ _stop("cy: seek failure!\n");
+ io->i_boff--;
+ }
+#ifdef NOBLOCK
+ if (io->i_flgs & F_READ) {
+ if((cyblksiz = cycmd(io,READ_FO)) == -1)
+ _stop("Read foriegn tape failed!\n");
+ if(cycmd(io,REWD_TA) == -1)
+ _stop("Rewind after read failed\n");
+ }
+#endif
+}
+
+/* if tape was open for writing write a file mark */
+cyclose(io)
+ register struct iob *io;
+{
+ if (io->i_flgs & F_WRITE) cycmd(io,WRITE_FMARK);
+ cycmd(io,REWD_TA);
+ cyblock = 0;
+}
+
+cystrategy(io,func)
+ register struct iob *io;
+ register long func;
+{
+
+#ifndef NOBLOCK
+ if ((func != SPACE) && (func != REWD_TA) && (io->i_bn != cyblock)) {
+ cycmd(io,SPACE);
+ tpb.rec_over = 0;
+ }
+ if(func==READ || func==WRITE) {
+ struct iob liob;
+ register struct iob *lio = &liob;
+ register count;
+
+ liob = *io;
+ while(lio->i_cc > 0) {
+ if((count = cycmd(lio, func)) == 0)
+ return(-1);
+ lio->i_cc -= count;
+ lio->i_ma += count;
+ }
+ return(io->i_cc);
+ }
+#endif
+ return(cycmd(io, func));
+}
+
+cycmd(io,func)
+ register struct iob *io;
+ long func;
+{
+ register ctlradr;
+ short j;
+
+ ctlradr = cystd[0] + (int)IOBASE;
+ cywait(9000);
+ if (func == READ) func = READ_TA;
+ else if (func == WRITE) func = WRIT_TA;
+ else if (func == WRITE_FMARK) func = WRIT_FM;
+ tpb.cmd = func;
+ uncache(&ccb.gate[0]);
+ while(ccb.gate[0] == GATE_CLOSED)
+ uncache(&ccb.gate[0]);
+ ccb.gate[0] = GATE_CLOSED;
+ tpb.control[0] = CW_BL;
+ tpb.control[1] = CW_16bits;
+ tpb.status[0] = tpb.status[1] = 0;
+ tpb.count = 0;
+ set_pointer((long)&tpb,(char *)ccb.pt_tpb);
+ switch (func)
+ {
+ case READ_TA:
+ if (io->i_cc > cyblksiz)
+ tpb.size = TM_SHORT(cyblksiz);
+ else tpb.size = TM_SHORT(io->i_cc);
+ set_pointer((long)io->i_ma,(char *)tpb.pt_data);
+ cyblock += 1;
+ break;
+ case WRIT_TA:
+ tpb.size = TM_SHORT(io->i_cc);
+ set_pointer((long)io->i_ma,(char *)tpb.pt_data);
+ cyblock += 1;
+ break;
+ case SPACE:
+ if ((j = io->i_bn - cyblock) < 0) {
+ j = -j;
+ tpb.control[1] |= CW_R;
+ cyblock -= j;
+ }
+ else
+ cyblock += j;
+ tpb.rec_over = TM_SHORT(j);
+ break;
+ case REWD_TA:
+ cyblock = 0;
+ break;
+ }
+ TM_ATTENTION(ctlradr, 0xff); /* execute! */
+ if (func == REWD_TA || func == SPACE) {
+ cywait(60*5*1000);
+ }
+ else cywait(10*1000);
+ /*
+ * First we clear the interrupt and close the gate.
+ */
+ mtpr(PADC, 0);
+ ccb.gate[0] = GATE_CLOSED;
+ set_pointer((int)&cycool,(char *)ccb.pt_tpb);
+ cycool.cmd = NO_OP; /* no operation */
+ cycool.control[0] = CW_BL; /* No INTERRUPTS */
+ cycool.control[1] = 0;
+ TM_ATTENTION(ctlradr,0xff); /* cool it ! */
+ cywait(20000);
+ uncache (&tpb.status[1] ) ;
+ if (tpb.status[1] & CS_ERm) {
+ cy_decode_error(tpb.status[1]&CS_ERm);
+ return -1;
+ }
+ uncache (&tpb.count);
+ return((long)TM_SHORT(tpb.count));
+}
+
+
+
+cyprint_error(message)
+register char *message;
+{
+ printf("cy0: %s.\n", message);
+}
+\f
+/*
+*/
+
+cy_decode_error(status)
+register int status;
+{
+ switch(status) {
+ case ER_TO1:
+ case ER_TO2:
+ case ER_TO3:
+ case ER_TO4:
+ case ER_TO5:
+ cyprint_error("Drive timed out during transfer");
+ break;
+ case ER_TO6:
+ cyprint_error("Non-existant system memory reference");
+ break;
+ case ER_DIAG:
+ case ER_JUMP:
+ cyprint_error("Controller micro diagnostics failed");
+ break;
+ case ER_HARD:
+ cyprint_error("Unrecoverble media error");
+ break;
+ case ER_TOF:
+ if (tpb.cmd == WRIT_TA)
+ cyprint_error("Unsatisfactory media");
+ break;
+ case ER_FIFO:
+ cyprint_error("Data transfer over run");
+ break;
+ case ER_TRN:
+ cyprint_error("Drive is not ready");
+ break;
+ case ER_PRO:
+ cyprint_error("Tape is write protected");
+ break;
+ case ER_PSUM:
+ cyprint_error("Checksum error in controller proms");
+ break;
+ case ER_PARI:
+ cyprint_error("Unrecoverable tape parity error");
+ break;
+ case ER_BLAN:
+ cyprint_error("Blank tape found where data was expected");
+ break;
+ case ER_ER:
+ cyprint_error("Unrecoverble hardware error");
+ default:
+ break;
+ }
+}
+
+
+long
+cywait(timeout)
+ long timeout;
+{
+ long dummy;
+
+ uncache (&ccb.gate[0]) ;
+ while (ccb.gate[0] != GATE_OPEN) {
+ uncache (&ccb.gate[0]) ;
+ DELAY(1000);
+ if (--timeout == 0) {
+ cyprint_error("Transfer timeout");
+ _stop("");
+ }
+ }
+}
+
+/*
+ * Set a TAPEMASTER pointer (first parameter), into the
+ * 4 bytes array pointed by the second parameter.
+ */
+set_pointer(pointer,dest)
+long pointer;
+char * dest;
+{
+ *dest++ = pointer & 0xff; /* low byte - offset */
+ *dest++ = (pointer >> 8) & 0xff; /* high byte - offset */
+ *dest++ = 0;
+ *dest = (pointer & 0xf0000) >> 12; /* base */
+}