1*4746Ssam /* ut.c 4.2 81/11/06 */ 24744Swnj 34744Swnj #include "ut.h" 44744Swnj #if NUT > 0 54744Swnj #define UTDEBUG 1 64744Swnj /* 74744Swnj * System Industries Model 9700 Tape Drive 84744Swnj * emulates a TU45 on the UNIBUS 94744Swnj * 104744Swnj * TODO: 114744Swnj * check out attention processing 124744Swnj * try reset code and dump code 134744Swnj */ 144744Swnj #include "../h/param.h" 154744Swnj #include "../h/systm.h" 164744Swnj #include "../h/buf.h" 174744Swnj #include "../h/conf.h" 184744Swnj #include "../h/dir.h" 194744Swnj #include "../h/file.h" 204744Swnj #include "../h/user.h" 214744Swnj #include "../h/map.h" 224744Swnj #include "../h/pte.h" 234744Swnj #include "../h/ubareg.h" 244744Swnj #include "../h/ubavar.h" 254744Swnj #include "../h/mtio.h" 264744Swnj #include "../h/ioctl.h" 274744Swnj #include "../h/cmap.h" 284744Swnj #include "../h/cpu.h" 294744Swnj 304744Swnj #include "../h/utreg.h" 314744Swnj 324744Swnj struct buf rutbuf[NUT]; /* bufs for raw i/o */ 334744Swnj struct buf cutbuf[NUT]; /* bufs for control operations */ 344744Swnj struct buf tjutab[NTJ]; /* bufs for slave queue headers */ 354744Swnj 364744Swnj struct uba_ctlr *utminfo[NUT]; 374744Swnj struct uba_device *tjdinfo[NTJ]; 384744Swnj int utprobe(), utslave(), utattach(), utdgo(); 394744Swnj u_short utstd[] = { 0772440, 0 }; 404744Swnj struct uba_driver utdriver = 414744Swnj { utprobe, utslave, utattach, utdgo, utstd, "tj", tjdinfo, "ut", utminfo, 0 }; 424744Swnj 434744Swnj /* bits in minor device */ 444744Swnj #define TJUNIT(dev) (minor(dev)&03) 454744Swnj #define T_NOREWIND 04 464744Swnj #define T_1600BPI 010 474744Swnj #define T_6250BPI 020 484744Swnj short utdens[] = { UT_NRZI, UT_PE, UT_GCR, UT_NRZI }; 494744Swnj 504744Swnj /* slave to controller mapping table */ 514744Swnj short tjtout[NTJ]; 524744Swnj #define UTUNIT(dev) (tjtout[TJUNIT(dev)]) 534744Swnj 544744Swnj #define INF (daddr_t)1000000L /* a block number that wont exist */ 554744Swnj 564744Swnj struct tj_softc { 574744Swnj char sc_openf; /* exclusive open */ 584744Swnj char sc_lastiow; /* last I/O operation was a write */ 594744Swnj daddr_t sc_blkno; /* next block to transfer */ 604744Swnj daddr_t sc_nxrec; /* next record on tape */ 614744Swnj u_short sc_erreg; /* image of uter */ 624744Swnj u_short sc_dsreg; /* image of utds */ 63*4746Ssam u_short sc_resid; /* residual from transfer */ 644744Swnj u_short sc_dens; /* sticky selected density */ 654744Swnj } tj_softc[NTJ]; 664744Swnj 674744Swnj /* 684744Swnj * Internal per/slave states found in sc_state 694744Swnj */ 704744Swnj #define SSEEK 1 /* seeking */ 714744Swnj #define SIO 2 /* doing sequential I/O */ 724744Swnj #define SCOM 3 /* sending a control command */ 734744Swnj #define SREW 4 /* doing a rewind op */ 74*4746Ssam #define SERASE 5 /* erase inter-record gap */ 75*4746Ssam #define SERASED 6 /* erased inter-record gap */ 764744Swnj 774744Swnj #if UTDEBUG 784744Swnj int utdebug; 794744Swnj #define printd if (utdebug) printf 804744Swnj #else 814744Swnj #define printd 824744Swnj #endif 834744Swnj 844744Swnj /* 854744Swnj * A NOP should get an interrupt back, if the 864744Swnj * device is there. 874744Swnj */ 884744Swnj utprobe(reg) 894744Swnj caddr_t reg; 904744Swnj { 914744Swnj register int br, cvec; 924744Swnj #ifdef lint 934744Swnj br=0; cvec=br; br=cvec; 944744Swnj #endif 95*4746Ssam /* 96*4746Ssam * It appears the controller won't interrupt unless the 97*4746Ssam * slave is off-line...this is as bad as the TS-11. 98*4746Ssam */ 99*4746Ssam #ifdef notdef 1004744Swnj ((struct utdevice *) reg)->utcs1 = UT_IE|UT_NOP|UT_GO; 1014744Swnj DELAY(10000); 1024744Swnj ((struct utdevice *) reg)->utcs1 = UT_CLEAR|UT_GO; 103*4746Ssam #else 104*4746Ssam br = 0x15; 105*4746Ssam cvec = 0164; 1064744Swnj return(1); 107*4746Ssam #endif 1084744Swnj } 1094744Swnj 1104744Swnj /*ARGSUSED*/ 1114744Swnj utslave(ui, reg) 1124744Swnj struct uba_device *ui; 1134744Swnj caddr_t reg; 1144744Swnj { 1154744Swnj /* 1164744Swnj * A real TU45 would support the slave present bit 1174744Swnj * int the drive type register, but this thing doesn't, 1184744Swnj * so there's no way to determine if a slave is present or not. 1194744Swnj */ 1204744Swnj return(1); 1214744Swnj } 1224744Swnj 1234744Swnj utattach(ui) 1244744Swnj struct uba_device *ui; 1254744Swnj { 1264744Swnj tjtout[ui->ui_unit] = ui->ui_mi->um_ctlr; 1274744Swnj } 1284744Swnj 1294744Swnj /* 1304744Swnj * Open the device with exclusive access. 1314744Swnj */ 1324744Swnj utopen(dev, flag) 1334744Swnj dev_t dev; 1344744Swnj int flag; 1354744Swnj { 1364744Swnj register int tjunit = TJUNIT(dev); 1374744Swnj register struct uba_device *ui; 1384744Swnj register struct tj_softc *sc; 1394744Swnj int olddens, dens; 1404744Swnj 1414744Swnj if (tjunit >= NTJ || (sc = &tj_softc[tjunit])->sc_openf || 1424744Swnj (ui = tjdinfo[tjunit]) == 0 || ui->ui_alive == 0) { 1434744Swnj u.u_error = ENXIO; 1444744Swnj return; 1454744Swnj } 1464744Swnj olddens = sc->sc_dens; 1474744Swnj dens = sc->sc_dens = utdens[(minor(dev)&(T_1600BPI|T_6250BPI))>>3]| 1484744Swnj PDP11FMT|(ui->ui_slave&07); 1494744Swnj get: 1504744Swnj utcommand(dev, UT_SENSE, 1); 1514744Swnj if (sc->sc_dsreg&UTDS_PIP) { 1524744Swnj sleep((caddr_t) &lbolt, PZERO+1); 1534744Swnj goto get; 1544744Swnj } 1554744Swnj sc->sc_dens = olddens; 1564744Swnj if ((sc->sc_dsreg&UTDS_MOL) == 0) { 1574744Swnj uprintf("tj%d: not online\n", tjunit); 1584744Swnj u.u_error = EIO; 1594744Swnj return; 1604744Swnj } 1614744Swnj if ((flag&FWRITE) && (sc->sc_dsreg&UTDS_WRL)) { 1624744Swnj uprintf("tj%d: no write ring\n", tjunit); 1634744Swnj u.u_error = EIO; 1644744Swnj return; 1654744Swnj } 1664744Swnj if ((sc->sc_dsreg&UTDS_BOT) == 0 && (flag&FWRITE) && 1674744Swnj dens != sc->sc_dens) { 1684744Swnj uprintf("tj%d: can't change density in mid-tape\n", tjunit); 1694744Swnj u.u_error = EIO; 1704744Swnj return; 1714744Swnj } 1724744Swnj sc->sc_openf = 1; 1734744Swnj sc->sc_blkno = (daddr_t)0; 1744744Swnj sc->sc_nxrec = INF; 1754744Swnj sc->sc_lastiow = 0; 1764744Swnj sc->sc_dens = dens; 177*4746Ssam /* 178*4746Ssam * For 6250 bpi take exclusive use of the UNIBUS. 179*4746Ssam */ 180*4746Ssam ui->ui_driver->ud_xclu = (dens&(T_1600BPI|T_6250BPI)) == T_6250BPI; 1814744Swnj } 1824744Swnj 1834744Swnj utclose(dev, flag) 1844744Swnj register dev_t dev; 1854744Swnj register flag; 1864744Swnj { 1874744Swnj register struct tj_softc *sc = &tj_softc[TJUNIT(dev)]; 1884744Swnj 1894744Swnj if (flag == FWRITE || ((flag&FWRITE) && sc->sc_lastiow)) { 1904744Swnj utcommand(dev, UT_WEOF, 1); 1914744Swnj utcommand(dev, UT_WEOF, 1); 1924744Swnj utcommand(dev, UT_SREV, 1); 1934744Swnj } 1944744Swnj if ((minor(dev)&T_NOREWIND) == 0) 1954744Swnj utcommand(dev, UT_REW, 0); 1964744Swnj sc->sc_openf = 0; 1974744Swnj } 1984744Swnj 1994744Swnj utcommand(dev, com, count) 2004744Swnj dev_t dev; 2014744Swnj int com, count; 2024744Swnj { 2034744Swnj register struct buf *bp; 2044744Swnj 2054744Swnj bp = &cutbuf[UTUNIT(dev)]; 2064744Swnj (void) spl5(); 2074744Swnj while (bp->b_flags&B_BUSY) { 2084744Swnj if(bp->b_repcnt == 0 && (bp->b_flags&B_DONE)) 2094744Swnj break; 2104744Swnj bp->b_flags |= B_WANTED; 2114744Swnj sleep((caddr_t)bp, PRIBIO); 2124744Swnj } 2134744Swnj bp->b_flags = B_BUSY|B_READ; 2144744Swnj (void) spl0(); 2154744Swnj bp->b_dev = dev; 2164744Swnj bp->b_command = com; 2174744Swnj bp->b_repcnt = count; 2184744Swnj bp->b_blkno = 0; 2194744Swnj utstrategy(bp); 2204744Swnj if (count == 0) 2214744Swnj return; 2224744Swnj iowait(bp); 2234744Swnj if (bp->b_flags&B_WANTED) 2244744Swnj wakeup((caddr_t)bp); 2254744Swnj bp->b_flags &= B_ERROR; 2264744Swnj } 2274744Swnj 2284744Swnj /* 2294744Swnj * Queue a tape operation. 2304744Swnj */ 2314744Swnj utstrategy(bp) 2324744Swnj register struct buf *bp; 2334744Swnj { 2344744Swnj int tjunit = TJUNIT(bp->b_dev); 2354744Swnj register struct uba_ctlr *um; 2364744Swnj register struct buf *dp; 2374744Swnj 2384744Swnj /* 2394744Swnj * Put transfer at end of unit queue 2404744Swnj */ 2414744Swnj dp = &tjutab[tjunit]; 2424744Swnj bp->av_forw = NULL; 2434744Swnj (void) spl5(); 2444744Swnj if (dp->b_actf == NULL) { 2454744Swnj dp->b_actf = bp; 2464744Swnj /* 2474744Swnj * Transport not active, so... 2484744Swnj * put at end of controller queue 2494744Swnj */ 2504744Swnj dp->b_forw = NULL; 2514744Swnj um = tjdinfo[tjunit]->ui_mi; 2524744Swnj if (um->um_tab.b_actf == NULL) 2534744Swnj um->um_tab.b_actf = dp; 2544744Swnj else 2554744Swnj um->um_tab.b_actl->b_forw = dp; 2564744Swnj um->um_tab.b_actl = dp; 2574744Swnj } else 2584744Swnj dp->b_actl->av_forw = bp; 2594744Swnj dp->b_actl = bp; 2604744Swnj /* 2614744Swnj * If the controller is not busy, set it going. 2624744Swnj */ 263*4746Ssam if (um->um_tab.b_state == 0) 2644744Swnj utstart(um); 2654744Swnj (void) spl0(); 2664744Swnj } 2674744Swnj 2684744Swnj utstart(um) 2694744Swnj register struct uba_ctlr *um; 2704744Swnj { 271*4746Ssam register struct utdevice *addr; 2724744Swnj register struct buf *bp, *dp; 2734744Swnj register struct tj_softc *sc; 2744744Swnj struct uba_device *ui; 2754744Swnj int tjunit; 2764744Swnj daddr_t blkno; 2774744Swnj 2784744Swnj loop: 2794744Swnj /* 2804744Swnj * Scan controller queue looking for units with 2814744Swnj * transaction queues to dispatch 2824744Swnj */ 2834744Swnj if ((dp = um->um_tab.b_actf) == NULL) 2844744Swnj return; 2854744Swnj if ((bp = dp->b_actf) == NULL) { 2864744Swnj um->um_tab.b_actf = dp->b_forw; 2874744Swnj goto loop; 2884744Swnj } 289*4746Ssam addr = (struct utdevice *)um->um_addr; 2904744Swnj tjunit = TJUNIT(bp->b_dev); 2914744Swnj ui = tjdinfo[tjunit]; 2924744Swnj sc = &tj_softc[tjunit]; 2934744Swnj /* note slave select, density, and format were merged on open */ 294*4746Ssam addr->uttc = sc->sc_dens; 295*4746Ssam sc->sc_dsreg = addr->utds; 296*4746Ssam sc->sc_erreg = addr->uter; 297*4746Ssam /* watch this, sports fans */ 298*4746Ssam sc->sc_resid = bp->b_flags&B_READ ? 299*4746Ssam bp->b_bcount - ((-addr->utfc)&0xffff) : -addr->utwc<<1; 3004744Swnj /* 3014744Swnj * Default is that last command was NOT a write command; 3024744Swnj * if we do a write command we will notice this in utintr(). 3034744Swnj */ 3044744Swnj sc->sc_lastiow = 0; 305*4746Ssam printd("utstart: cmd=%o openf=%d ds=%b\n", bp->b_command>>1, 306*4746Ssam sc->sc_openf, addr->utds, UTDS_BITS); 307*4746Ssam if (sc->sc_openf < 0 || (addr->utds&UTDS_MOL) == 0) { 3084744Swnj /* 3094744Swnj * Have had a hard error on a non-raw tape 3104744Swnj * or the tape unit is now unavailable 3114744Swnj * (e.g. taken off line). 3124744Swnj */ 3134744Swnj bp->b_flags |= B_ERROR; 3144744Swnj goto next; 3154744Swnj } 3164744Swnj if (bp == &cutbuf[UTUNIT(bp->b_dev)]) { 3174744Swnj /* 3184744Swnj * Execute a control operation with the specified 3194744Swnj * count. 3204744Swnj */ 3214744Swnj if (bp->b_command == UT_SENSE) 3224744Swnj goto next; 3234744Swnj /* 3244744Swnj * Set next state; handle timeouts 3254744Swnj */ 3264744Swnj if (bp->b_command == UT_REW) 327*4746Ssam um->um_tab.b_state = SREW; 3284744Swnj else 329*4746Ssam um->um_tab.b_state = SCOM; 3304744Swnj /* NOTE: this depends on the ut command values */ 3314744Swnj if (bp->b_command >= UT_SFORW && bp->b_command <= UT_SREVF) 332*4746Ssam addr->utfc = -bp->b_repcnt; 3334744Swnj goto dobpcmd; 3344744Swnj } 3354744Swnj /* 3364744Swnj * The following checks boundary conditions for operations 3374744Swnj * on non-raw tapes. On raw tapes the initialization of 3384744Swnj * sc->sc_nxrec by utphys causes them to be skipped normally 3394744Swnj * (except in the case of retries). 3404744Swnj */ 3414744Swnj if (dbtofsb(bp->b_blkno) > sc->sc_nxrec) { 3424744Swnj /* can't read past end of file */ 3434744Swnj bp->b_flags |= B_ERROR; 3444744Swnj bp->b_error = ENXIO; 3454744Swnj goto next; 3464744Swnj } 3474744Swnj if (dbtofsb(bp->b_blkno) == sc->sc_nxrec && (bp->b_flags&B_READ)) { 3484744Swnj /* read at eof returns 0 count */ 3494744Swnj bp->b_resid = bp->b_bcount; 3504744Swnj clrbuf(bp); 3514744Swnj goto next; 3524744Swnj } 3534744Swnj if ((bp->b_flags&B_READ) == 0) 3544744Swnj sc->sc_nxrec = dbtofsb(bp->b_blkno)+1; 3554744Swnj /* 3564744Swnj * If the tape is correctly positioned, set up all the 3574744Swnj * registers but the csr, and give control over to the 3584744Swnj * UNIBUS adaptor routines, to wait for resources to 3594744Swnj * start I/O. 3604744Swnj */ 3614744Swnj if ((blkno = sc->sc_blkno) == dbtofsb(bp->b_blkno)) { 362*4746Ssam addr->utwc = -(((bp->b_bcount)+1)>>1); 363*4746Ssam addr->utfc = -bp->b_bcount; 3644744Swnj if ((bp->b_flags&B_READ) == 0) { 3654744Swnj /* 3664744Swnj * On write error retries erase the 367*4746Ssam * inter-record gap before rewriting. 3684744Swnj */ 369*4746Ssam if (um->um_tab.b_errcnt) { 370*4746Ssam printd("utstart: erase\n"); 371*4746Ssam if (um->um_tab.b_state != SERASED) { 372*4746Ssam um->um_tab.b_state = SERASED; 373*4746Ssam addr->utcs1 = UT_ERASE|UT_IE|UT_GO; 374*4746Ssam return; 375*4746Ssam } 376*4746Ssam printd("utstart: erased\n"); 377*4746Ssam } 378*4746Ssam um->um_cmd = UT_WCOM; 3794744Swnj } else 3804744Swnj um->um_cmd = UT_RCOM; 381*4746Ssam um->um_tab.b_state = SIO; 3824744Swnj (void) ubago(ui); 3834744Swnj return; 3844744Swnj } 3854744Swnj /* 3864744Swnj * Tape positioned incorrectly; seek forwards or 3874744Swnj * backwards to the correct spot. This happens for 3884744Swnj * raw tapes only on error retries. 3894744Swnj */ 3904744Swnj printd("utstart: seek, blkno=%d dbtofsb=%d\n", blkno, 3914744Swnj dbtofsb(bp->b_blkno)); 392*4746Ssam um->um_tab.b_state = SSEEK; 3934744Swnj if (blkno < dbtofsb(bp->b_blkno)) { 394*4746Ssam addr->utfc = blkno - dbtofsb(bp->b_blkno); 3954744Swnj bp->b_command = UT_SFORW; 3964744Swnj } else { 397*4746Ssam addr->utfc = dbtofsb(bp->b_blkno) - blkno; 3984744Swnj bp->b_command = UT_SREV; 3994744Swnj } 4004744Swnj 4014744Swnj dobpcmd: 4024744Swnj /* 4034744Swnj * Perform the command setup in bp. 4044744Swnj */ 4054744Swnj printd("utstart: dobpcmd\n"); 406*4746Ssam addr->utcs1 = bp->b_command|UT_IE|UT_GO; 4074744Swnj return; 4084744Swnj next: 4094744Swnj /* 4104744Swnj * Advance to the next command in the slave queue, 4114744Swnj * posting notice and releasing resources as needed. 4124744Swnj */ 4134744Swnj printd("utstart: next\n"); 4144744Swnj if (um->um_ubinfo) 4154744Swnj ubadone(um); 4164744Swnj um->um_tab.b_errcnt = 0; 4174744Swnj dp->b_actf = bp->av_forw; 4184744Swnj iodone(bp); 4194744Swnj goto loop; 4204744Swnj } 4214744Swnj 4224744Swnj /* 4234744Swnj * Start operation on controller -- 4244744Swnj * UNIBUS resources have been allocated. 4254744Swnj */ 4264744Swnj utdgo(um) 4274744Swnj register struct uba_ctlr *um; 4284744Swnj { 4294744Swnj register struct utdevice *addr = (struct utdevice *)um->um_addr; 4304744Swnj 4314744Swnj addr->utba = (u_short) um->um_ubinfo; 4324744Swnj addr->utcs1 = um->um_cmd|((um->um_ubinfo>>8)&0x30)|UT_IE|UT_GO; 433*4746Ssam printd("utdgo: cs1=%b fc=%x wc=%x\n", addr->utcs1, UT_BITS, 434*4746Ssam addr->utfc, addr->utwc); 4354744Swnj } 4364744Swnj 4374744Swnj /* 4384744Swnj * Ut interrupt handler 4394744Swnj */ 4404744Swnj /*ARGSUSED*/ 4414744Swnj utintr(ut11) 4424744Swnj int ut11; 4434744Swnj { 4444744Swnj struct buf *dp; 4454744Swnj register struct buf *bp; 4464744Swnj register struct uba_ctlr *um = utminfo[ut11]; 4474744Swnj register struct utdevice *addr; 4484744Swnj register struct tj_softc *sc; 449*4746Ssam u_short tjunit, cs2, cs1; 4504744Swnj register state; 4514744Swnj 4524744Swnj if ((dp = um->um_tab.b_actf) == NULL) 4534744Swnj return; 4544744Swnj bp = dp->b_actf; 4554744Swnj tjunit = TJUNIT(bp->b_dev); 4564744Swnj addr = (struct utdevice *)tjdinfo[tjunit]->ui_addr; 4574744Swnj sc = &tj_softc[tjunit]; 4584744Swnj /* 4594744Swnj * Record status... 4604744Swnj */ 4614744Swnj sc->sc_dsreg = addr->utds; 4624744Swnj sc->sc_erreg = addr->uter; 463*4746Ssam sc->sc_resid = bp->b_flags&B_READ ? 464*4746Ssam bp->b_bcount - (-addr->utfc)&0xffff : -addr->utwc<<1; 4654744Swnj printd("utintr: state=%d cs1=%b cs2=%b ds=%b er=%b\n", 466*4746Ssam um->um_tab.b_state, 4674744Swnj ((struct utdevice *) addr)->utcs1, UT_BITS, 4684744Swnj ((struct utdevice *) addr)->utcs2, UTCS2_BITS, 4694744Swnj ((struct utdevice *) addr)->utds, UTDS_BITS, 4704744Swnj ((struct utdevice *) addr)->uter, UTER_BITS); 471*4746Ssam if ((bp->b_flags&B_READ) == 0) 4724744Swnj sc->sc_lastiow = 1; 473*4746Ssam state = um->um_tab.b_state; 474*4746Ssam um->um_tab.b_state = 0; 4754744Swnj /* 4764744Swnj * Check for errors... 4774744Swnj */ 4784744Swnj if ((addr->utds&UTDS_ERR) || (addr->utcs1&UT_TRE)) { 4794744Swnj /* 480*4746Ssam * If we hit a tape mark or EOT update our position. 4814744Swnj */ 482*4746Ssam if (addr->utds&(UTDS_TM|UTDS_EOT)) { 4834744Swnj /* 484*4746Ssam * Set blkno and nxrec 4854744Swnj */ 4864744Swnj if (bp == &cutbuf[UTUNIT(bp->b_dev)]) { 4874744Swnj if (sc->sc_blkno > dbtofsb(bp->b_blkno)) { 4884744Swnj sc->sc_nxrec = 4894744Swnj dbtofsb(bp->b_blkno) - addr->utfc; 4904744Swnj sc->sc_blkno = sc->sc_nxrec; 4914744Swnj } else { 4924744Swnj sc->sc_blkno = 4934744Swnj dbtofsb(bp->b_blkno) + addr->utfc; 4944744Swnj sc->sc_nxrec = sc->sc_blkno-1; 4954744Swnj } 496*4746Ssam } else 4974744Swnj sc->sc_nxrec = dbtofsb(bp->b_blkno); 4984744Swnj state = SCOM; /* force completion */ 4994744Swnj addr->utcs1 = UT_CLEAR|UT_GO; 5004744Swnj /* 501*4746Ssam * Stuff so we can unstuff later 502*4746Ssam * to get the residual. 5034744Swnj */ 504*4746Ssam addr->utwc = (-bp->b_bcount)>>1; 5054744Swnj addr->utfc = -bp->b_bcount; 506*4746Ssam if (sc->sc_dsreg&UTDS_EOT) 507*4746Ssam goto harderror; 5084744Swnj goto opdone; 5094744Swnj } 510*4746Ssam cs2 = addr->utcs2; /* save it for printf below */ 511*4746Ssam if ((cs1 = addr->utcs1)&UT_TRE) 512*4746Ssam addr->utcs2 |= UTCS2_CLR; 5134744Swnj addr->utcs1 = UT_CLEAR|UT_GO; /* must clear ERR bit */ 514*4746Ssam printd("after clear: cs1=%b er=%b cs2=%b ds=%b\n", 515*4746Ssam addr->utcs1, UT_BITS, addr->uter, UTER_BITS, 516*4746Ssam addr->utcs2, UTCS2_BITS, addr->utds, UTDS_BITS); 5174744Swnj /* 5184744Swnj * If we were reading from a raw tape and the only error 5194744Swnj * was that the record was too long, then we don't consider 5204744Swnj * this an error. 5214744Swnj */ 5224744Swnj if (bp == &rutbuf[UTUNIT(bp->b_dev)] && (bp->b_flags&B_READ) && 5234744Swnj (sc->sc_erreg&UTER_FCE)) 5244744Swnj goto ignoreerr; 5254744Swnj /* 526*4746Ssam * Fix up errors which occur due to backspacing "over" the 527*4746Ssam * front of the tape. 528*4746Ssam */ 529*4746Ssam if ((sc->sc_dsreg&UTDS_BOT) && 530*4746Ssam (bp->b_command == UT_SREV || bp->b_command == UT_SREV) && 531*4746Ssam ((sc->sc_erreg &= ~(UTER_NEF|UTER_FCE)) == 0)) 532*4746Ssam goto opdone; 533*4746Ssam /* 5344744Swnj * Retry soft errors up to 8 times 5354744Swnj */ 5364744Swnj if ((sc->sc_erreg&UTER_HARD) == 0 && state == SIO) { 5374744Swnj if (++um->um_tab.b_errcnt < 7) { 5384744Swnj sc->sc_blkno++; 5394744Swnj ubadone(um); 5404744Swnj goto opcont; 5414744Swnj } 5424744Swnj } else 543*4746Ssam harderror: 5444744Swnj /* 5454744Swnj * Hard or non-I/O errors on non-raw tape 546*4746Ssam * cause it to close; also, reading off the 547*4746Ssam * end of the tape. 5484744Swnj */ 549*4746Ssam if (sc->sc_openf > 0 && 550*4746Ssam bp != &rutbuf[UTUNIT(bp->b_dev)] || 551*4746Ssam sc->sc_dsreg&UTDS_EOT) 5524744Swnj sc->sc_openf = -1; 5534744Swnj /* 5544744Swnj * Couldn't recover error. 5554744Swnj */ 556*4746Ssam printf("ut%d: hard error bn%d cs1=%b er=%b cs2=%b ds=%b\n", 557*4746Ssam tjunit, bp->b_blkno, cs1, UT_BITS, sc->sc_erreg, 558*4746Ssam UTER_BITS, cs2, UTCS2_BITS, sc->sc_dsreg, UTDS_BITS); 5594744Swnj bp->b_flags |= B_ERROR; 5604744Swnj goto opdone; 5614744Swnj } 5624744Swnj ignoreerr: 5634744Swnj /* 5644744Swnj * Advance tape control FSM. 5654744Swnj */ 5664744Swnj switch (state) { 5674744Swnj 5684744Swnj case SIO: /* read/write increments tape block # */ 5694744Swnj sc->sc_blkno++; 570*4746Ssam break; 5714744Swnj 5724744Swnj case SCOM: /* forw/rev space updates current position */ 5734744Swnj if (bp == &cutbuf[UTUNIT(bp->b_dev)]) 5744744Swnj switch (bp->b_command) { 5754744Swnj 5764744Swnj case UT_SFORW: 5774744Swnj sc->sc_blkno -= bp->b_repcnt; 5784744Swnj break; 5794744Swnj 5804744Swnj case UT_SREV: 5814744Swnj sc->sc_blkno += bp->b_repcnt; 5824744Swnj break; 5834744Swnj } 584*4746Ssam break; 5854744Swnj 5864744Swnj case SSEEK: 5874744Swnj sc->sc_blkno = dbtofsb(bp->b_blkno); 5884744Swnj goto opcont; 5894744Swnj 590*4746Ssam case SERASE: 591*4746Ssam /* 592*4746Ssam * Completed erase of the inter-record gap due to a 593*4746Ssam * write error; now retry the write operation. 594*4746Ssam */ 595*4746Ssam um->um_tab.b_state = SERASED; 596*4746Ssam goto opcont; 597*4746Ssam 598*4746Ssam case SREW: /* clear attention bit */ 599*4746Ssam addr->utcs1 = UT_CLEAR|UT_GO; 600*4746Ssam break; 601*4746Ssam 6024744Swnj default: 603*4746Ssam printf("bad state %d\n", state); 6044744Swnj panic("utintr"); 6054744Swnj } 6064744Swnj 6074744Swnj opdone: 6084744Swnj /* 6094744Swnj * Reset error count and remove 6104744Swnj * from device queue 6114744Swnj */ 6124744Swnj um->um_tab.b_errcnt = 0; 613*4746Ssam dp->b_actf = bp->av_forw; 614*4746Ssam bp->b_resid = bp->b_command&B_READ ? 615*4746Ssam bp->b_bcount - ((-addr->utfc)&0xffff) : -addr->utwc<<1; 6164744Swnj ubadone(um); 6174744Swnj iodone(bp); 6184744Swnj /* 6194744Swnj * Circulate slave to end of controller queue 6204744Swnj * to give other slaves a chance 6214744Swnj */ 6224744Swnj um->um_tab.b_actf = dp->b_forw; 6234744Swnj if (dp->b_actf) { 6244744Swnj dp->b_forw = NULL; 6254744Swnj if (um->um_tab.b_actf == NULL) 6264744Swnj um->um_tab.b_actf = dp; 6274744Swnj else 6284744Swnj um->um_tab.b_actl->b_forw = dp; 6294744Swnj um->um_tab.b_actl = dp; 6304744Swnj } 6314744Swnj if (um->um_tab.b_actf == 0) 6324744Swnj return; 6334744Swnj opcont: 6344744Swnj utstart(um); 6354744Swnj } 6364744Swnj 6374744Swnj /* 6384744Swnj * Raw interface for a read 6394744Swnj */ 6404744Swnj utread(dev) 6414744Swnj dev_t dev; 6424744Swnj { 6434744Swnj utphys(dev); 6444744Swnj if (u.u_error) 6454744Swnj return; 6464744Swnj physio(utstrategy, &rutbuf[UTUNIT(dev)], dev, B_READ, minphys); 6474744Swnj } 6484744Swnj 6494744Swnj /* 6504744Swnj * Raw interface for a write 6514744Swnj */ 6524744Swnj utwrite(dev) 6534744Swnj { 6544744Swnj utphys(dev); 6554744Swnj if (u.u_error) 6564744Swnj return; 6574744Swnj physio(utstrategy, &rutbuf[UTUNIT(dev)], dev, B_WRITE, minphys); 6584744Swnj } 6594744Swnj 6604744Swnj /* 6614744Swnj * Check for valid device number dev and update our notion 6624744Swnj * of where we are on the tape 6634744Swnj */ 6644744Swnj utphys(dev) 6654744Swnj dev_t dev; 6664744Swnj { 6674744Swnj register int tjunit = TJUNIT(dev); 6684744Swnj register struct tj_softc *sc; 6694744Swnj register struct uba_device *ui; 6704744Swnj 6714744Swnj if (tjunit >= NTJ || (ui=tjdinfo[tjunit]) == 0 || ui->ui_alive == 0) { 6724744Swnj u.u_error = ENXIO; 6734744Swnj return; 6744744Swnj } 6754744Swnj sc = &tj_softc[tjunit]; 676*4746Ssam sc->sc_blkno = dbtofsb(u.u_offset>>9); 677*4746Ssam sc->sc_nxrec = sc->sc_blkno+1; 6784744Swnj } 6794744Swnj 6804744Swnj /*ARGSUSED*/ 6814744Swnj utioctl(dev, cmd, addr, flag) 6824744Swnj dev_t dev; 6834744Swnj caddr_t addr; 6844744Swnj { 6854744Swnj register struct tj_softc *sc = &tj_softc[TJUNIT(dev)]; 6864744Swnj register struct buf *bp = &cutbuf[UTUNIT(dev)]; 6874744Swnj register callcount; 6884744Swnj int fcount; 6894744Swnj struct mtop mtop; 6904744Swnj struct mtget mtget; 6914744Swnj /* we depend of the values and order of the MT codes here */ 6924744Swnj static utops[] = 6934744Swnj {UT_WEOF,UT_SFORWF,UT_SREVF,UT_SFORW,UT_SREV,UT_REW,UT_REWOFFL,UT_SENSE}; 6944744Swnj 6954744Swnj switch (cmd) { 6964744Swnj 6974744Swnj case MTIOCTOP: 6984744Swnj if (copyin((caddr_t)addr, (caddr_t)&mtop, sizeof(mtop))) { 6994744Swnj u.u_error = EFAULT; 7004744Swnj return; 7014744Swnj } 7024744Swnj switch(mtop.mt_op) { 7034744Swnj 7044744Swnj case MTWEOF: 7054744Swnj callcount = mtop.mt_count; 7064744Swnj fcount = 1; 7074744Swnj break; 7084744Swnj 7094744Swnj case MTFSF: case MTBSF: 7104744Swnj case MTFSR: case MTBSR: 7114744Swnj callcount = 1; 7124744Swnj fcount = mtop.mt_count; 7134744Swnj break; 7144744Swnj 7154744Swnj case MTREW: case MTOFFL: case MTNOP: 7164744Swnj callcount = 1; 7174744Swnj fcount = 1; 7184744Swnj break; 7194744Swnj 7204744Swnj default: 7214744Swnj u.u_error = ENXIO; 7224744Swnj return; 7234744Swnj } 7244744Swnj if (callcount <= 0 || fcount <= 0) { 7254744Swnj u.u_error = ENXIO; 7264744Swnj return; 7274744Swnj } 7284744Swnj while (--callcount >= 0) { 7294744Swnj utcommand(dev, utops[mtop.mt_op], fcount); 730*4746Ssam /* note this depends on the mtop values */ 731*4746Ssam if ((mtop.mt_op >= MTFSF || mtop.mt_op <= MTBSR) && 7324744Swnj bp->b_resid) { 7334744Swnj u.u_error = EIO; 7344744Swnj break; 7354744Swnj } 7364744Swnj if ((bp->b_flags&B_ERROR) || (sc->sc_dsreg&UTDS_BOT)) 7374744Swnj break; 7384744Swnj } 7394744Swnj geterror(bp); 7404744Swnj return; 7414744Swnj 7424744Swnj case MTIOCGET: 7434744Swnj mtget.mt_dsreg = sc->sc_dsreg; 7444744Swnj mtget.mt_erreg = sc->sc_erreg; 7454744Swnj mtget.mt_resid = sc->sc_resid; 7464744Swnj mtget.mt_type = MT_ISUT; 7474744Swnj if (copyout((caddr_t)&mtget, addr, sizeof(mtget))) 7484744Swnj u.u_error = EFAULT; 7494744Swnj return; 7504744Swnj 7514744Swnj default: 7524744Swnj u.u_error = ENXIO; 7534744Swnj } 7544744Swnj } 7554744Swnj 7564744Swnj utreset(uban) 7574744Swnj int uban; 7584744Swnj { 7594744Swnj register struct uba_ctlr *um; 7604744Swnj register ut11, tjunit; 7614744Swnj register struct uba_device *ui; 7624744Swnj register struct buf *dp; 7634744Swnj 7644744Swnj for (ut11 = 0; ut11 < NUT; ut11++) { 7654744Swnj if ((um = utminfo[ut11]) == 0 || um->um_alive == 0 || 7664744Swnj um->um_ubanum != uban) 7674744Swnj continue; 7684744Swnj printf(" ut%d", ut11); 769*4746Ssam um->um_tab.b_state = 0; 7704744Swnj um->um_tab.b_actf = um->um_tab.b_actl = 0; 7714744Swnj if (um->um_ubinfo) { 7724744Swnj printf("<%d>", (um->um_ubinfo>>28)&0xf); 7734744Swnj ubadone(um); 7744744Swnj } 7754744Swnj ((struct utdevice *)(um->um_addr))->utcs1 = UT_CLEAR|UT_GO; 776*4746Ssam ((struct utdevice *)(um->um_addr))->utcs2 |= UTCS2_CLR; 7774744Swnj for (tjunit = 0; tjunit < NTJ; tjunit++) { 7784744Swnj if ((ui = tjdinfo[tjunit]) == 0 || ui->ui_mi != um || 7794744Swnj ui->ui_alive == 0) 7804744Swnj continue; 7814744Swnj dp = &tjutab[tjunit]; 782*4746Ssam dp->b_state = 0; 7834744Swnj dp->b_forw = 0; 7844744Swnj if (um->um_tab.b_actf == NULL) 7854744Swnj um->um_tab.b_actf = dp; 7864744Swnj else 7874744Swnj um->um_tab.b_actl->b_forw = dp; 7884744Swnj um->um_tab.b_actl = dp; 7894744Swnj if (tj_softc[tjunit].sc_openf > 0) 7904744Swnj tj_softc[tjunit].sc_openf = -1; 7914744Swnj } 7924744Swnj utstart(um); 7934744Swnj } 7944744Swnj } 7954744Swnj 7964744Swnj /* 7974744Swnj * Do a stand-alone core dump to tape -- 7984744Swnj * from here down, routines are used only in dump context 7994744Swnj */ 8004744Swnj #define DBSIZE 20 8014744Swnj 8024744Swnj utdump() 8034744Swnj { 8044744Swnj register struct uba_device *ui; 8054744Swnj register struct uba_regs *up; 806*4746Ssam register struct utdevice *addr; 8074744Swnj int blk, num = maxfree; 8084744Swnj int start = 0; 8094744Swnj 8104744Swnj #define phys(a,b) ((b)((int)(a)&0x7fffffff)) 8114744Swnj if (tjdinfo[0] == 0) 8124744Swnj return (ENXIO); 8134744Swnj ui = phys(tjdinfo[0], struct uba_device *); 8144744Swnj up = phys(ui->ui_hd, struct uba_hd *)->uh_physuba; 8154744Swnj ubainit(); 8164744Swnj DELAY(1000000); 817*4746Ssam utwait(addr); 818*4746Ssam addr = (struct utdevice *)ui->ui_physaddr; 819*4746Ssam /* 820*4746Ssam * Be sure to set the appropriate density here. We use 821*4746Ssam * 6250, but maybe it should be done at 1600 to insure the 822*4746Ssam * tape can be read by most any other tape drive available. 823*4746Ssam */ 824*4746Ssam addr->uttc = UT_GCR|PDP11FMT; /* implicit slave 0 or-ed in */ 825*4746Ssam addr->utcs1 = UT_CLEAR|UT_GO; 8264744Swnj while (num > 0) { 8274744Swnj blk = num > DBSIZE ? DBSIZE : num; 828*4746Ssam utdwrite(start, blk, addr, up); 829*4746Ssam if ((addr->utds&UTDS_ERR) || (addr->utcs1&UT_TRE)) 830*4746Ssam return(EIO); 8314744Swnj start += blk; 8324744Swnj num -= blk; 8334744Swnj } 834*4746Ssam uteof(addr); 835*4746Ssam uteof(addr); 836*4746Ssam utwait(addr); 837*4746Ssam if ((addr->utds&UTDS_ERR) || (addr->utcs1&UT_TRE)) 8384744Swnj return(EIO); 839*4746Ssam addr->utcs1 = UT_REW|UT_GO; 8404744Swnj return (0); 8414744Swnj } 8424744Swnj 843*4746Ssam utdwrite(dbuf, num, addr, up) 8444744Swnj register dbuf, num; 845*4746Ssam register struct utdevice *addr; 8464744Swnj struct uba_regs *up; 8474744Swnj { 8484744Swnj register struct pte *io; 8494744Swnj register int npf; 8504744Swnj 851*4746Ssam utwait(addr); 8524744Swnj io = up->uba_map; 8534744Swnj npf = num + 1; 8544744Swnj while (--npf != 0) 8554744Swnj *(int *)io++ = (dbuf++ | (1<<UBAMR_DPSHIFT) | UBAMR_MRV); 8564744Swnj *(int *)io = 0; 857*4746Ssam addr->utwc = -((num*NBPG)>>1); 858*4746Ssam addr->utfc = -(num*NBPG); 859*4746Ssam addr->utba = 0; 860*4746Ssam addr->utcs1 = UT_WCOM|UT_GO; 8614744Swnj } 8624744Swnj 863*4746Ssam utwait(addr) 864*4746Ssam struct utdevice *addr; 8654744Swnj { 8664744Swnj register s; 8674744Swnj 8684744Swnj do 869*4746Ssam s = addr->utds; 8704744Swnj while ((s&UTDS_DRY) == 0); 8714744Swnj } 8724744Swnj 873*4746Ssam uteof(addr) 874*4746Ssam struct utdevice *addr; 8754744Swnj { 8764744Swnj 877*4746Ssam utwait(addr); 878*4746Ssam addr->utcs1 = UT_WEOF|UT_GO; 8794744Swnj } 8804744Swnj #endif 881