# /* * tm11 driver * * minor devices 0-7 get rewound on close * minor devices 8-15 don't * * indutry standard 2 tape marks for EOF * * stty functions for skip forward/reverse file/block * rewind * write tape mark * put drive off line. */ /*#define ERRORS 01 /* set debug bit 0 to enable error reports */ /*#define TRACE 02 /* set debug bit 1 to enable trace */ #ifdef ERRORS | TRACE int tmdebug; /* non-zero to debug */ #endif #include "../defines.h" #include "../param.h" #include "../conf.h" #include "../user.h" #include "../buf.h" #include "../reg.h" /* * Structure of device registers */ #define TMADDR 0172520 /* register base */ struct { int tmer; int tmcs; int tmbc; int tmba; int tmdb; int tmrd; }; #define TMER TMADDR->tmer #define TMCS TMADDR->tmcs #define TMBC TMADDR->tmbc #define TMBA TMADDR->tmba #define TMDB TMADDR->tmdb #define TMRD TMADDR->tmrd struct devtab tmtab; #ifndef RAW_BUFFER_POOL struct buf rtmbuf; #endif #define NTM11 3 /* drives on controller */ #define MANY_DRIVES /* if NTM11 > 1 */ /* tmcs */ #define GO 01 /* hardware go bit */ #define RCOM 03 /* read command */ #define WCOM 05 /* write command */ #define WIRG 015 /* write extended gap */ #define IENABLE 0100 /* interrupt enable */ #define CRDY 0200 /* controller ready */ #define UNIT 03400 /* unit select */ #define PCLR 010000 /* power clear */ #define DENS 060000 /* 9-track 800 bpi */ /* tmrd */ #define GAPSD 010000 /* gap slow down */ /* tmer */ #define TUR 01 /* tape driver ready */ #define HARD 0102200 /* hard error */ #define EOF 0040000 /* end-of-file */ #define RWS 02 /* rewind started */ #define WRL 04 /* write locked */ #define TAPSD 010 /* tape settle down */ #define SELR 0100 /* tape unit on line */ /* driver states */ #define SSEEK 1 /* positioning */ #define SIO 2 /* read/write */ #define SBSP 3 /* error recovery BS */ #define SPHYS 4 /* stty functions */ /* special commands */ #define SSFB 011 /* skip forward block */ #define SSRB 013 /* skip reverse block */ #define SSFF 001 /* skip forward file */ #define SSRF 003 /* skip reverse file */ #define SSREW 017 /* rewind on-line */ #define SSWEOF 007 /* write eof */ #define SSOFFL 020000 /* put drive off-line */ #define SSWRL 040000 /* check tape write lock */ #define SSONLN 0100000 /* check tape on-line */ /* drive states */ #define OPEN 01 /* drive open */ #define HARDE 02 /* drive hard error */ #define LASTW 04 /* driver last command was write */ struct { char t_status[NTM11]; unsigned t_blkno[NTM11]; unsigned t_nxrec[NTM11]; } tm11; /* * Open: check for legal drive number, and * already open drive. */ tmopen(dev, flag) { register dminor; dminor = dev.d_minor & 07; if(dminor >= NTM11 || tm11.t_status[dminor]&OPEN) u.u_error = ENXIO; else { tm11.t_status[dminor] = OPEN; tmcommand(dev, flag ? SSWRL|SSONLN : SSONLN); tm11.t_blkno[dminor] = 0; tm11.t_nxrec[dminor] = 0177777; /* last block written set at 64k */ if(u.u_error) tm11.t_status[dminor] = 0; } } /* * Close: if the last command on the drive was * a write, then close off with a WEOF */ tmclose(dev, flag) { register dminor; dminor = dev.d_minor&07; if(tm11.t_status[dminor]&LASTW) { tmcommand(dev, SSWEOF, -2); if ( dev.d_minor & 010 ) tmcommand(dev, SSRB, -1); } if ( (dev.d_minor & 010) == 0 ) tmcommand(dev, SSREW); tm11.t_status[dminor] = 0; } /* * Strategy: the tm supports three basically distinct types * of I/O. As a UNIX style block device, it behaves just like * a random access device, with the exception that writes * must be sequential, and invalidate what lies beyond them. * As a raw device, it can read and write records of any * blocksize. It also supports tape positioning functions: * skip [forward/reverse] [blocks/files], rewind, and weof. */ tmstrategy(bp) register struct buf *bp; { register unsigned *p; if((bp->b_flags&(B_NOTIO|B_PHYS))==0) /* i.e. block device */ { p = &tm11.t_nxrec[bp->b_dev.d_minor&07]; if(*p <= bp->b_blkno) { if(*p < bp->b_blkno) /* attempt to read/write non-existent block */ { bp->b_flags =| B_ERROR; iodone(bp); return; } if(bp->b_flags&B_READ) /* attempt to read block immediately beyond last block written */ { clrbuf(bp); iodone(bp); return; } } if((bp->b_flags&B_READ)==0) *p = bp->b_blkno + 1; } # ifdef _1170 if(bp->b_flags&B_PHYS) mapalloc(bp); # endif bp->av_forw = NULL; spl5(); if(tmtab.d_actf == NULL) { tmtab.d_actf = bp; tmstart(); } else { tmtab.d_actl->av_forw = bp; } tmtab.d_actl = bp; spl0(); } /* * Start: If the requested function is on a drive with * a hard error, shoot it down. turn skip-files into * the skip-blocks that the hardware supports. Do positioning * for block-type i/o. Write with extended IRG for * crudy tapes. */ tmstart() { register struct buf *bp; register com; register blkno; unsigned unit; loop: if((bp = tmtab.d_actf) == NULL) return; unit = bp->b_dev.d_minor & 07; if(tm11.t_status[unit]&HARDE) { bp->b_flags =| B_ERROR; deq: tmtab.d_actf = bp->av_forw; iodone(bp); goto loop; } tm11.t_status[unit] =& ~LASTW; com = (unit << 8) | ((bp->b_xmem&03) << 4) | IENABLE | DENS; if(bp->b_flags&B_NOTIO) { tmtab.d_active = SPHYS; if ((blkno = bp->b_blkno) & (SSWRL|SSONLN)) { # ifdef MANY_DRIVES TMCS = (com & (UNIT|IENABLE)); com = 100; do; while(--com); # endif MANY_DRIVES if ( (com = TMER) & RWS ) return; /* await rewind completed */ if ( (com&(TUR|SELR))!=(TUR|SELR) || ((com&WRL)&&(blkno&SSWRL)) ) bp->b_flags =| B_ERROR; goto deq; } if ( blkno & SSOFFL ) { TMCS = com|GO; goto deq; } if(blkno == SSFF || blkno ==SSRF) { com =| 010; TMBC = 0; } else TMBC = bp->b_wcount; if ( blkno == SSRF || blkno == SSRB ) { TMCS = (com&03400); while ( TMER & TAPSD ); } TMCS = com | blkno; } else { if((bp->b_flags&B_PHYS)==0 && (blkno = tm11.t_blkno[unit] - bp->b_blkno)) { tmtab.d_active = SSEEK; if(blkno < 0) { com =| SSFB; } else { blkno = -blkno; if(bp->b_blkno == 0) com =| SSREW; else { com =| SSRB; } while ( TMER & TAPSD ); } TMBC = blkno; } else { tmtab.d_active = SIO; TMBC = (bp->b_wcount << 1); TMBA = bp->b_addr; if ( bp->b_flags & B_READ ) com =| RCOM; else { tm11.t_status[unit] =| LASTW; if ( tmtab.d_errcnt ) com =| WIRG; else com =| WCOM; } } TMCS = com; } } /* Interrupt: If there is nothing to do, just return. * Fix up soft errors on read and write, continue * processing of Skip files. * Note that physical limitations mean that a mag-tape * could not have more than about 50000 records . * * rewritten - Piers Lauder Jan '78 */ #define iotyp error tmintr() { register struct buf *bp; register unsigned unit, error; # ifdef TRACE if ( tmdebug & TRACE ) printf( "\nTMER %o CS %o BC %o BA %o DB %o RD %o IOTYP %d\n" ,TMER, TMCS, TMBC, TMBA, TMDB, TMRD, tmtab.d_active ); # endif if ( (bp = tmtab.d_actf) == NULL ) /* nothing doing */ return; unit = bp->b_dev.d_minor & 07; bp->b_resid = TMBC; if ( TMCS < 0 ) /* error */ { error = TMER; # ifdef ERRORS if ( tmdebug & ERRORS ) printf( "\nTMER %o CS %o BC %o BA %o DB %o RD %o IOTYP %d\n" ,error, TMCS, TMBC, TMBA, TMDB, TMRD, tmtab.d_active ); # endif /*while ( TMRD & GAPSD );*/ if ( error & HARD ) tm11.t_status[unit] =| HARDE; else if ( error & EOF ) { if ( tmtab.d_active == SPHYS ) { if (((error = bp->b_blkno) == SSFF) || (error == SSRF) || (error == SSWEOF)) if ( ++bp->b_wcount < 0 ) { TMBC = 0; TMCS =| GO; return; } } else if ( bp->b_flags & B_PHYS ) bp->b_resid = bp->b_wcount; /* indicate EOF by 0 bytes read */ else { bp->b_flags =| B_ERROR; /* indicate EOF by error */ goto backsp; } goto done; } else if ((tmtab.d_active == SIO) && (++tmtab.d_errcnt < 10)) { backsp: tmtab.d_active = SBSP; while ( TMER & TAPSD ); TMBC = -1; TMCS = (unit << 8)|IENABLE|DENS|SSRB; if ( bp->b_flags & B_ERROR ) { tmtab.d_actf = bp->av_forw; iodone( bp ); } return; } bp->b_flags =| B_ERROR; tmtab.d_active = SIO; } if ((iotyp = tmtab.d_active) == SSEEK) tm11.t_blkno[unit] = bp->b_blkno; else if ( iotyp != SBSP ) { tm11.t_blkno[unit]++; if ( iotyp == SIO ) bp->b_resid =>> 1; done: tmtab.d_actf = bp->av_forw; iodone( bp ); tmtab.d_errcnt = 0; } tmstart(); } /* * TM stty: fetch code and count from user, and pass then * into raw buffer header. Obey buffer protocols. * Codes: * 0 Skip forward file * 1 Skip reverse file * 2 Clear ( super-users only ) * 3 Write end-of-file * 4 Skip forward block * 5 Skip reverse block * 6 Drive off line * 7 Rewind */ tmsgtty(dev, v) { register cnt, com; if ( v || (u.u_arg[0] & ~07) ) { u.u_error = ENXIO; return; } com = ((u.u_arg[0] & 07) << 1) | GO; if(com == 05) { if ( suser() ) TMCS = PCLR; return; } if (com == 015) com = SSOFFL; if ((cnt = -u.u_arg[1]) >= 0) cnt = -1; u.u_ar0[R0] = tmcommand(dev, com, cnt); } /* * queue a non-io type request to the tapedrive */ tmcommand(dev, com, cnt) register com, cnt; { register struct buf *bp; if ( u.u_error ) goto out; # ifndef RAW_BUFFER_POOL bp = &rtmbuf; while(bp->b_flags&B_BUSY) { bp->b_flags =| B_WANTED; sleep(bp, PRIBIO); } # else RAW_BUFFER_POOL bp = getrb(dev , B_NOTIO); # endif RAW_BUFFER_POOL bp->b_flags = B_BUSY | B_NOTIO; bp->b_dev = dev; bp->b_blkno = com; bp->b_wcount = cnt; tmstrategy(bp); # ifndef RAW_BUFFER_POOL spl5(); # else RAW_BUFFER_POOL spl6(); # endif RAW_BUFFER_POOL while((bp->b_flags&B_DONE)==0) sleep(bp, PRIBIO); # ifndef RAW_BUFFER_POOL if(bp->b_flags&B_WANTED) wakeup(bp); spl0(); bp->b_flags =& ~(B_BUSY|B_WANTED); # else RAW_BUFFER_POOL freerb(bp); # endif RAW_BUFFER_POOL geterror(bp); out: if(com == SSFB || com == SSRB) return(bp->b_resid); return(bp->b_wcount); } #ifndef RAW_BUFFER_POOL tmread(dev) { physio(tmstrategy, &rtmbuf, dev, B_READ); } tmwrite(dev) { physio(tmstrategy, &rtmbuf, dev, B_WRITE); } #else RAW_BUFFER_POOL tmread(dev) { physio(tmstrategy, 0, dev, B_READ); } tmwrite(dev) { physio(tmstrategy, 0, dev, B_WRITE); } #endif RAW_BUFFER_POOL #ifdef POWER_FAIL /* * restart controller after a power restore */ tmpowerf() { register char *cp; for ( cp=tm11.t_status ; cp < &tm11.t_status[NTM11] ; cp++ ) *cp =| HARDE; tmstart(); } #endif POWER_FAIL