📄 tmscp.c
字号:
} tmscpaddr->tmscpsa = ((int)&sc->sc_tmscp->tmscp_ca.ca_ringbase) | ((cpu == VAX_780 || cpu == VAX_8600) ? TMSCP_PI : 0); sc->sc_state = S_STEP2; return; /* Controller was in step 2 last, see if its gone to step 3 */ case S_STEP2:# define STEP2MASK 0174377# define STEP2GOOD (TMSCP_STEP3|TMSCP_IE|(sc->sc_ivec/4)) for (i = 0; i < 150; i++) { if ((tmscpaddr->tmscpsa&STEP2MASK) != STEP2GOOD) { /* still in step 2 (wait 1/100 sec) */ DELAY(10000);# ifdef DEBUG printd("still in step 2, delaying\n");# endif DEBUG } else break; } if (i > 149) { sc->sc_state = S_IDLE; printf("failed to initialize, in step2: sa 0x%x", tmscpaddr->tmscpsa); wakeup((caddr_t)um); return; } tmscpaddr->tmscpsa = ((int)&sc->sc_tmscp->tmscp_ca.ca_ringbase)>>16; sc->sc_state = S_STEP3; return; /* Controller was in step 3 last, see if its gone to step 4 */ case S_STEP3:# define STEP3MASK 0174000# define STEP3GOOD TMSCP_STEP4 for (i = 0; i < 150; i++) { if ((tmscpaddr->tmscpsa&STEP3MASK) != STEP3GOOD) { /* still in step 3 (wait 1/100 sec) */ DELAY(10000);# ifdef DEBUG printd("still in step 3, delaying\n");# endif DEBUG } else break; } if (i > 149) { sc->sc_state = S_IDLE; printf("failed to initialize, in step3: sa 0x%x", tmscpaddr->tmscpsa); wakeup((caddr_t)um); return; } /* * Get microcode version and model number of controller; * Signal initialization complete (_GO) (to the controller); * ask for Last Fail response if tmscperror is set; * Set state to "set controller characteristics". */ tmscpmicro[d] = tmscpaddr->tmscpsa; tmscpaddr->tmscpsa = TMSCP_GO | (tmscperror? TMSCP_LF : 0); sc->sc_state = S_SCHAR;# ifdef DEBUG printd("tmscpintr: completed state %d \n", sc->sc_state); printd("tmscp%d Version %d model %d\n",d,tmscpmicro[d]&0xF, (tmscpmicro[d]>>4) & 0xF);# endif /* * Initialize the data structures (response and command queues). */ ttm = sc->sc_tmscp; for (i = 0; i < NRSP; i++) { tm->tmscp_ca.ca_rspdsc[i] = TMSCP_OWN | TMSCP_INT | (long)&ttm->tmscp_rsp[i].mscp_cmdref; tm->tmscp_rsp[i].mscp_dscptr = &tm->tmscp_ca.ca_rspdsc[i]; tm->tmscp_rsp[i].mscp_header.tmscp_msglen = mscp_msglen; } for (i = 0; i < NCMD; i++) { tm->tmscp_ca.ca_cmddsc[i] = TMSCP_INT | (long)&ttm->tmscp_cmd[i].mscp_cmdref; tm->tmscp_cmd[i].mscp_dscptr = &tm->tmscp_ca.ca_cmddsc[i]; tm->tmscp_cmd[i].mscp_header.tmscp_msglen = mscp_msglen; tm->tmscp_cmd[i].mscp_header.tmscp_vcid = 1; } bp = &tmscpwtab[d]; bp->av_forw = bp->av_back = bp; sc->sc_lastcmd = 1; sc->sc_lastrsp = 0; mp = &tmscp[um->um_ctlr].tmscp_cmd[0]; mp->mscp_unit = mp->mscp_modifier = 0; mp->mscp_flags = 0; mp->mscp_version = 0; mp->mscp_cntflgs = M_CF_ATTN|M_CF_MISC|M_CF_THIS; /* * A host time out value of 0 means that the controller will not * time out. This is ok for the TK50. */ mp->mscp_hsttmo = 0; mp->mscp_time.val[0] = 0; mp->mscp_time.val[1] = 0; mp->mscp_cntdep = 0; mp->mscp_opcode = M_OP_STCON; *((long *)mp->mscp_dscptr) |= TMSCP_OWN|TMSCP_INT; i = tmscpaddr->tmscpip; /* initiate polling */ return; case S_SCHAR: case S_RUN: break; default: printf("tmscp%d: interrupt in unknown state %d ignored\n",d,sc->sc_state); return; } /* end switch */ /* * The controller state is S_SCHAR or S_RUN */ /* * If the error bit is set in the SA register then print an error * message and reinitialize the controller. */ if (tmscpaddr->tmscpsa&TMSCP_ERR) { printf("tmscp%d: fatal error (%o)\n", d, tmscpaddr->tmscpsa&0xffff); tmscpaddr->tmscpip = 0; wakeup((caddr_t)um); } /* * Check for a buffer purge request. (Won't happen w/ TK50 on Q22 bus) */ if (tm->tmscp_ca.ca_bdp) { UBAPURGE(um->um_hd->uh_uba, tm->tmscp_ca.ca_bdp); tm->tmscp_ca.ca_bdp = 0; tmscpaddr->tmscpsa = 0; /* signal purge complete */ } /* * Check for response ring transition. */ if (tm->tmscp_ca.ca_rspint) { tm->tmscp_ca.ca_rspint = 0; for (i = sc->sc_lastrsp;; i++) { i %= NRSP; if (tm->tmscp_ca.ca_rspdsc[i]&TMSCP_OWN) break; tmscprsp(um, tm, sc, i); tm->tmscp_ca.ca_rspdsc[i] |= TMSCP_OWN; } sc->sc_lastrsp = i; } /* * Check for command ring transition. */ if (tm->tmscp_ca.ca_cmdint) {# ifdef DEBUG printd("tmscpintr: command ring transition\n");# endif tm->tmscp_ca.ca_cmdint = 0; } if(tmscp_cp_wait) wakeup((caddr_t)&tmscp_cp_wait); (void) tmscpstart(um);}/* * Open a tmscp device and set the unit online. If the controller is not * in the run state, call init to initialize the tmscp controller first. *//* ARGSUSED */tmscpopen(dev, flag) dev_t dev; int flag;{ register int unit; register struct uba_device *ui; register struct tmscp_softc *sc; register struct tms_info *tms; register struct mscp *mp; register struct uba_ctlr *um; struct tmscpdevice *tmscpaddr; int s,i; unit = TMSUNIT(dev);# ifdef DEBUG printd("tmscpopen unit %d\n",unit); if(tmscpdebug)DELAY(10000);# endif if (unit >= NTMS || (ui = tmsdinfo[unit]) == 0 || ui->ui_alive == 0) return (ENXIO); tms = &tms_info[ui->ui_unit]; if (tms->tms_openf) return (EBUSY); sc = &tmscp_softc[ui->ui_ctlr]; tms->tms_openf = 1; tms->tms_tpr = tprintf_open(); s = spl5(); if (sc->sc_state != S_RUN) { if (sc->sc_state == S_IDLE) if(!tmscpinit(ui->ui_ctlr)) { printf("tmscp controller failed to init\n"); (void) splx(s); tms->tms_openf = 0; return(ENXIO); } /* * Wait for initialization to complete */ timeout(wakeup,(caddr_t)ui->ui_mi,11*hz); /* to be sure*/ sleep((caddr_t)ui->ui_mi, 0); if (sc->sc_state != S_RUN) { (void) splx(s); tms->tms_openf = 0; return (EIO); } } /* * Check to see if the device is really there. * this code was taken from Fred Canters 11 driver */ um = ui->ui_mi; tmscpaddr = (struct tmscpdevice *) um->um_addr; (void) splx(s); if(ui->ui_flags == 0) { s = spl5(); while(0 ==(mp = tmscpgetcp(um))) { tmscp_cp_wait++; sleep((caddr_t)&tmscp_cp_wait,PSWP+1); tmscp_cp_wait--; } (void) splx(s); mp->mscp_opcode = M_OP_ONLIN; mp->mscp_unit = ui->ui_slave; mp->mscp_cmdref = (long) & tms->tms_type; /* need to sleep on something */# ifdef DEBUG printd("tmscpopen: bring unit %d online\n",ui->ui_unit);# endif *((long *) mp->mscp_dscptr ) |= TMSCP_OWN | TMSCP_INT; i = tmscpaddr->tmscpip;#ifdef lint i = i;#endif /* * To make sure we wake up, timeout in 240 seconds. * Wakeup in tmscprsp routine. * 240 seconds (4 minutes) is necessary since a rewind * can take a few minutes. */ timeout(wakeup,(caddr_t) mp->mscp_cmdref,240 * hz); sleep((caddr_t) mp->mscp_cmdref,PSWP+1); } if(ui->ui_flags == 0) { tms->tms_openf = 0; return(ENXIO); /* Didn't go online */ } tms->tms_lastiow = 0; /* * If the high density device is not specified, set unit to low * density. This is done as an "internal" ioctl command so * that the command setup and response handling * is done thru "regular" command routines. */ if ((minor(dev) & T_HIDENSITY) == 0) tmscpcommand(dev, TMS_LOWDENSITY, 1); else tmscpcommand(dev, TMS_HIDENSITY, 1); return (0);}/* * Close tape device. * * If tape was open for writing or last operation was * a write, then write two EOF's and backspace over the last one. * Unless this is a non-rewinding special file, rewind the tape. * * NOTE: * We want to be sure that any serious exception is cleared on the * close. A Clear Serious Exception (CSE) modifier is always done on * the rewind command. For the non-rewind case we check to see if the * "serex" field is set in the softc struct; if it is then issue a noop * command with the CSE modifier. * Make the tape available to others, by clearing openf flag. */tmscpclose(dev, flag) register dev_t dev; register flag;{ register struct tms_info *tms; register struct uba_device *ui; ui = tmsdinfo[TMSUNIT(dev)];# ifdef DEBUG printd("tmscpclose: ctlr = %d\n",TMSCPCTLR(dev)); printd("tmscpclose: unit = %d\n",TMSUNIT(dev)); if(tmscpdebug)DELAY(10000);# endif tms = &tms_info[ui->ui_unit]; if (flag == FWRITE || (flag&FWRITE) && tms->tms_lastiow) { /* device, command, count */ tmscpcommand (dev, TMS_WRITM, 1); tmscpcommand (dev, TMS_WRITM, 1); tmscpcommand (dev, TMS_BSR, 1); } if ((minor(dev)&T_NOREWIND) == 0) /* * Don't hang waiting for rewind complete. */ tmscpcommand(dev, TMS_REW, 0); else if (tms->tms_serex) {# ifdef DEBUG printd("tmscpclose: clearing serex\n"); if(tmscpdebug)DELAY(10000);# endif tmscpcommand(dev, TMS_CSE, 1); } tprintf_close(tms->tms_tpr); tms->tms_openf = 0; return (0);}/* * Execute a command on the tape drive a specified number of times. * This routine sets up a buffer and calls the strategy routine which * links the buffer onto the drive's buffer queue. * The start routine will take care of creating a tmscp command packet * with the command. The start routine is called by the strategy or the * interrupt routine. */tmscpcommand (dev, com, count) register dev_t dev; int com, count;{ register struct uba_device *ui; register struct buf *bp; register int s; int unit = TMSUNIT(dev); ui = tmsdinfo[unit]; bp = &ctmscpbuf[ui->ui_ctlr]; s = spl5(); while (bp->b_flags&B_BUSY) { /* * This special check is because B_BUSY never * gets cleared in the non-waiting rewind case. */ if (bp->b_bcount == 0 && (bp->b_flags&B_DONE)) break; bp->b_flags |= B_WANTED; sleep((caddr_t)bp, PRIBIO); } bp->b_flags = B_BUSY|B_READ; splx(s); /* * Load the buffer. The b_count field gets used to hold the command * count. the b_resid field gets used to hold the command mneumonic. * These 2 fields are "known" to be "safe" to use for this purpose. * (Most other drivers also use these fields in this way.) */ bp->b_dev = dev; bp->b_bcount = count; bp->b_resid = com; bp->b_blkno = 0; tmscpstrategy(bp); /* * In case of rewind from close, don't wait. * This is the only case where count can be 0. */ if (count == 0) return; iowait(bp); if (bp->b_flags&B_WANTED) wakeup((caddr_t)bp); bp->b_flags &= B_ERROR;}/* * Find an unused command packet */struct mscp *tmscpgetcp(um) struct uba_ctlr *um;{ register struct mscp *mp; register struct tmscpca *cp; register struct tmscp_softc *sc; register int i; int s; s = spl5(); cp = &tmscp[um->um_ctlr].tmscp_ca; sc = &tmscp_softc[um->um_ctlr]; /* * If no credits, can't issue any commands * until some outstanding commands complete. */ i = sc->sc_lastcmd;# ifdef DEBUG printd10("tmscpgetcp: %d credits remain\n", sc->sc_credits);# endif if(((cp->ca_cmddsc[i]&(TMSCP_OWN|TMSCP_INT))==TMSCP_INT) && (sc->sc_credits >= 2)) { sc->sc_credits--; /* This commits to issuing a command */ cp->ca_cmddsc[i] &= ~TMSCP_INT; mp = &tmscp[um->um_ctlr].tmscp_cmd[i]; mp->mscp_unit = mp->mscp_modifier = 0; mp->mscp_opcode = mp->mscp_flags = 0; mp->mscp_bytecnt = mp->mscp_buffer = 0; sc->sc_lastcmd = (i + 1) % NCMD; (void) splx(s); return(mp); } (void) splx(s); return(NULL);}/* * Initialize a TMSCP device. Set up UBA mapping registers, * initialize data structures, and start hardware * initialization sequence. */tmscpinit (d) int d; /* index to the controller */{ register struct tmscp_softc *sc; register struct tmscp *t; /* communications area; cmd & resp packets */ struct tmscpdevice *tmscpaddr; struct uba_ctlr *um; sc = &tmscp_softc[d]; um = tmscpminfo[d]; um->um_tab.b_active++; t = &tmscp[d]; tmscpaddr = (struct tmscpdevice *)um->um_addr; if (sc->sc_mapped == 0) { /* * Map the communications area and command * and response packets into Unibus address * space. */ sc->sc_ubainfo = uballoc(um->um_ubanum, (caddr_t)t, sizeof (struct tmscp), 0); sc->sc_tmscp = (struct tmscp *)(UBAI_ADDR(sc->sc_ubainfo)); sc->sc_mapped = 1; } /* * Start the hardware initialization sequence. */ tmscpaddr->tmscpip = 0; /* start initialization */ while((tmscpaddr->tmscpsa & TMSCP_STEP1) == 0) {# ifdef DEBUG printd("tmscpinit: tmscpsa = 0%o\n",tmscpaddr->tmscpsa); DELAY(100000);# endif if(tmscpaddr->tmscpsa & TMSCP_ERR) return(0); /* CHECK */ } tmscpaddr->tmscpsa=TMSCP_ERR|(NCMDL2<<11)|(NRSPL2<<8)|TMSCP_IE|(sc->sc_ivec/4); /* * Initialization continues in the interrupt routine. */ sc->sc_state = S_STEP1; sc->sc_credits = 0; return(1);}/* * Start I/O operation * This code is convoluted. The majority of it was copied from the uda driver. */tmscpstart(um) register struct uba_ctlr *um;{ register struct buf *bp, *dp; register struct mscp *mp; register struct tmscp_softc *sc; register struct tms_info *tms; register struct uba_device *ui; struct tmscpdevice *tmscpaddr; struct tmscp *tm = &tmscp[um->um_ctlr]; int i,tempi; char ioctl; /* flag: set true if its an IOCTL command */ sc = &tmscp_softc[um->um_ctlr]; for(;;) { if ((dp = um->um_tab.b_actf) == NULL) { /* * Release unneeded UBA resources and return * (drive was inactive) */ um->um_tab.b_active = 0; break; } if ((bp = dp->b_actf) == NULL) { /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -