📄 wd33c93lib2.c
字号:
/* reject attempts to select self */ if (devBusId == pScsiCtrl->scsiCtrlBusId) { errnoSet (S_scsiLib_ILLEGAL_BUS_ID); return (ERROR); } /* convert timeout period to SBIC units, in place */ sbicSelTimeOutCvt (pSbic, selTimeOut, &selTimeOut); selectCmd = (msgLen != 0) ? SBIC_CMD_SEL_ATN : SBIC_CMD_SELECT; /* * Check SBIC is IDLE, then issue SELECT command. Must be done with * interrupts locked to avoid races with the reselection ISR. * * If the controller is not IDLE, do not issue a select command. Just * return - there must be a (re)selection event pending which will be * handled exactly as if it occurred just after the select command was * issued. */ lockKey = intLock (); switch (pSbic->state) { case SBIC_STATE_IDLE: pSbic->state = SBIC_STATE_SELECT_PENDING; sbicRegWrite (pSbic, SBIC_REG_DEST_ID, (UINT8) devBusId); sbicRegWrite (pSbic, SBIC_REG_TO_PERIOD, (UINT8) selTimeOut); sbicCommand (pSbic, selectCmd); status = OK; break; case SBIC_STATE_CONNECTED: status = OK; break; case SBIC_STATE_SELECT_PENDING: default: status = ERROR; break; } intUnlock (lockKey); if (status != OK) { SCSI_DEBUG_MSG ("sbicDevSelect: invalid controller state (%d)\n", pSbic->state, 0, 0, 0, 0, 0); } return (status); }/********************************************************************************* sbicBusControl - miscellaneous low-level SCSI bus control operations** Allows the caller to:-** - assert the SCSI ATN signal (indicating a message out is available)* - negate the SCSI ACK signal (indicating a message in has been read)** The "operation" parameter is a bitmask which allows any combination of these* functions to be carried out with a single call. Note that the order in* which things are done is important: ATN is asserted (if specified) before* ACK is negated. (This allows an incoming SCSI message to be rejected.)** NOTE: since the SBIC is not connected to the SCSI bus RST signal, a board-* specific function is used to implement the BUS_RESET command. As a result* of calling this function, something must notify the SCSI manager task that* a bus reset has occurred ...** RETURNS: OK (no error conditions)*/LOCAL STATUS sbicBusControl ( SCSI_CTRL *pScsiCtrl, /* ptr to SBIC info */ int operation /* bitmask for operation(s) to be performed */ ) { FAST SBIC *pSbic = (SBIC *) pScsiCtrl; if (operation & SCSI_BUS_RESET) (*pSbic->sysScsiBusReset) (pSbic->sysScsiResetArg); if (operation & SCSI_BUS_ASSERT_ATN) sbicCommand (pSbic, SBIC_CMD_SET_ATN); if (operation & SCSI_BUS_NEGATE_ACK) sbicCommand (pSbic, SBIC_CMD_NEG_ACK); return (OK); }/********************************************************************************* sbicXferParamsQuery - get (synchronous) transfer parameters** Updates the synchronous transfer parameters suggested in the call to match* the SBIC's capabilities. Transfer period is in SCSI units (multiples of* 4 ns).** Note: the transfer period is made longer and the offset is made smaller if* the SBIC cannot handle the specified values.** RETURNS: OK*/LOCAL STATUS sbicXferParamsQuery ( SCSI_CTRL *pScsiCtrl, /* ptr to SBIC info */ UINT8 *pOffset, /* max REQ/ACK offset [in/out] */ UINT8 *pPeriod /* min transfer period [in/out] */ ) { UINT8 unused; (void) sbicXferParamsCvt ((SBIC *) pScsiCtrl, pOffset, pPeriod, &unused); return (OK); } /********************************************************************************* sbicXferParamsSet - set transfer parameters** Programs the SBIC to use the specified transfer parameters. An offset* of zero specifies asynchronous transfer (period is then irrelevant).* Transfer period is in SCSI units (multiples of 4 ns).** RETURNS: OK if transfer parameters are OK, else ERROR.*/LOCAL STATUS sbicXferParamsSet ( SCSI_CTRL *pScsiCtrl, /* ptr to SBIC info */ UINT8 offset, /* max REQ/ACK offset */ UINT8 period /* min transfer period */ ) { FAST SBIC *pSbic = (SBIC *) pScsiCtrl; UINT8 xferParams; if (!sbicXferParamsCvt (pSbic, &offset, &period, &xferParams)) { /* should never happen */ errnoSet (S_scsiLib_ILLEGAL_PARAMETER); return (ERROR); } sbicRegWrite (pSbic, SBIC_REG_SYNC_XFER, xferParams); return (OK); }/********************************************************************************* sbicInfoXfer - transfer information bytes to/from target via SCSI bus** Executes a "Transfer Info" command to read (write) bytes from (to) the* SCSI bus. If the transfer phase is DATA IN or DATA OUT and there is a* DMA routine available, DMA is used - otherwise it's a tight programmed* i/o loop.** Returns when the transfer has completed; i.e., the last byte has been* received (sent) and ACK is still asserted in the case of MESSAGE IN* transfers, or a request for a new information transfer has occurred (for* all other types of transfer).** The returned value is the number of bytes actually transferred across the* SCSI bus. In the case of DATA phases, this may be less than the requested* transfer length because the target may change the information phase "early"* (to send a message in, for example). For non-DATA transfers, if the byte* count transferred is not what was asked for, it is probably an error* condition (left to the caller's discretion).** RETURNS: Number of bytes transferred across SCSI bus, or ERROR.*/LOCAL int sbicInfoXfer ( FAST SCSI_CTRL *pScsiCtrl, /* ptr to SCSI controller info */ int phase, /* SCSI phase being transferred */ FAST UINT8 *pBuf, /* ptr to byte buffer for i/o */ UINT bufLength /* number of bytes to be transferred */ ) { FAST SBIC *pSbic; /* ptr to SBIC info */ FAST UINT8 auxStatus; /* SBIC auxiliary status */ UINT bytesLeft; /* bytes not transferred across bus */ int direction; /* input (READ) or output (WRITE) */ BOOL usingDMA; /* using DMA for this transfer ? */ pSbic = (SBIC *) pScsiCtrl; SCSI_DEBUG_MSG ("sbicInfoXfer: phase = %d, buf = 0x%x, length = %d\n", phase, (int) pBuf, bufLength, 0, 0, 0); /* * Set transfer count, issue Transfer Info command */ if (sbicXferCountSet (pSbic, (UINT) bufLength) != OK) { errnoSet (S_scsiLib_ILLEGAL_PARAMETER); return (ERROR); } sbicCommand (pSbic, SBIC_CMD_XFER_INFO); /* * Identify phase, set transfer mode accordingly */ switch (phase) { case SCSI_DATA_OUT_PHASE: direction = WRITE; usingDMA = (pSbic->sysScsiDmaStart != NULL); break; case SCSI_DATA_IN_PHASE: direction = READ; usingDMA = (pSbic->sysScsiDmaStart != NULL); break; case SCSI_COMMAND_PHASE: case SCSI_MSG_OUT_PHASE: direction = WRITE; usingDMA = FALSE; break; case SCSI_STATUS_PHASE: case SCSI_MSG_IN_PHASE: direction = READ; usingDMA = FALSE; break; default: SCSI_MSG ("sbicInfoXfer: invalid bus phase (%d)\n", phase, 0, 0, 0, 0, 0); return (ERROR); } /* * Start DMA, if used, or programmed i/o loop to transfer data */ pSbic->xferPending = TRUE; if (usingDMA) { if ((*pSbic->sysScsiDmaStart) (pSbic->sysScsiDmaArg, pBuf, bufLength, direction) != OK) { SCSI_MSG ("sbicInfoXfer: unable to start DMA transfer\n", 0, 0, 0, 0, 0, 0); return (ERROR); } } else if (direction == READ) { *pSbic->pAdrsReg = SBIC_REG_DATA; while ((auxStatus = *pSbic->pAuxStatReg) & SBIC_AUX_STAT_BUSY) { if (auxStatus & SBIC_AUX_STAT_DBUF_READY) *pBuf++ = *pSbic->pRegFile; } } else /* (direction == WRITE) */ { *pSbic->pAdrsReg = SBIC_REG_DATA; while ((auxStatus = *pSbic->pAuxStatReg) & SBIC_AUX_STAT_BUSY) { if (auxStatus & SBIC_AUX_STAT_DBUF_READY) *pSbic->pRegFile = *pBuf++; } } /* * Wait for transfer to complete: find out how many bytes transferred, * abort DMA transfer if necessary. */ semTake (&pSbic->xferDoneSem, WAIT_FOREVER); sbicXferCountGet (pSbic, &bytesLeft); if (bytesLeft > bufLength) { SCSI_MSG ("sbicInfoXfer: invalid count (len = 0x%0x, left = 0x%0x)\n", bufLength, bytesLeft, 0, 0, 0, 0); return (ERROR); } if (usingDMA && (bytesLeft > 0)) (*pSbic->sysScsiDmaAbort) (pSbic->sysScsiDmaArg); SCSI_DEBUG_MSG ("sbicInfoXfer: transferred %d of %d byte(s)\n", bufLength - bytesLeft, bufLength, 0, 0, 0, 0); return (bufLength - bytesLeft); }/********************************************************************************* sbicSelTimeOutCvt - convert a select timeout from usec to SBIC units ** The conversion formula is given on p. 11 of the Western Digital WD33C93* manual. Note that 0 specifies the default setting of 250 usec. Also,* the SBIC accepts up to a 8-bit timeout, so a maximum value of 0xff is* returned in *pTimeOutSetting.*/LOCAL void sbicSelTimeOutCvt ( SBIC *pSbic, /* ptr to SBIC info */ FAST UINT timeOutInUsec, /* timeout in microsecs */ FAST UINT *pTimeOutSetting /* ptr to result */ ) { FAST UINT tempSelTimeOut; /* temp. select timeout setting */ if (timeOutInUsec == (UINT) 0) timeOutInUsec = pSbic->defSelTimeOut; tempSelTimeOut = (timeOutInUsec / (80 * pSbic->clkPeriod)) + 1; if (tempSelTimeOut > 0xff) tempSelTimeOut = 0xff; *pTimeOutSetting = tempSelTimeOut; }/******************************************************************************** sbicXferParamsCvt - convert transfer period from SCSI to SBIC units** Given a "suggested" REQ/ACK offset and transfer period (in SCSI units of* 4 ns), return the nearest offset and transfer period the SBIC is capable of* using. Also return the corresponding value of the SBIC's Synchronous* Transfer register.** An offset of zero specifies asynchronous transfer, in which case the period* is irrelevant. Otherwise, the offset may be clipped to be within the* maximum limit the SBIC is capable of.** The transfer period is normally rounded towards longer periods if the SBIC* is not capable of using the exact specified value. The period may also be* clipped to be within the SBIC's maximum and minimum limits according to its* clock period.** If either the offset or period need to be clipped, the value FALSE is* retuned as this may reflect an error condition.** Currently assumes that the SBIC is a 33C93, not a 33C93A (i.e. fixed clock* divider of 2) - see 33C93A data sheet pp 9, 13 and 14 for further details.** RETURNS: TRUE if input period is within SBIC's range, else FALSE*/LOCAL BOOL sbicXferParamsCvt ( FAST SBIC *pSbic, /* ptr to SBIC info */ FAST UINT8 *pOffset, /* REQ/ACK offset */ FAST UINT8 *pPeriod, /* xfer period, SCSI units (x 4 ns) */ FAST UINT8 *pXferParams /* corresponding Sync Xfer Reg value */ ) { UINT8 offset = *pOffset; UINT8 period = *pPeriod; BOOL inRange = TRUE; UINT8 xferParams; if (offset == SCSI_SYNC_XFER_ASYNC_OFFSET) xferParams = SBIC_SYNC_XFER_PARAMS_ASYNC; else { UINT clkCycles; /* * Round (and perhaps clip) transfer period to suit SBIC * * Note: split into two stages to avoid associativity problems, * and careful to round up rather than truncate result. */ clkCycles = (period * 4) + (pSbic->clkPeriod - 1); clkCycles /= pSbic->clkPeriod; if (clkCycles < SBIC_MIN_XFER_PERIOD) { clkCycles = SBIC_MIN_XFER_PERIOD; inRange = FALSE; } else if (clkCycles > SBIC_MAX_XFER_PERIOD) /* very unlikely ! */ { clkCycles = SBIC_MAX_XFER_PERIOD; inRange = FALSE; } period = (clkCycles * pSbic->clkPeriod) / 4; /* * Clip offset (if necessary) to suit SBIC */ if (offset > SBIC_MAX_REQ_ACK_OFFSET) { offset = SBIC_MAX_REQ_ACK_OFFSET; inRange = FALSE; } /* * Merge offset and cycles to form Sync. Transfer Reg. contents */ if (clkCycles == SBIC_MAX_XFER_PERIOD) clkCycles = 0; xferParams = (clkCycles << SBIC_SYNC_XFER_PERIOD_SHIFT) | offset;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -