📄 scsi.c
字号:
*/ else { sc->sz_modsel.pll = 0x0e; sc->sz_modsel.bufmode = 0; sc->sz_modsel.rdeclen = 0x08; sc->sz_modsel.vulen = 1; sc->sz_modsel.nof = 0; } break; case SZ_TRKSEL: return(SZ_RET_ERR); break; case SZ_RESUNIT: case SZ_RELUNIT:#ifdef SZDEBUG printd("sz_bldpkt: unimplemented command 0x%x\n", cmd);#endif SZDEBUG return (SZ_RET_ERR); break; default:#ifdef SZDEBUG printd("sz_bldpkt: unknown command = 0x%x\n", cmd);#endif SZDEBUG return (SZ_RET_ERR); break; } /* * We assume each unit is a single SCSI target device, i.e., * no sub-units. So logical unit is always zero. * * sz_read is used here to get to the fields in the * structure for all commands. * TODO: need to worry about 12 byte commands? */ len = sz_cdb_length (cmd, targid); if (len == 6) { len = 6; sc->sz_t_read.lun = 0; sc->sz_t_read.link = 0; sc->sz_t_read.flag = 0; sc->sz_t_read.mbz = 0; } sc->sz_opcode = cmd; /* * Save the current command bytes for the error log. * Unless we are doing a request sense after a failed command. */ byteptr = (u_char *)&sc->sz_command; if (sc->sc_curcmd[targid] == sc->sc_actcmd[targid]) { for (i = 0; i < len; i++) sc->sc_cmdlog[targid][i] = *byteptr++; for (; i < 12; i++) sc->sc_cmdlog[targid][i] = 0; } return(SZ_SUCCESS);}int sz_szf_print = 0;int sz_sp_szflags = 0;int sz_rw_szflags = 0;/* * * Name: sz_start -Start routine (state machine) * * Abstract: This routine is called to start or continue * a data transfer or other type of command on a * SCSI target device. This routine is a state * machine for each target on each SCSI controller. * The state machine keeps track of the target's * progress thru all the steps necessary to do * a data transfer or other type command. * The state machine states/events are defined in * the scsireg.h and scsivar.h header files. * * Inputs: * * sc Pointer to the controller's sz_softc structure. * targid Device SCSI target ID (0 - 7), or * -1 start command on next ready target device. * * Outputs: * * Calls sc_port_start routine to start command. * Much sz_softc context is modified. * * Return Values: None. * * Side Effects: * Must backoff command and try later if bus busy. * */sz_start(sc, targid)register struct sz_softc *sc;int targid;{ int cntlr = sc - &sz_softc[0]; register struct sz_regs *szaddr = (struct sz_regs *)szmem + cntlr; register struct nb_regs *sziaddr = (struct nb_regs *)nexus; register struct buf *bp; register struct buf *dp; char *stv; /* virtual address of st page tables */ struct pte *pte, *mpte; /* used for mapping page table entries */ char *bufp; unsigned v; int o, npf; struct proc *rp; int ssrv; int i; int bcount; short count; u_char *byteptr; struct uba_ctlr *um = szminfo[cntlr]; struct uba_device *ui; int unit; int part; daddr_t blkno; daddr_t badblk; struct scsi_devtab *sdp; struct tape_opt_tab *todp; struct tape_info *ddp; /* * Just return if bus reset in progress. */ if (sc->sc_rip) return; if (targid != -1) { /* we know the target ID */ /* * If the target ID is not minus one, just enter * the next state/event for that target. */ unit = sc->sc_unit[targid]; dp = &szutab[unit]; bp = dp->b_actf; } else { /* * If the target ID is minus one, we were called from * szintr() because the currently active target * disconnected. Start I/O on the next target * with a request pending, but not already active. */ targid = sc->sc_lastid; while (1) { targid++; if (targid >= NDPS) targid = 0; if ((1 << targid) == sc->sc_sysid) continue; /* skip initiator */ if (targid == sc->sc_lastid) return; /* no target ready to start I/O */ /* TODO: need more checking (could be unknown target) */ if (sc->sc_alive[targid] == 0) continue; /* non existent target */ unit = sc->sc_unit[targid]; dp = (struct buf *)&szutab[unit]; if (dp->b_active) continue; /* target already active */ if (dp->b_actf == NULL) continue; /* no request pending on this target */ /* found one, start it */ bp = dp->b_actf; break; } } /* * The sz_start routine is a state machine for each * target device on each bus. * The states are kept in the sz_softc structure. * The state variables used in this state machine are: * * sc->sc_xstate used to dispatch to major states * sc->sc_xevent used to dispatch to minor (sub)states */ for (;;) { /* forever */ switch(sc->sc_xstate[targid]) {/**/ case SZ_NEXT: switch(sc->sc_xevent[targid]) { case SZ_CONT: /* Call the particular device completion routine. */ bp = dp->b_actf; (*sc->device_comp[targid])(bp); /* * Find the next target with an I/O reuest pending * and is not already busy, and start I/O on it. */ if (sc->sc_szflags[targid] & SZ_BUSYBUS) return; /* bus busy don't start next command */ targid = sc->sc_lastid; while (1) { targid++; if (targid >= NDPS) targid = 0; if ((1 << targid) == sc->sc_sysid) continue; /* skip initiator */ /* TODO: need more checking, see comment above */ if (sc->sc_alive[targid] == 0) continue; /* non existent target */ unit = sc->sc_unit[targid]; dp = (struct buf *)&szutab[unit]; if (dp->b_actf == NULL) { dp->b_active = 0; if (targid == sc->sc_lastid) return; else continue; } else { if (dp->b_active) { if (targid == sc->sc_lastid) return; else continue; } } bp = dp->b_actf; /* MUST set buffer pointer */ dp->b_active = 1; /* TODO: also set in LATER */ break; } continue; /* takes us to SZ_BEGIN or SZ_RW_CONT */ break; /* NOTREACHED */ case SZ_BEGIN: if ((bp = dp->b_actf) == NULL) { /* TODO: debug - remove later */ printf("sz_start: SZ_BEGIN with null bp "); printf("t=%d un=%d last=%d dp=%x\n", targid, unit, sc->sc_lastid, dp); dp->b_active = 0; return; } sc->sc_szflags[targid] &= ~SZ_BUSYBUS; if((sc->sc_flags[targid] & DEV_EOM) && !((sc->sc_flags[targid] & DEV_CSE) || (dis_eot_sz[unit] & DISEOT))) { bp->b_resid = bp->b_bcount; bp->b_error = ENOSPC; bp->b_flags |= B_ERROR; sc->sc_xevent[targid] = SZ_CONT; break; } /* * For nbufio... If we are not at eot and DEV_CSE * is set then clear the flag. In other words the * dev_cse bit was set in response to a file mark * detected during nbufio...The reasoning for the * clear of the flag here is because of the previous * check and all operations begin life at this * state. We must notice the flag with the previous * if because the user have set the flag to get * past eot. This is allowed, else if not at * eot the flag was set for nbufio and if we * what to notice eot we have to clear the flag. */ if( !(sc->sc_flags[targid] & DEV_EOM) && (sc->sc_flags[targid] & DEV_CSE)) { sc->sc_flags[targid] &= ~DEV_CSE; } if((bp->b_flags&B_READ) && (bp->b_flags&B_RAWASYNC) && ((sc->sc_category_flags[targid]&DEV_TPMARK) || (sc->sc_flags[targid]&DEV_HARDERR))) { bp->b_error = EIO; bp->b_flags |= B_ERROR; sc->sc_xevent[targid] = SZ_CONT; break; } /* * For fix block tape units check to see if tape mark * is pending.... IF so this is the next read and post * TPMARK AND clear the pending flag... */ if((bp->b_flags & B_READ) && (bp->b_flags & B_RAWASYNC) && (sc->sc_category_flags[targid] & TPMARK_PENDING)){ sc->sc_resid[targid] = bp->b_bcount; sc->sc_xstate[targid] = SZ_NEXT; sc->sc_xevent[targid] = SZ_CONT; sc->sc_category_flags[targid] |= DEV_TPMARK; sc->sc_category_flags[targid] &= ~TPMARK_PENDING; break; } /* ok we have gotten to this point.... If * we had nbuf io and a tpmark then we can't * get to here..... This is an operation, whether * control or (sync)read/write it does not matter we * must clear out the tpmark indicator... * Ie straight reads will report the tpmark and * the next operation should get the next record.. * tpmark cleared... for nbuf io we can't get here * and the only way to clear it is with MTCSE ioctl. * see mtio(4). Now there are side effects to this * that mimicks the tmscp class driver.. on a tmscp * class device if a control operation/write operation * issused to the device the tpmark indicator is cleared * and the operation is declared a success so if we * clear DEV_TPMARK here we are golden...Give the man * a beer. */ sc->sc_category_flags[targid] &= ~DEV_TPMARK; /* * clear out the short record indicator left over * from last operation........... * This is done for reads/writes control and * nbuf i/o. Reasoning is that we report the short * record status for the previous command. It * it is up to the programmer to get status if they * want it. If not any new operation must start * with a clean slate so clear short rec indicator. */ sc->sc_category_flags[targid] &= ~DEV_SHRTREC; /* log progress */ sc->sc_progress = time; /* Check to see what path is required for this command. */ if((bp == &cszbuf[unit]) || ((sc->sc_bbr_active[targid] == 1) && (sc->sc_bbr_oper[targid] == SZ_SP_START))) { /* * execute control operation with the specified count */ if (bp->b_comand == SZ_REWIND) { sc->sc_flags[targid] &= ~DEV_EOM; } else { } sc->sc_xstate[targid] = SZ_SP_START; sc->sc_xevent[targid] = SZ_CONT; break; } /* * If it's not a control operation, it must be * data. */ sc->sc_xstate[targid] = SZ_RW_START; sc->sc_xevent[targid] = SZ_CONT; break; default: ; } break; /* * Start a non data transfer command. */ case SZ_SP_START: /* remember where we are in case target returns busy status */ sc->sc_pxstate[targid] = SZ_SP_START; /* * Forgive the gotos, but..... * The SELWAIT? flags tell us we must restart processing * at the point where we call scsistart, because either * a select timed out or completed during the timeout. */ if (sc->sc_xevent[targid] == SZ_SELWAIT1) goto sz_sp_s1; if (sc->sc_xevent[targid] == SZ_SELWAIT2) goto sz_sp_s2; /* * Clear DEV_WRITTEN flag only for commands * which would actually change tape position * from the last (possible) write. * TODO: deal with SZ_ERASE, SZ_UNLOAD, SZ_VFY */ if (sc->sc_devtyp[targid] & SZ_TAPE) { switch (bp->b_comand) { case SZ_REWIND: case SZ_WFM: case SZ_P_BSPACEF: case SZ_P_FSPACEF: case SZ_P_BSPACER: case SZ_P_FSPACER: sc->sc_flags[targid] &= ~DEV_WRITTEN; default: break; } } /* * The write to b_resid has to follow the * read of b_command. This is because both * b_command and b_resid are the same field * (overloaded). The write to b_resid destroys * the data in b_command. This is a black * hole waiting to be fallen into! * * NOTE: * * We fell into the black hole described above! * This code can be executed more than once for * a command. This happens if the command has to * be restarted after waiting for the bus to free up. * To avoid the black hole, the read of b_command * is only done the first time thru this code path. * * NOTE: * * The above bug is fixed. The command is now called * b_comand and is stored in the b_gid buffer field. */ if (sc->sc_selstat[targid] != SZ_BBWAIT) { sc->sc_bp[targid] = bp; } sc->sc_curcmd[targid] = bp->b_comand; dp->b_active = 1; sc->sc_lastid = targid; bp->b_resid = 0; sc->sc_resid[targid] = 0; /* makes debug output look cleaner */ sc->sc_c_status[targid] = SZ_GOOD; /* TODO: debug - check for left over bits in szflags */ sz_sp_szflags = sc->sc_szflags[targid]; if (sz_szf_print && (sc->sc_szflags[targid] != SZ_NORMAL)) printf("SP_START: szflags = %X\n", sc->sc_szflags[targid]); /* TODO: end of debug */ sc->sc_szflags[targid] = SZ_NORMAL; sz_bldpkt(sc, targid, sc->sc_curcmd[targid], 0, bp->b_bcount);sz_sp_s1: /* restart due to select timeout */ bp = sc->sc_bp[targid]; /* restore buffer pointer */ if(sc->sc_rzspecial[targid]) { struct mode_sel_sns_params *msp; msp = (struct mode_sel_sns_params *)sc->sc_rzparams[targid]; stv = sc->sc_rambuff + sc->sc_dboff[targid]; if(sc->sc_actcmd[targid] == SZ_MODSNS)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -