/* $NetBSD: ctu.c,v 1.39 2023/12/20 15:34:45 thorpej Exp $ */ /* * Copyright (c) 1996 Ludd, University of Lule}, Sweden. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Device driver for 11/750 Console TU58. * * Writing of tapes does not work, by some unknown reason so far. * It is almost useless to try to use this driver when running * multiuser, because the serial device don't have any buffers * so we will lose interrupts. */ #include __KERNEL_RCSID(0, "$NetBSD: ctu.c,v 1.39 2023/12/20 15:34:45 thorpej Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef TUDEBUG #define TU_IDLE 0 #define TU_RESET 1 #define TU_RUNNING 2 #define TU_WORKING 3 #define TU_READING 4 #define TU_WRITING 5 #define TU_ENDPACKET 6 #define TU_RESTART 7 struct tu_softc { int sc_state; int sc_step; char sc_rsp[15]; /* Should be struct rsb; but don't work */ int sc_tpblk; /* Start block number */ int sc_wto; /* Timeout counter */ int sc_xbytes; /* Number of xfer'd bytes */ int sc_op; /* Read/write */ struct bufq_state *sc_bufq; /* pending I/O requests */ } tu_sc; struct ivec_dsp tu_recv, tu_xmit; void ctuattach(void); static void ctutintr(void *); static void cturintr(void *); static void ctustart(void); static void ctuwatch(void *); static u_short ctu_cksum(unsigned short *, int); dev_type_open(ctuopen); dev_type_close(ctuclose); #if 0 /* not yet */ dev_type_read(cturead); dev_type_write(ctuwrite); #endif dev_type_strategy(ctustrategy); const struct bdevsw ctu_bdevsw = { .d_open = ctuopen, .d_close = ctuclose, .d_strategy = ctustrategy, .d_ioctl = noioctl, .d_dump = nodump, .d_psize = nosize, .d_discard = nodiscard, .d_flag = D_TAPE }; #if 0 /* not yet */ const struct cdevsw ctu_cdevsw = { .d_open = ctuopen, .d_close = ctuclose, .d_read = cturead, .d_write = ctuwrite, .d_ioctl = noioctl, .d_stop = nostop, .d_tty = notty, .d_poll = nopoll, .d_mmap = nommap, .d_kqfilter = nokqfilter, .d_discard = nodiscard, .d_flag = D_TAPE }; #endif static callout_t ctu_watch_ch; void ctuattach(void) { callout_init(&ctu_watch_ch, 0); bufq_alloc(&tu_sc.sc_bufq, "fcfs", 0); tu_recv = idsptch; tu_recv.hoppaddr = cturintr; scb->scb_csrint = (void *)&tu_recv; tu_xmit = idsptch; tu_xmit.hoppaddr = ctutintr; scb->scb_cstint = (void *)&tu_xmit; } static void ctuinit(void) { int s = spl7(); #define WAIT while ((mfpr(PR_CSTS) & 0x80) == 0) /* * Do a reset as described in the * "TU58 DECtape II Users Guide". */ mtpr(0101, PR_CSTS); /* Enable transmit interrupt + send break */ WAIT; mtpr(0, PR_CSTD); WAIT; mtpr(0, PR_CSTD); WAIT; mtpr(RSP_TYP_INIT, PR_CSTD); WAIT; mtpr(RSP_TYP_INIT, PR_CSTD); WAIT; #undef WAIT splx(s); } int ctuopen(dev_t dev, int oflags, int devtype, struct lwp *l) { int error; if (minor(dev)) return ENXIO; if (tu_sc.sc_state != TU_IDLE) return EBUSY; tu_sc.sc_state = TU_RESET; tu_sc.sc_step = 0; mtpr(0100, PR_CSRS); /* Enable receive interrupt */ mtpr(0101, PR_CSTS); /* Enable transmit interrupt + send break */ if ((error = tsleep((void *)&tu_sc, (PZERO + 10)|PCATCH, "reset", 0))) return error; #ifdef TUDEBUG printf("ctuopen: running\n"); #endif tu_sc.sc_state = TU_RUNNING; callout_reset(&ctu_watch_ch, hz, ctuwatch, NULL); return 0; } int ctuclose(dev_t dev, int oflags, int devtype, struct lwp *l) { struct buf *bp; int s = spl7(); while ((bp = bufq_get(tu_sc.sc_bufq))) ; splx(s); mtpr(0, PR_CSRS); mtpr(0, PR_CSTS); tu_sc.sc_state = TU_IDLE; callout_stop(&ctu_watch_ch); return 0; } void ctustrategy(struct buf *bp) { int s, empty; #ifdef TUDEBUG printf("ctustrategy: bcount %ld blkno %d\n", bp->b_bcount, bp->b_blkno); printf("ctustrategy: bp %p\n", bp); #endif s = spl7(); if (bp->b_blkno >= 512) { bp->b_resid = bp->b_bcount; biodone(bp); splx(s); return; } empty = (bufq_peek(tu_sc.sc_bufq) == NULL); bufq_put(tu_sc.sc_bufq, bp); if (empty) ctustart(); splx(s); } void ctustart(void) { struct rsp *rsp = (struct rsp *)tu_sc.sc_rsp; struct buf *bp; bp = bufq_peek(tu_sc.sc_bufq); if (bp == NULL) return; #ifdef TUDEBUG printf("ctustart: %s\n", bp->b_flags & B_READ ? "READING":"WRITING"); #endif tu_sc.sc_tpblk = bp->b_blkno; tu_sc.sc_xbytes = 0; tu_sc.sc_op = bp->b_flags & B_READ ? RSP_OP_READ : RSP_OP_WRITE; tu_sc.sc_step = 0; bp->b_resid = bp->b_bcount; tu_sc.sc_wto = 0; rsp->rsp_typ = RSP_TYP_COMMAND; rsp->rsp_sz = 012; rsp->rsp_op = tu_sc.sc_op; rsp->rsp_mod = 0; rsp->rsp_drv = 0; rsp->rsp_sw = rsp->rsp_xx1 = rsp->rsp_xx2 = 0; rsp->rsp_cnt = bp->b_bcount; rsp->rsp_blk = tu_sc.sc_tpblk; rsp->rsp_sum = ctu_cksum((unsigned short *)rsp, 6); tu_sc.sc_state = TU_WORKING; ctutintr(NULL); } static int readchr(void) { int i; for (i = 0; i < 5000; i++) if ((mfpr(PR_CSRS) & 0x80)) break; if (i == 5000) return -1; return mfpr(PR_CSRD); } /* * Loop in a tight (busy-wait-)loop when receiving packets, this is * the only way to avoid loosing characters. */ void cturintr(void *arg) { int status = mfpr(PR_CSRD); struct buf *bp; int i, c, tck; unsigned short ck = 0; char *buf; bp = bufq_peek(tu_sc.sc_bufq); buf = bp->b_data; switch (tu_sc.sc_state) { case TU_RESET: if (status != RSP_TYP_CONTINUE) printf("Bad response %d\n", status); wakeup(&tu_sc); return; case TU_READING: if (status != RSP_TYP_DATA) bp->b_error = EIO; tu_sc.sc_wto = 0; for (i = 0; i < 131; i++) { if ((c = readchr()) < 0) { #ifdef TUDEBUG printf("Timeout...%d\n", i); #endif goto bad; } if ((i > 0) && (i < 129)) buf[tu_sc.sc_xbytes++] = c; if (i == 129) ck = (c & 0xff); if (i == 130) ck |= ((c & 0xff) << 8); } tck = ctu_cksum((void *)&buf[tu_sc.sc_xbytes-128], 64); tck += 0x8001; if (tck > 0xffff) tck -= 0xffff; if (tck != ck) { #ifdef TUDEBUG int i; printf("Bad cksum: tck %x != ck %x\n", tck, ck); printf("block %d\n", tu_sc.sc_xbytes/128-1); for (i = -128; i < 0; i+=16) printf("%x %x %x %x\n", *(int *)&bp->b_data[tu_sc.sc_xbytes+i], *(int *)&bp->b_data[tu_sc.sc_xbytes+i+4], *(int *)&bp->b_data[tu_sc.sc_xbytes+i+8], *(int *)&bp->b_data[tu_sc.sc_xbytes+i+12]); #endif goto bad; } bp->b_resid = 0; if (bp->b_bcount == tu_sc.sc_xbytes) tu_sc.sc_state = TU_ENDPACKET; return; case TU_ENDPACKET: if (status != RSP_TYP_COMMAND) { #ifdef TUDEBUG int g[14], j; g[0] = status; for (i = 1; i < 14; i++) if ((g[i] = readchr()) < 0) break; j=0; while (readchr() >= 0) j++; for (i = 0; i < 14; i++) printf("%d: %x\n", i, g[i]); printf("Got %d char more\n", j); printf("error: state %d xbytes %d status %d\n", tu_sc.sc_state, tu_sc.sc_xbytes, status); #endif bp->b_error = EIO; } tu_sc.sc_wto = 0; for (i = 0; i < 13; i++) { if ((c = readchr()) < 0) { #ifdef TUDEBUG printf("Timeout epack %d\n", i); #endif goto bad; } if ((i == 2) && ((c != RSP_MOD_OK) && (c != RSP_MOD_RETR))) { #ifdef TUDEBUG printf("end packet status bad: %d\n", c); #endif bp->b_error = EIO; } } break; case TU_WRITING: #define WAIT while ((mfpr(PR_CSTS) & 0x80) == 0) if (status != RSP_TYP_CONTINUE) goto bad; #ifdef TUDEBUG printf("Writing byte %d\n", tu_sc.sc_xbytes); #endif WAIT; mtpr(RSP_TYP_DATA, PR_CSTD); WAIT; mtpr(128, PR_CSTD); for (i = 0; i < 128; i++) { WAIT; mtpr(buf[tu_sc.sc_xbytes++], PR_CSTD); } tck = ctu_cksum((void *)&buf[tu_sc.sc_xbytes-128], 64); tck += 0x8001; if (tck > 0xffff) tck -= 0xffff; WAIT; mtpr(tck & 0xff, PR_CSTD); WAIT; mtpr((tck >> 8) & 0xff, PR_CSTD); bp->b_resid = 0; if (tu_sc.sc_xbytes == bp->b_bcount) tu_sc.sc_state = TU_ENDPACKET; return; #undef WAIT case TU_RESTART: if (status != RSP_TYP_CONTINUE) goto bad; ctustart(); return; default: printf("bad rx state %d char %d\n", tu_sc.sc_state, status); return; } if (bp->b_error == 0) { (void)bufq_get(tu_sc.sc_bufq); biodone(bp); #ifdef TUDEBUG printf("biodone %p\n", bp); #endif } #ifdef TUDEBUG else { printf("error: state %d xbytes %d status %d\n", tu_sc.sc_state, tu_sc.sc_xbytes, status); } #endif bp->b_error = 0; tu_sc.sc_state = TU_IDLE; ctustart(); return; bad: tu_sc.sc_state = TU_RESTART; ctuinit(); } void ctutintr(void *arg) { while ((mfpr(PR_CSTS) & 0x80) == 0) ; switch (tu_sc.sc_state) { case TU_RESET: switch (tu_sc.sc_step) { case 0: case 1: mtpr(0, PR_CSTD); break; case 2: case 3: mtpr(RSP_TYP_INIT, PR_CSTD); break; default: break; } tu_sc.sc_step++; return; case TU_WORKING: if (tu_sc.sc_step == 14) { if (tu_sc.sc_op == RSP_OP_READ) tu_sc.sc_state = TU_READING; else tu_sc.sc_state = TU_WRITING; } else mtpr(tu_sc.sc_rsp[tu_sc.sc_step++], PR_CSTD); return; case TU_IDLE: printf("Idle interrupt\n"); return; case TU_ENDPACKET: case TU_WRITING: case TU_RESTART: return; default: printf("bad tx state %d\n", tu_sc.sc_state); } } unsigned short ctu_cksum(unsigned short *buf, int words) { int i, cksum; for (i = cksum = 0; i < words; i++) cksum += buf[i]; hej: if (cksum > 65535) { cksum = (cksum & 65535) + (cksum >> 16); goto hej; } return cksum; } int oldtp; /* * Watch so that we don't get blocked unnecessary due to lost int's. */ void ctuwatch(void *arg) { callout_reset(&ctu_watch_ch, hz, ctuwatch, NULL); if (tu_sc.sc_state == TU_WORKING) { /* * Died in sending command. * Wait 5 secs. */ if (tu_sc.sc_wto++ > 5) { #ifdef TUDEBUG printf("Died in sending command\n"); #endif tu_sc.sc_state = TU_RESTART; ctuinit(); } } if (tu_sc.sc_state == TU_READING || tu_sc.sc_state == TU_WRITING) { /* * Positioning, may take long time. * Wait one minute. */ if (tu_sc.sc_wto++ > 60) { #ifdef TUDEBUG printf("Died in Positioning, wto %d\n", tu_sc.sc_wto); #endif tu_sc.sc_state = TU_RESTART; ctuinit(); } } }