# /* * Interdata 800 bpi mag tape driver * * This is a 'first attempt' at a driver, and should be thrown * away and rewritten. The major known problems are: * - it probably won't work for multiple transports * - attempting a backspace from loadpoint will hang up the controller * (this can be cleared manually by a reset/forward/reset/online * sequence) * - error checking, particularly for end-of-tape, is insufficient * */ #include "param.h" #include "conf.h" #include "user.h" #include "buf.h" #include "selch.h" /* Configuration */ #define NTAPES 1 #define NSELCH 0 /* connected to selch 0 */ char mtaddr[] { 0xc5 }; #define NMTERR 8 /* number of error retries */ int mtio(), mtscintr(); struct tape { int m_status; /* status of transport */ int m_blkno; /* current block position in file */ int m_lastrec; /* next record # at end of file */ } tape[NTAPES]; #define ISOPEN 01 #define ISDU 02 #define ISWRITING 04 #define ISBOT 010 struct devtab mtab; #define SCOM 1 #define SSFOR 2 #define SSREV 3 #define SIO 4 #define SBOT 5 struct selchq mtselchq { &mtio, &mtscintr, 0 }; struct buf rmtbuf, cmtbuf; int mtead; /* * Tape controller commands */ #define ENABLE 0x40 #define CLEAR 0x20 #define READ 0x21 #define WRITE 0x22 #define WF 0x30 #define RW 0x38 #define FF 0x23 #define BF 0x13 #define BR 0x11 #define OPEN 0xff /* * Tape status */ #define ERR 0x80 #define EOF 0x40 #define EOT 0x20 #define NMTN 0x10 #define BSY 0x04 #define DU 0x01 mtopen(dev, flag) { register struct tape *mt; register unit; if ((unit=dev.d_minor&03) >= NTAPES || (mt = &tape[unit])->m_status & ISOPEN) { u.u_error = ENXIO; return; } mt->m_status = mt->m_blkno = 0; mt->m_lastrec = 1000000; mtcommand(dev, OPEN); if (mt->m_status & ISDU) u.u_error = ENXIO; if (u.u_error == 0) mt->m_status =| ISOPEN; } mtclose(dev, flag) { register struct tape *mt; register writing; mt = &tape[dev.d_minor&03]; writing = 0; if (mt->m_status&ISWRITING) { writing++; mtcommand(dev, WF); mtcommand(dev, WF); } if (dev.d_minor & 04) { if (writing) mtcommand(dev, BR); } else mtcommand(dev, RW); mt->m_status = 0; } mtcommand(dev, command) { register struct buf *bp; trace(0100<<16, "mtcommand", command); bp = &cmtbuf; spl(5); while (bp->b_flags & B_BUSY) { bp->b_flags =| B_WANTED; sleep(bp, PRIBIO); } bp->b_flags =| B_BUSY | B_READ; spl(0); bp->b_dev = dev; bp->b_blkno = command; mtstrategy(bp); iowait(bp); if (bp->b_flags & B_WANTED) wakeup(bp); bp->b_flags = 0; } mtstrategy(abp) struct buf *abp; { register struct buf *bp; register struct tape *mt; register int *p; if ((bp = abp) != &cmtbuf) { mt = &tape[bp->b_dev.d_minor&03]; p = &mt->m_lastrec; if (bp->b_blkno > *p) { bp->b_flags =| B_ERROR; iodone(bp); return; } if (bp->b_blkno == *p && (bp->b_flags&B_READ)) { bp->b_resid = bp->b_bcount; clrbuf(bp); iodone(bp); return; } if ((bp->b_flags&B_READ) == 0) *p = bp->b_blkno + 1; } bp->av_forw = 0; spl(5); if (mtab.d_actf) mtab.d_actl->av_forw = bp; else mtab.d_actf = bp; mtab.d_actl = bp; if (mtab.d_active == 0) mtstart(); spl(0); } mtstart() { register struct tape *mt; register struct buf *bp; while (bp = mtab.d_actf) { mt = &tape[bp->b_dev.d_minor&03]; if (bp != &cmtbuf && (mt->m_status&ISDU)) { bp->b_flags =| B_ERROR; mtab.d_actf = bp->av_forw; iodone(bp); continue; } mt->m_status =& ~ISWRITING; if (bp == &cmtbuf) mtab.d_active = SCOM; else if (bp->b_blkno == mt->m_blkno) { mtab.d_active = SIO; if ((bp->b_flags&B_READ) == 0) { mt->m_status =| ISWRITING; /* * According to the Interdata tape manual, * when writing the first block it is necessary * to write a file mark first and backspace * over it. If this is not done, the block * is sometimes not written correctly. */ if (mt->m_status&ISBOT) mtab.d_active = SBOT; } } else if (bp->b_blkno > mt->m_blkno) mtab.d_active = SSFOR; else mtab.d_active = SSREV; selchreq(NSELCH, &mtselchq); return; } } mtio() { register struct buf *bp; register addr; register stat; trace(0100<<16, "mtio", mtab.d_active); if ((bp = mtab.d_actf) == 0) return; addr = mtaddr[bp->b_dev.d_minor&03]; switch(mtab.d_active) { case SCOM: if (bp->b_blkno == OPEN) { register struct tape *mt; mt = &tape[bp->b_dev.d_minor&03]; oc(addr, CLEAR); oc(addr, ENABLE); if ((stat = ss(addr))&DU) mt->m_status =| ISDU; if (stat&EOT) mt->m_status =| ISBOT; mtab.d_active = 0; } else oc(addr, bp->b_blkno); trace(0200<<16, "mtoc", bp->b_blkno); trace(0200<<16, "status", ss(addr)); mtab.d_actf = bp->av_forw; iodone(bp); selchfree(NSELCH); return; case SSREV: oc(addr, BR); trace(0200<<16, "mtoc", BR); trace(0200<<16, "status", ss(addr)); return; case SBOT: oc(addr, WF); trace(0200<<16, "mtoc", WF); trace(0200<<16, "status", ss(addr)); return; case SSFOR: case SIO: oc(selchaddr[NSELCH], STOP); wdh(selchaddr[NSELCH], bp->b_addr); wdh(selchaddr[NSELCH], bp->b_addr + bp->b_bcount - 1); trace(0200<<16, "mtrw", bp->b_addr); if ((bp->b_flags&B_READ) || mtab.d_active == SSFOR) { oc(addr, READ); oc(selchaddr[NSELCH], READ_GO); } else { oc(addr, WRITE); oc(selchaddr[NSELCH], GO); } } } mtscintr(dev, stat) { oc(selchaddr[NSELCH], STOP); mtead = (rdh(selchaddr[NSELCH])+1) & ~01; } mtintr(dev, stat) { register struct buf *bp; register struct tape *mt; register op; trace(0200<<16, "interrupt", dev); trace(0200<<16, "status", stat); /* * If NMTN status is not set, wait for another * interrupt. NMTN should be the last bit to set. */ if ((stat&NMTN) == 0) return; op = mtab.d_active; mtab.d_active = 0; if ((bp = mtab.d_actf) == 0) return; if (op == SCOM) { mtstart(); return; } selchfree(NSELCH); mt = &tape[bp->b_dev.d_minor&03]; mt->m_status =& ~(ISBOT|ISDU); if (stat&DU) { mt->m_status =| ISDU; mtab.d_actf = bp->av_forw; bp->b_flags =| B_ERROR; iodone(bp); mtstart(); return; } if (op == SSREV) mt->m_blkno--; else mt->m_blkno++; if ((stat&ERR) && op == SIO) { deverror(bp, stat, dev); if (++mtab.d_errcnt < NMTERR) { mtab.d_active = SSREV; selchreq(NSELCH, &mtselchq); return; } bp->b_flags =| B_ERROR; } if (op == SIO || (stat&(EOF|EOT)) && op == SSFOR) { mtab.d_errcnt = 0; mtab.d_actf = bp->av_forw; bp->b_resid = stat&(EOF|EOT) ? bp->b_bcount : bp->b_bcount - (mtead - bp->b_addr); iodone(bp); } mtstart(); } /* * Raw magtape interface */ mtread(dev) { mtseek(dev); physio(&mtstrategy, &rmtbuf, dev, B_READ); } mtwrite(dev) { mtseek(dev); physio(&mtstrategy, &rmtbuf, dev, B_WRITE); } /* * Kludge to ignore seeks on raw mag tape by making block no. look right */ mtseek(dev) { register struct tape *mt; mt = &tape[dev.d_minor&03]; mt->m_lastrec = (mt->m_blkno = lshift(u.u_offset, -9)) + 1; } /* * Stty call is used to issue commands to raw mag tape * First word is command function; other two must be 0 */ char mtcmds[8] { /* command functions */ FF, /* 0 - forward space file */ BF, /* 1 - back space file */ 0, /* 2 */ WF, /* 3 - write file mark */ READ, /* 4 - forward space record * (since there is no FR command, we simply * do a read without starting up the selch * and ignore the overrun error) */ BR, /* 5 - back space record */ 0, /* 6 */ RW /* 7 - rewind */ }; mtsgtty(dev, v) int *v; { register fn, cmd; register int *ap; /* Ignore gtty */ if (v) return; ap = u.u_arg; if ((fn = *ap) < 0 || fn > 7 || !(cmd = mtcmds[fn]) || ap[1] || ap[2]) { u.u_error = ENXIO; return; } mtcommand(dev, cmd); }