scsi2Lib.c
上传用户:nvosite88
上传日期:2007-01-17
资源大小:4983k
文件大小:130k
- LOCAL int scsiCmdLength [8] =
- {
- SCSI_GROUP_0_CMD_LENGTH,
- SCSI_GROUP_1_CMD_LENGTH,
- NONE,
- NONE,
- NONE,
- SCSI_GROUP_5_CMD_LENGTH,
- NONE,
- NONE
- };
- if ((*pCmdLength = cmdLength = scsiCmdLength [groupCode]) == NONE)
- return (ERROR);
- if ((groupCode == 0) && (logBlockAdrs > 0x1fffff || xferLength > 0xff))
- return (ERROR);
- else if (xferLength > 0xffff)
- return (ERROR);
- scsiCmd[0] = opCode;
- scsiCmd[1] = (UINT8) ((LUN & 0x7) << 5);
- switch (groupCode)
- {
- case 0:
- scsiCmd[1] |= (UINT8) ((logBlockAdrs >> 16) & 0x1f);
- scsiCmd[2] = (UINT8) ((logBlockAdrs >> 8) & 0xff);
- scsiCmd[3] = (UINT8) ((logBlockAdrs ) & 0xff);
- scsiCmd[4] = (UINT8) xferLength;
- scsiCmd[5] = controlByte;
- break;
- case 1:
- case 5:
- scsiCmd[1] |= (UINT8) (relAdrs ? 1 : 0);
- scsiCmd[2] = (UINT8) ((logBlockAdrs >> 24) & 0xff);
- scsiCmd[3] = (UINT8) ((logBlockAdrs >> 16) & 0xff);
- scsiCmd[4] = (UINT8) ((logBlockAdrs >> 8) & 0xff);
- scsiCmd[5] = (UINT8) ((logBlockAdrs ) & 0xff);
- scsiCmd[6] = (UINT8) 0;
- if (groupCode == 5)
- {
- scsiCmd[7] = (UINT8) 0;
- scsiCmd[8] = (UINT8) 0;
- }
- scsiCmd [cmdLength - 3] = (UINT8) ((xferLength >> 8) & 0xff);
- scsiCmd [cmdLength - 2] = (UINT8) ((xferLength ) & 0xff);
- scsiCmd [cmdLength - 1] = controlByte;
- break;
- }
- return (OK);
- }
- /*
- *
- * Controller-independent SCSI support routines
- *
- */
- /*******************************************************************************
- *
- * scsiCacheSynchronize - synchronize the caches for data coherency
- *
- * This routine performs whatever cache action is necessary to ensure cache
- * coherency with respect to the various buffers involved in a SCSI command.
- *
- * The process is as follows:
- *
- * .iP 1. 4
- * The buffers for command, identification, and write data,
- * which are simply written to SCSI, are flushed before the command.
- * .iP 2.
- * The status buffer, which is written and then read, is cleared
- * (flushed and invalidated) before the command.
- * .iP 3.
- * The data buffer for a read command, which is only read, is
- * cleared before the command.
- * .LP
- *
- * The data buffer for a read command is cleared before the command rather
- * than invalidated after it because it may share dirty cache lines with data
- * outside the read buffer. DMA drivers for older versions of the SCSI
- * library have flushed the first and last bytes of the
- * data buffer before the command. However, this approach is not sufficient
- * with the enhanced SCSI library because the amount of data transferred into
- * the buffer may not fill it, which would cause dirty cache lines which
- * contain correct data for the un-filled part of the buffer to be lost
- * when the buffer is invalidated after the command.
- *
- * To optimize the performance of the driver in supporting different caching
- * policies, the routine uses the CACHE_USER_FLUSH macro when flushing the
- * cache. In the absence of a CACHE_USER_CLEAR macro, the following steps are
- * taken:
- * .iP 1. 5
- * If there is a non-NULL flush routine in the `cacheUserFuncs' structure,
- * the cache is cleared.
- * .iP 2.
- * If there is a non-NULL invalidate routine, the cache is invalidated.
- * .iP 3.
- * Otherwise nothing is done; the cache is assumed to be coherent
- * without any software intervention.
- * .LP
- *
- * Finally, since flushing (clearing) cache line entries for a large data
- * buffer can be time-consuming, if the data buffer is larger
- * than a preset (run-time configurable) size, the entire cache is flushed.
- *
- * RETURNS: N/A
- */
- void scsiCacheSynchronize
- (
- SCSI_THREAD * pThread, /* ptr to thread info */
- SCSI_CACHE_ACTION action /* cache action required */
- )
- {
- int direction = pThread->dataDirection;
- UINT8 * dataAddr = pThread->dataAddress;
- UINT dataSize = pThread->dataLength;
- FUNCPTR clearRtn = (cacheUserFuncs.flushRtn != NULL)
- ? cacheClear
- : cacheUserFuncs.invalidateRtn;
- /*
- * If hardware snooping is enabled then don't do anything. By default
- * cacheSnooping is disabled, unless enabled by the BSP
- */
- if (pThread->pScsiCtrl->cacheSnooping == TRUE)
- return;
- /*
- * Invalidating the entire cache is incorrect. However, flushing the
- * entire cache is correct. It is safest to invalidate and flush
- * by cache lines, although there is a performance penalty to be
- * paid.
- */
- #if FALSE
- if (dataSize >= scsiCacheFlushThreshold)
- {
- dataSize = ENTIRE_CACHE;
- dataAddr = 0;
- }
- #endif
- switch (action)
- {
- case SCSI_CACHE_PRE_COMMAND:
- #if (CPU == SPARC)
- /*
- * The "cacheClear ()" function, called by CACHE_USER_FLUSH,
- * occasionally crashes on microSPARC when asked to clear a
- * small area of cache (try "repeat (0, cacheClear, 1, 0, 100)").
- * It appears to work reliably when clearing the entire cache.
- *
- * To avoid this, if there is a non-null flush routine or a non-
- * null invalidation routine, just clear the whole cache.
- * This is a superset of everything normally done.
- *
- * If both flush and invalidation routines are null, there is
- * nothing to do.
- */
- if ((cacheUserFuncs.flushRtn != 0) ||
- (cacheUserFuncs.invalidateRtn != 0))
- {
- cacheClear (DATA_CACHE, 0, ENTIRE_CACHE);
- }
- break;
- #endif
- /*
- * Flush command and identify buffers; clear status buffer.
- */
- CACHE_USER_FLUSH (pThread->identMsg, pThread->identMsgLength);
- CACHE_USER_FLUSH (pThread->cmdAddress, pThread->cmdLength);
- if (clearRtn != 0)
- (* clearRtn) (DATA_CACHE, pThread->statusAddress,
- pThread->statusLength);
- /*
- * Flush (write) or clear (read) data buffer.
- */
- if (direction == O_WRONLY)
- CACHE_USER_FLUSH (dataAddr, dataSize);
- else if (clearRtn != 0)
- {
- #if (CPU == MC68040)
- /*
- * "cacheInvalidate (DATA_CACHE, 0, ENTIRE_CACHE)" crashes
- * on the 68040 (for some reason). Use "cacheClear" instead
- * in this case.
- */
- if (dataSize == ENTIRE_CACHE)
- clearRtn = cacheClear;
- #endif
- (* clearRtn) (DATA_CACHE, dataAddr, dataSize);
- }
- break;
-
- case SCSI_CACHE_POST_COMMAND:
- /*
- * No action is required. For a read, we cleared the cache
- * before the command and therefore don't need to invalidate
- * it here.
- */
- break;
-
- default:
- logMsg ("scsiCacheSynchronize: invalid action (%d).n",
- action, 0, 0, 0, 0, 0);
- break;
- }
- }
- /*******************************************************************************
- *
- * scsiIdentMsgBuild - build an identification message
- *
- * This routine builds an identification message in the caller's buffer,
- * based on the specified physical device, tag type, and tag number.
- *
- * If the target device does not support messages, there is no identification
- * message to build.
- *
- * Otherwise, the identification message consists of an IDENTIFY byte plus an
- * optional QUEUE TAG message (two bytes), depending on the type of tag used.
- *
- * NOTE:
- * This function is not intended for use by application programs.
- *
- * RETURNS: The length of the resulting identification message in bytes or -1
- * for ERROR.
- */
- int scsiIdentMsgBuild
- (
- UINT8 * msg,
- SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_TAG_TYPE tagType,
- UINT tagNumber
- )
- {
- SCSI_TARGET * pScsiTarget = pScsiPhysDev->pScsiTarget;
-
- int msgLen = 0;
- if (!pScsiTarget->messages)
- return (0);
- msg[0] = SCSI_MSG_IDENTIFY | (UINT8) pScsiPhysDev->scsiDevLUN;
-
- if (pScsiTarget->disconnect)
- msg[0] |= SCSI_MSG_IDENT_DISCONNECT;
- switch (tagType)
- {
- case SCSI_TAG_UNTAGGED:
- case SCSI_TAG_SENSE_RECOVERY:
- msgLen = 1;
- break;
- case SCSI_TAG_SIMPLE:
- msg[1] = SCSI_MSG_SIMPLE_Q_TAG;
- msg[2] = tagNumber;
- msgLen = 3;
- break;
- case SCSI_TAG_ORDERED:
- msg[1] = SCSI_MSG_ORDERED_Q_TAG;
- msg[2] = tagNumber;
- msgLen = 3;
- break;
- case SCSI_TAG_HEAD_OF_Q:
- msg[1] = SCSI_MSG_HEAD_OF_Q_TAG;
- msg[2] = tagNumber;
- msgLen = 3;
- break;
- default:
- logMsg ("scsiIdentMsgBuild: invalid tag type (%d)n",
- tagType, 0, 0, 0, 0, 0);
- return (-1);
- }
- return (msgLen);
- }
- /*******************************************************************************
- *
- * scsiIdentMsgParse - parse an identification message
- *
- * This routine scans a (possibly incomplete) identification message,
- * validating it in the process. If there is an IDENTIFY message, it
- * identifies the corresponding physical device.
- *
- * If the physical device is currently processing an untagged (ITL) nexus,
- * identification is complete. Otherwise, the identification is complete only
- * if there is a complete QUEUE TAG message.
- *
- * If there is no physical device corresponding to the IDENTIFY message, or
- * if the device is processing tagged (ITLQ) nexuses and the tag does not
- * correspond to an active thread (it may have been aborted by a timeout, for
- * example), then the identification sequence fails.
- *
- * The caller's buffers for physical device and tag number (the results
- * of the identification process) are always updated. This is required by
- * the thread event handler (see scsiMgrThreadEvent().)
- *
- * NOTE: This function is not intended for use by application programs.
- *
- * RETURNS: The identification status (incomplete, complete, or rejected).
- */
- SCSI_IDENT_STATUS scsiIdentMsgParse
- (
- SCSI_CTRL * pScsiCtrl,
- UINT8 * msg,
- int msgLength,
- SCSI_PHYS_DEV ** ppScsiPhysDev,
- SCSI_TAG * pTagNum
- )
- {
- SCSI_PHYS_DEV * pScsiPhysDev = 0; /* initialize to avoid warning */
- SCSI_IDENT_STATUS status;
- BOOL tagged = (msgLength >= 1 + 2); /* id (1) + tag (2) */
- /*
- * If no IDENTIFY message has been received, always incomplete.
- */
- if (msgLength < 1)
- {
- status = SCSI_IDENT_INCOMPLETE;
- }
- /*
- * First byte should be an IDENTIFY message: check it
- */
- else if ((msg[0] & ~SCSI_MSG_IDENT_LUN_MASK) != SCSI_MSG_IDENTIFY)
- {
- SCSI_ERROR_MSG ("scsiIdentMsgParse: invalid IDENTIFY message (0x%x)n",
- msg[0], 0, 0, 0, 0, 0);
-
- status = SCSI_IDENT_FAILED;
- }
-
- /*
- * Second byte, if present, should be a SIMPLE QUEUE TAG message: check it
- */
- else if (tagged && (msg[1] != SCSI_MSG_SIMPLE_Q_TAG))
- {
- SCSI_ERROR_MSG ("scsiIdentMsgParse: not a queue tag message (0x%x)n",
- msg[1], 0, 0, 0, 0, 0);
- status = SCSI_IDENT_FAILED;
- }
- /*
- * IDENTIFY message has been received: get the physical device.
- */
- else if ((pScsiPhysDev = scsiPhysDevIdGet (pScsiCtrl,
- pScsiCtrl->peerBusId,
- msg[0] & SCSI_MSG_IDENT_LUN_MASK)) == 0)
- {
- status = SCSI_IDENT_FAILED; /* invalid IDENTIFY message */
- }
- /*
- * Physical device has been found: if using tagged commands, ident.
- * is complete iff a tag message has been received.
- */
- else if (pScsiPhysDev->nexus == SCSI_NEXUS_ITLQ)
- {
- status = tagged ? SCSI_IDENT_COMPLETE : SCSI_IDENT_INCOMPLETE;
- }
- /*
- * Device is not using tagged commands: there can only be a single
- * thread, so identification is complete. Error if a tag message
- * has been received.
- */
- else if (!tagged)
- {
- status = SCSI_IDENT_COMPLETE;
- }
- else
- {
- SCSI_ERROR_MSG ("scsiIdentMsgParse: unexpected tag "
- "(phys dev = 0x%08x)n",
- (int) pScsiPhysDev, 0, 0, 0, 0, 0);
- tagged = FALSE;
- status = SCSI_IDENT_FAILED;
- }
- *ppScsiPhysDev = pScsiPhysDev;
- *pTagNum = tagged ? msg[2] : NONE;
- return (status);
- }
- /******************************************************************************
- *
- * scsiMsgOutComplete - perform post-processing after a SCSI message is sent
- *
- * This routine parses the complete message and takes any necessary action.
- *
- * NOTE:
- * This function is intended for use only by SCSI controller drivers.
- *
- * RETURNS: OK, or ERROR if the message is not supported.
- */
- STATUS scsiMsgOutComplete
- (
- SCSI_CTRL *pScsiCtrl, /* ptr to SCSI controller info */
- SCSI_THREAD *pThread /* ptr to thread info */
- )
- {
- int msgType; /* SCSI message type code */
- msgType = pScsiCtrl->msgOutBuf[0];
- switch (msgType)
- {
- case SCSI_MSG_ABORT:
- SCSI_DEBUG_MSG ("ABORT message outn", 0, 0, 0, 0, 0, 0);
- break;
- case SCSI_MSG_ABORT_TAG:
- SCSI_DEBUG_MSG ("ABORT TAG message outn", 0, 0, 0, 0, 0, 0);
- break;
-
- case SCSI_MSG_MESSAGE_REJECT:
- SCSI_DEBUG_MSG ("MESSAGE REJECT message outn", 0, 0, 0, 0, 0, 0);
- break;
- case SCSI_MSG_NO_OP:
- SCSI_DEBUG_MSG ("NO OP message outn", 0, 0, 0, 0, 0, 0);
- break;
- case SCSI_MSG_EXTENDED_MESSAGE:
- msgType = pScsiCtrl->msgOutBuf[SCSI_EXT_MSG_TYPE_BYTE];
- switch (msgType)
- {
- case SCSI_EXT_MSG_SYNC_XFER_REQ:
- SCSI_DEBUG_MSG ("SYNCHRONOUS TRANSFER REQUEST message outn"
- " offset = %d, period = %dn",
- pScsiCtrl->msgOutBuf[SCSI_SYNC_XFER_MSG_OFFSET],
- pScsiCtrl->msgOutBuf[SCSI_SYNC_XFER_MSG_PERIOD],
- 0, 0, 0, 0);
- scsiSyncXferNegotiate (pScsiCtrl,
- pThread->pScsiTarget,
- SYNC_XFER_MSG_OUT);
- break;
- case SCSI_EXT_MSG_WIDE_XFER_REQ:
- SCSI_DEBUG_MSG ("WIDE DATA TRANSFER REQUEST message outn"
- " xferWidth = %d where 0 => 8 bits 1 => 16...n",
- pScsiCtrl->msgOutBuf[SCSI_WIDE_XFER_MSG_WIDTH],
- 0, 0, 0, 0, 0);
- scsiWideXferNegotiate (pScsiCtrl,
- pThread->pScsiTarget,
- WIDE_XFER_MSG_OUT);
- break;
- default:
- SCSI_MSG ("Unsupported extended message (0x%02x) out !n",
- msgType, 0, 0, 0, 0, 0);
- return (ERROR);
- }
- break;
- default:
- if (msgType & SCSI_MSG_IDENTIFY)
- {
- SCSI_DEBUG_MSG ("IDENTIFY message (0x%02x) outn", msgType,
- 0, 0, 0, 0, 0);
- }
- else
- {
- SCSI_MSG ("Unsupported message (0x%02x) out !n", msgType,
- 0, 0, 0, 0, 0);
- return (ERROR);
- }
- break;
- }
- return (OK);
- }
- /******************************************************************************
- *
- * scsiMsgOutReject - perform post-processing when an outgoing message is rejected
- *
- * NOTE:
- * This function is intended for use only by SCSI controller drivers.
- *
- * RETURNS: OK, or ERROR if the message is not supported.
- */
- void scsiMsgOutReject
- (
- SCSI_CTRL *pScsiCtrl, /* ptr to SCSI controller info */
- SCSI_THREAD *pThread /* ptr to thread info */
- )
- {
- int msgType; /* SCSI message type code */
- pScsiCtrl->msgOutState = SCSI_MSG_OUT_NONE;
- /*
- * Handle rejection of the message according to type
- */
- if ((msgType = pScsiCtrl->msgOutBuf[0]) == SCSI_MSG_EXTENDED_MESSAGE)
- msgType = pScsiCtrl->msgOutBuf[SCSI_EXT_MSG_TYPE_BYTE];
- switch (msgType)
- {
- case SCSI_EXT_MSG_SYNC_XFER_REQ:
- SCSI_DEBUG_MSG ("SYNCHRONOUS TRANSFER REQUEST rejectedn",
- 0, 0, 0, 0, 0, 0);
- scsiSyncXferNegotiate (pScsiCtrl,
- pThread->pScsiTarget,
- SYNC_XFER_MSG_REJECTED);
- break;
- case SCSI_EXT_MSG_WIDE_XFER_REQ:
- SCSI_DEBUG_MSG ("WIDE DATA TRANSFER REQUEST rejectedn",
- 0, 0, 0, 0, 0, 0);
- scsiWideXferNegotiate (pScsiCtrl,
- pThread->pScsiTarget,
- WIDE_XFER_MSG_REJECTED);
- break;
- default:
- break;
- }
- }
- /******************************************************************************
- *
- * scsiMsgInComplete - handle a complete SCSI message received from the target
- *
- * This routine parses the complete message and takes any necessary action,
- * which may include setting up an outgoing message in reply. If the message
- * is not understood, the routine rejects it and returns an ERROR status.
- *
- * NOTE:
- * This function is intended for use only by SCSI controller drivers.
- *
- * RETURNS: OK, or ERROR if the message is not supported.
- */
- STATUS scsiMsgInComplete
- (
- SCSI_CTRL *pScsiCtrl, /* ptr to SCSI controller info */
- SCSI_THREAD *pThread /* ptr to thread info */
- )
- {
- int msgType; /* SCSI message type code */
- BOOL msgReject = FALSE; /* will we send MSG REJECT msg ? */
- msgType = pScsiCtrl->msgInBuf[0];
- switch (msgType)
- {
- case SCSI_MSG_COMMAND_COMPLETE:
- SCSI_DEBUG_MSG ("COMMAND COMPLETE message inn", 0, 0, 0, 0, 0, 0);
- pThread->state = SCSI_THREAD_WAIT_COMPLETE;
- break;
-
- case SCSI_MSG_DISCONNECT:
- SCSI_DEBUG_MSG ("DISCONNECT message inn", 0, 0, 0, 0, 0, 0);
- pThread->state = SCSI_THREAD_WAIT_DISCONNECT;
- break;
-
-
- case SCSI_MSG_SAVE_DATA_POINTER:
- SCSI_DEBUG_MSG ("SAVE DATA POINTER message inn", 0, 0, 0, 0, 0, 0);
- pThread->savedDataAddress = pThread->activeDataAddress;
- pThread->savedDataLength = pThread->activeDataLength;
- break;
-
- case SCSI_MSG_RESTORE_POINTERS:
- SCSI_DEBUG_MSG ("RESTORE POINTERS message inn", 0, 0, 0, 0, 0, 0);
- pThread->activeDataAddress = pThread->savedDataAddress;
- pThread->activeDataLength = pThread->savedDataLength;
- break;
-
- case SCSI_MSG_MESSAGE_REJECT:
- SCSI_DEBUG_MSG ("MESSAGE REJECT message inn", 0, 0, 0, 0, 0, 0);
- scsiMsgOutReject (pScsiCtrl, pThread);
- break;
-
- case SCSI_MSG_NO_OP:
- SCSI_DEBUG_MSG ("NO OP message inn", 0, 0, 0, 0, 0, 0);
- break;
-
- case SCSI_MSG_EXTENDED_MESSAGE:
- msgType = pScsiCtrl->msgInBuf[SCSI_EXT_MSG_TYPE_BYTE];
- switch (msgType)
- {
- case SCSI_EXT_MSG_SYNC_XFER_REQ:
- SCSI_DEBUG_MSG ("SYNCHRONOUS TRANSFER REQUEST message inn"
- " offset = %d, period = %dn",
- pScsiCtrl->msgInBuf[SCSI_SYNC_XFER_MSG_OFFSET],
- pScsiCtrl->msgInBuf[SCSI_SYNC_XFER_MSG_PERIOD],
- 0, 0, 0, 0);
-
- scsiSyncXferNegotiate (pScsiCtrl,
- pThread->pScsiTarget,
- SYNC_XFER_MSG_IN);
- break;
- case SCSI_EXT_MSG_WIDE_XFER_REQ:
- SCSI_DEBUG_MSG ("WIDE DATA TRANSFER REQUEST message inn"
- " xferWidth = %d where 0 => 8 bits 1 => 16...n",
- pScsiCtrl->msgInBuf[SCSI_WIDE_XFER_MSG_WIDTH],
- 0, 0, 0, 0, 0);
- scsiWideXferNegotiate (pScsiCtrl,
- pThread->pScsiTarget,
- WIDE_XFER_MSG_IN);
- break;
- default:
- SCSI_ERROR_MSG ("unsupported extended message in"
- "(type = 0x%02x)n",
- msgType, 0, 0, 0, 0, 0);
- msgReject = TRUE;
- break;
- }
-
- break;
- default:
- SCSI_ERROR_MSG ("unsupported message in (type = 0x%02x)n",
- msgType, 0, 0, 0, 0, 0);
-
- msgReject = TRUE;
- break;
- }
-
- /*
- * If the message is to be rejected, set up a MESSAGE REJECT message out.
- */
- if (msgReject)
- {
- pScsiCtrl->msgOutState = SCSI_MSG_OUT_PENDING;
- pScsiCtrl->msgOutBuf[0] = SCSI_MSG_MESSAGE_REJECT;
- pScsiCtrl->msgOutLength = 1;
- }
-
- return (msgReject ? ERROR : OK);
- }
- /*******************************************************************************
- *
- * scsiSyncXferNegotiate - initiate or continue negotiating transfer parameters
- *
- * This routine manages negotiation by means of a finite-state machine which
- * is driven by "significant events" such as incoming and outgoing messages.
- * Each SCSI target has its own independent state machine.
- *
- * NOTE:
- * If the controller does not support synchronous transfer or if the
- * target's maximum REQ/ACK offset is zero, attempts to initiate a round of
- * negotiation are ignored.
- *
- * This function is intended for use only by SCSI controller drivers.
- *
- * RETURNS: N/A
- */
- void scsiSyncXferNegotiate
- (
- SCSI_CTRL *pScsiCtrl, /* ptr to SCSI controller info */
- SCSI_TARGET *pScsiTarget, /* ptr to SCSI target info */
- SCSI_SYNC_XFER_EVENT eventType /* tells what has just happened */
- )
- {
- SCSI_SYNC_XFER_STATE state = pScsiTarget->syncXferState;
- UINT8 offset;
- UINT8 period;
- BOOL setValues = FALSE;
- BOOL sendMsg = FALSE;
- switch (eventType)
- {
- case SYNC_XFER_RESET:
- state = SYNC_XFER_NOT_NEGOTIATED;
- break;
- case SYNC_XFER_NEW_THREAD:
- offset = pScsiTarget->maxOffset;
- period = pScsiTarget->minPeriod;
- if (pScsiCtrl->syncXfer &&
- ((offset != pScsiTarget->xferOffset) ||
- (period != pScsiTarget->xferPeriod)) &&
- (state != SYNC_XFER_NEGOTIATION_COMPLETE))
- {
- /* initiate a round of negotiation */
-
- sendMsg = TRUE;
- state = SYNC_XFER_REQUEST_PENDING;
- }
- else
- state = SYNC_XFER_NEGOTIATION_COMPLETE;
- break;
- case SYNC_XFER_MSG_IN:
- offset = pScsiCtrl->msgInBuf[SCSI_SYNC_XFER_MSG_OFFSET];
- period = pScsiCtrl->msgInBuf[SCSI_SYNC_XFER_MSG_PERIOD];
- if (state == SYNC_XFER_REQUEST_SENT)
- {
- /* target is replying to our initial request */
-
- setValues = TRUE;
- state = SYNC_XFER_NEGOTIATION_COMPLETE;
- }
- else
- {
- /* target is making unsolicited request */
-
- sendMsg = TRUE;
- state = SYNC_XFER_REPLY_PENDING;
- }
- break;
- case SYNC_XFER_MSG_OUT:
- offset = pScsiCtrl->msgOutBuf[SCSI_SYNC_XFER_MSG_OFFSET];
- period = pScsiCtrl->msgOutBuf[SCSI_SYNC_XFER_MSG_PERIOD];
- if (state == SYNC_XFER_REQUEST_PENDING)
- {
- /* we have sent initial request (need target's reply) */
-
- state = SYNC_XFER_REQUEST_SENT;
- }
- else if (state == SYNC_XFER_REPLY_PENDING)
- {
- /* we have replied to target's unsolicited request */
-
- setValues = TRUE;
- state = SYNC_XFER_NEGOTIATION_COMPLETE;
- }
- else
- SCSI_ERROR_MSG ("scsiSyncXferNegotiate: unexpected msg outn",
- 0, 0, 0, 0, 0, 0);
- break;
- case SYNC_XFER_MSG_REJECTED:
- offset = SCSI_SYNC_XFER_ASYNC_OFFSET;
- period = SCSI_SYNC_XFER_ASYNC_PERIOD;
-
- if (state == SYNC_XFER_REQUEST_SENT)
- {
- /* target has rejected our initial request */
-
- setValues = TRUE;
- state = SYNC_XFER_NEGOTIATION_COMPLETE;
- }
- break;
- default:
- SCSI_MSG ("scsiSyncXferNegotiate: invalid event type (%d)n",
- eventType, 0, 0, 0, 0, 0);
- break;
- }
- if (sendMsg)
- {
- UINT8 *msg = pScsiCtrl->msgOutBuf;
-
- if (pScsiCtrl->scsiSpecialHandler != NULL)
- {
- if ((pScsiTarget->syncSupport) && (pScsiTarget->syncXferState !=
- SYNC_XFER_NEGOTIATION_COMPLETE))
- {
- (*pScsiCtrl->scsiXferParamsQuery) (pScsiCtrl, &offset,
- &period);
- if ((*pScsiCtrl->scsiXferParamsSet) (pScsiCtrl, offset, period)
- != OK)
- SCSI_ERROR_MSG ("syncXferNego: can't set xfer params.n",
- 0, 0, 0, 0, 0, 0);
- state = SYNC_XFER_NEGOTIATION_COMPLETE;
- }
- }
- else
- {
- (*pScsiCtrl->scsiXferParamsQuery) (pScsiCtrl, &offset, &period);
-
- msg[0] = SCSI_MSG_EXTENDED_MESSAGE;
- msg[SCSI_EXT_MSG_LENGTH_BYTE] = SCSI_SYNC_XFER_REQ_MSG_LENGTH;
- msg[SCSI_EXT_MSG_TYPE_BYTE] = SCSI_EXT_MSG_SYNC_XFER_REQ;
- msg[SCSI_SYNC_XFER_MSG_PERIOD] = period;
- msg[SCSI_SYNC_XFER_MSG_OFFSET] = offset;
-
- pScsiCtrl->msgOutState = SCSI_MSG_OUT_PENDING;
- pScsiCtrl->msgOutLength = SCSI_EXT_MSG_HDR_LENGTH +
- SCSI_SYNC_XFER_REQ_MSG_LENGTH;
- }
- }
- if (setValues)
- {
- pScsiTarget->xferOffset = offset;
- pScsiTarget->xferPeriod = period;
- if ((*pScsiCtrl->scsiXferParamsSet) (pScsiCtrl, offset, period) != OK)
- {
- SCSI_ERROR_MSG ("scsiSyncXferNegotiate: can't set xfer params.n",
- 0, 0, 0, 0, 0, 0);
- }
- }
- pScsiTarget->syncXferState = state;
- }
- /*******************************************************************************
- *
- * scsiWideXferNegotiate - initiate or continue negotiating wide parameters
- *
- * This routine manages negotiation means of a finite-state machine which is
- * driven by "significant events" such as incoming and outgoing messages.
- * Each SCSI target has its own independent state machine.
- *
- * NOTE:
- * If the controller does not support wide transfers or the
- * target's transfer width is zero, attempts to initiate a round of
- * negotiation are ignored; this is because zero is the default narrow transfer.
- *
- * This function is intended for use only by SCSI controller drivers.
- *
- * RETURNS: N/A
- */
- void scsiWideXferNegotiate
- (
- SCSI_CTRL *pScsiCtrl, /* ptr to SCSI controller info */
- SCSI_TARGET *pScsiTarget, /* ptr to SCSI target info */
- SCSI_WIDE_XFER_EVENT eventType /* tells what has just happened */
- )
- {
- SCSI_WIDE_XFER_STATE state = pScsiTarget->wideXferState;
- UINT8 xferWidth;
- BOOL setValues = FALSE;
- BOOL sendMsg = FALSE;
- switch (eventType)
- {
- case WIDE_XFER_RESET:
- state = WIDE_XFER_NOT_NEGOTIATED;
- break;
- case WIDE_XFER_NEW_THREAD:
- xferWidth = pScsiTarget->xferWidth;
- if (pScsiCtrl->wideXfer &&
- (xferWidth != SCSI_WIDE_XFER_SIZE_NARROW) &&
- (state != WIDE_XFER_NEGOTIATION_COMPLETE))
- {
- /* initiate a round of negotiation */
-
- sendMsg = TRUE;
- state = WIDE_XFER_REQUEST_PENDING;
- /*
- * Set the synchronous state to "complete". This state
- * should be reset when the wide negotiation is over. This
- * is because a wide negotiation must occur before a
- * sync xfer negotiation.
- */
- if (pScsiCtrl->scsiSpecialHandler == NULL)
- pScsiTarget->syncXferState = SYNC_XFER_NEGOTIATION_COMPLETE;
- }
- else
- {
- if ((pScsiCtrl->scsiSpecialHandler != NULL) &&
- (state != WIDE_XFER_NEGOTIATION_COMPLETE))
- sendMsg = TRUE;
- else
- state = WIDE_XFER_NEGOTIATION_COMPLETE;
- }
- break;
- case WIDE_XFER_MSG_IN:
- xferWidth = pScsiCtrl->msgInBuf[SCSI_WIDE_XFER_MSG_WIDTH];
- if (state == WIDE_XFER_REQUEST_SENT)
- {
- /* target is replying to our initial request */
-
- setValues = TRUE;
- state = WIDE_XFER_NEGOTIATION_COMPLETE;
- /*
- * After a wide negotiation is complete, a synchronous
- * negotiation must be activated when the next new
- * thread begins.
- */
- pScsiTarget->syncXferState = SYNC_XFER_NOT_NEGOTIATED;
- }
- else
- {
- /* target is making unsolicited request */
-
- sendMsg = TRUE;
- state = WIDE_XFER_REPLY_PENDING;
- }
- break;
- case WIDE_XFER_MSG_OUT:
- xferWidth = pScsiCtrl->msgOutBuf[SCSI_WIDE_XFER_MSG_WIDTH];
- if (state == WIDE_XFER_REQUEST_PENDING)
- {
- /* we have sent initial request (need target's reply) */
-
- state = WIDE_XFER_REQUEST_SENT;
- }
- else if (state == WIDE_XFER_REPLY_PENDING)
- {
- /* we have replied to target's unsolicited request */
-
- setValues = TRUE;
- state = WIDE_XFER_NEGOTIATION_COMPLETE;
- /* Allow a synchronous negotiation to occur */
- pScsiTarget->syncXferState = SYNC_XFER_NOT_NEGOTIATED;
- }
- else
- SCSI_ERROR_MSG ("scsiWideXferNegotiate: unexpected msg outn",
- 0, 0, 0, 0, 0, 0);
- break;
- case WIDE_XFER_MSG_REJECTED:
- xferWidth = SCSI_WIDE_XFER_SIZE_NARROW;
-
- if (state == WIDE_XFER_REQUEST_SENT)
- {
- /* target has rejected our initial request */
-
- setValues = TRUE;
- state = WIDE_XFER_NEGOTIATION_COMPLETE;
- /* Allow a synchronous negotiation to occur */
- pScsiTarget->syncXferState = SYNC_XFER_NOT_NEGOTIATED;
- }
- break;
- default:
- SCSI_MSG ("scsiWideXferNegotiate: invalid event type (%d)n",
- eventType, 0, 0, 0, 0, 0);
- break;
- }
- if (sendMsg)
- {
- UINT8 *msg = pScsiCtrl->msgOutBuf;
-
- /*
- * Make sure that we are sending the correct params according to the
- * controller driver.
- */
- if (pScsiCtrl->scsiSpecialHandler != NULL)
- {
- if ((pScsiTarget->wideSupport) && (pScsiTarget->wideXferState !=
- WIDE_XFER_NEGOTIATION_COMPLETE))
- {
- if ((*pScsiCtrl->scsiWideXferParamsSet) (pScsiCtrl, xferWidth)
- != OK)
- SCSI_ERROR_MSG ("syncXferNego: can't set xfer params.n",
- 0, 0, 0, 0, 0, 0);
- state = WIDE_XFER_NEGOTIATION_COMPLETE;
- pScsiTarget->syncXferState = SYNC_XFER_NOT_NEGOTIATED;
- }
- }
- else
- {
- (*pScsiCtrl->scsiWideXferParamsQuery) (pScsiCtrl, &xferWidth);
- msg[0] = SCSI_MSG_EXTENDED_MESSAGE;
- msg[SCSI_EXT_MSG_LENGTH_BYTE] = SCSI_WIDE_XFER_REQ_MSG_LENGTH;
- msg[SCSI_EXT_MSG_TYPE_BYTE] = SCSI_EXT_MSG_WIDE_XFER_REQ;
- msg[SCSI_WIDE_XFER_MSG_WIDTH] = xferWidth;
-
- pScsiCtrl->msgOutState = SCSI_MSG_OUT_PENDING;
- pScsiCtrl->msgOutLength = SCSI_EXT_MSG_HDR_LENGTH +
- SCSI_WIDE_XFER_REQ_MSG_LENGTH;
- }
- }
-
- if (setValues)
- {
- pScsiTarget->xferWidth = xferWidth;
- if ((*pScsiCtrl->scsiWideXferParamsSet) (pScsiCtrl, xferWidth) != OK)
- {
- SCSI_ERROR_MSG ("scsiWideXferNegotiate: can't set xfer params.n",
- 0, 0, 0, 0, 0, 0);
- }
- }
- pScsiTarget->wideXferState = state;
- }
- /*******************************************************************************
- *
- * scsiTimeoutCvt - convert timeout in microseconds to system clock ticks
- *
- * Clip the specified timeout to be within the allowed range, then convert
- * to system clock ticks.
- *
- * NOTE: this is non-trivial simply because it must avoid problems with
- * overflow or underflow during the conversion. A quick-and-dirty approach
- * would use floating point maths, but that needlessly drags in another
- * (possibly large) library.
- *
- * RETURNS: timeout in system clock ticks
- */
- LOCAL UINT scsiTimeoutCvt
- (
- UINT uSecs /* timeout in microseconds */
- )
- {
- UINT ticksPerSec = sysClkRateGet ();
- UINT secs;
- UINT Mticks;
-
- /*
- * Clip timeout (if necessary) to be within current allowed range
- */
- if (uSecs > scsiMaxTimeout)
- uSecs = scsiMaxTimeout;
- if (uSecs < scsiMinTimeout)
- uSecs = scsiMinTimeout;
- /*
- * Convert timeout from usec to system clock ticks
- */
- if (uSecs < 0xffffffff / ticksPerSec)
- {
- Mticks = uSecs * ticksPerSec;
-
- return (Mticks / 1000000);
- }
- else
- {
- secs = uSecs / 1000000;
-
- return (secs * ticksPerSec);
- }
- }
- /*******************************************************************************
- *
- * scsiPriorityCvt - convert priority for a SCSI transaction
- *
- * Converts a priority as specified by the application to one used by the
- * SCSI manager task. If the application specifies a default priority, the
- * current task priority is used. Otherwise, the specified value is used.
- *
- * NOTE:
- * This routine should trap out-of-range priorities and return an error.
- *
- * RETURNS: SCSI transaction priority
- */
- LOCAL SCSI_PRIORITY scsiPriorityCvt
- (
- UINT priority
- )
- {
- if (priority == SCSI_THREAD_TASK_PRIORITY)
- {
- int taskPriority;
-
- taskPriorityGet (0, &taskPriority);
- priority = taskPriority;
- }
- return (priority);
- }
- /*******************************************************************************
- *
- * scsiThreadCreate - create and initialise a new SCSI thread context
- *
- * Use the thread allocator to allocate a thread structure. Initialise the
- * thread context from the transaction it will execute.
- *
- * RETURNS: thread ptr, or 0 if an error occurs
- */
- LOCAL SCSI_THREAD * scsiThreadCreate
- (
- SCSI_PHYS_DEV * pScsiPhysDev, /* physical device used by thread */
- SCSI_TRANSACTION * pScsiXaction /* transaction thread will execute */
- )
- {
- SCSI_THREAD * pThread;
- SCSI_CTRL * pScsiCtrl = pScsiPhysDev->pScsiCtrl;
-
- /*
- * Allocate a thread structure
- */
- if ((pThread = scsiThreadAllocate (pScsiCtrl)) == 0)
- {
- SCSI_DEBUG_MSG ("scsiThreadCreate: can't allocate thread.n",
- 0, 0, 0, 0, 0, 0);
- return (0);
- }
- /*
- * Initialise thread structure for this transaction
- */
- pThread->pScsiCtrl = pScsiCtrl;
- pThread->pScsiPhysDev = pScsiPhysDev;
- pThread->pScsiTarget = pScsiPhysDev->pScsiTarget;
-
- pThread->role = SCSI_ROLE_INITIATOR;
- pThread->state = SCSI_THREAD_INACTIVE;
- if (pScsiXaction->cmdTimeout == WAIT_FOREVER)
- pThread->timeout = WAIT_FOREVER;
- else
- pThread->timeout = scsiTimeoutCvt (pScsiXaction->cmdTimeout);
- pThread->priority = scsiPriorityCvt (pScsiXaction->priority);
- pThread->dataDirection = pScsiXaction->dataDirection;
- pThread->tagType = pScsiXaction->tagType;
- pThread->cmdAddress = pScsiXaction->cmdAddress;
- pThread->cmdLength = pScsiXaction->cmdLength;
- pThread->dataAddress = pScsiXaction->dataAddress;
- pThread->dataLength = pScsiXaction->dataLength;
- pThread->statusAddress = &pScsiXaction->statusByte;
- pThread->statusLength = 1;
- return (pThread);
- }
-
- /*******************************************************************************
- *
- * scsiThreadDelete - delete a SCSI thread context
- *
- * Deallocate the thread structure.
- *
- * NOTE:
- * This function is provided mainly for symmetry with "scsiThreadCreate()",
- * and to provide a place-holder for future extensions.
- *
- * RETURNS: N/A
- */
- LOCAL void scsiThreadDelete
- (
- SCSI_THREAD * pThread /* thread to be destroyed */
- )
- {
- /*
- * Deallocate the thread structure
- */
- scsiThreadDeallocate (pThread->pScsiCtrl, pThread);
- }
-
- /*******************************************************************************
- *
- * scsiThreadAllocate - allocate and initialise a SCSI thread structure
- *
- * To avoid dynamic creation and deletion of threads (and the objects they
- * contain) for every SCSI transaction, a pool of available thread structures
- * is automatically built up and maintained for each SCSI controller.
- *
- * Thus, the allocator first checks the pool (list) of free threads and takes
- * a thread from this pool if possible. Otherwise (there are no free threads)
- * it allocates storage for and initialises a number of thread structures,
- * reserves one to be returned to the caller, and adds the rest to the free
- * list.
- *
- * Two globals control the allocation policy:
- *
- * scsiAllocNumThreads - how many threads to allocate in one go, and
- *
- * scsiMaxNumThreads - the maximum number of threads to allow per ctrlr
- *
- * NOTE:
- * If a new thread cannot be allocated, this routine returns an error rather
- * than waiting for a thread to become free.
- *
- * Access to the free thread list must be serialised because this routine is
- * called in the context of the SCSI client task(s).
- *
- * RETURNS: thread ptr, or 0 if none can be allocated
- */
- LOCAL SCSI_THREAD * scsiThreadAllocate
- (
- SCSI_CTRL * pScsiCtrl
- )
- {
- SCSI_THREAD * pThread;
- semTake (pScsiCtrl->mutexSem, WAIT_FOREVER);
-
- if ((pThread = (SCSI_THREAD *) lstGet (&pScsiCtrl->freeThreads)) == 0)
- {
- /*
- * No free threads: create more if possible.
- */
- if (pScsiCtrl->nThreads >= scsiMaxNumThreads)
- {
- SCSI_DEBUG_MSG ("scsiThreadAllocate: max threads (%d) reachedn",
- scsiMaxNumThreads, 0, 0, 0, 0, 0);
- errnoSet (S_scsiLib_NO_MORE_THREADS);
- }
- else
- {
- char * pArray;
- int i;
- int nThreads = min (scsiAllocNumThreads,
- scsiMaxNumThreads - pScsiCtrl->nThreads);
- if ((pArray = scsiThreadArrayCreate (pScsiCtrl, nThreads)) == 0)
- {
- SCSI_DEBUG_MSG ("scsiThreadAllocate: can't create arrayn",
- 0, 0, 0, 0, 0, 0);
- semGive (pScsiCtrl->mutexSem);
- return (NULL);
- }
- pScsiCtrl->nThreads += nThreads;
- /*
- * Grab the first one; put the rest onto the free queue
- */
- pThread = (SCSI_THREAD *) pArray;
- for (i = 1; i < nThreads; ++i)
- lstAdd (&pScsiCtrl->freeThreads,
- (NODE *) (pArray + i * pScsiCtrl->threadSize));
- }
- }
- semGive (pScsiCtrl->mutexSem);
-
- return (pThread);
- }
- /*******************************************************************************
- *
- * scsiThreadDeallocate - deallocate a thread structure
- *
- * Deallocation consists of returning the thread structure to the free thread
- * list: once allocated, threads are never "really freed".
- *
- * NOTE:
- * Access to the free thread list must be serialised because this routine is
- * called in the context of the SCSI client task(s).
- *
- * RETURNS: N/A
- */
- LOCAL void scsiThreadDeallocate
- (
- SCSI_CTRL * pScsiCtrl,
- SCSI_THREAD * pThread
- )
- {
- semTake (pScsiCtrl->mutexSem, WAIT_FOREVER);
-
- lstAdd (&pScsiCtrl->freeThreads, (NODE *) pThread);
- semGive (pScsiCtrl->mutexSem);
- }
- /*******************************************************************************
- *
- * scsiThreadArrayCreate - allocate and initialise storage for N threads
- *
- * Allocate storage for an array of `nThreads' thread structures, each of the
- * controller-specific size. Step through the array calling the controller-
- * specific initialisation routine for each thread structure.
- *
- * RETURNS: ptr to array of thread structures, or 0 if an error occurs
- */
- LOCAL char * scsiThreadArrayCreate
- (
- SCSI_CTRL * pScsiCtrl,
- int nThreads
- )
- {
- char * pArray;
- int i;
- /*
- * Allocate memory for an array of thread structures
- */
- if ((pArray = malloc (nThreads * pScsiCtrl->threadSize)) == 0)
- {
- SCSI_DEBUG_MSG ("scsiThreadArrayCreate: can't allocate threads.n",
- 0, 0, 0, 0, 0, 0);
- return (0);
- }
- /*
- * Initialise (controller-specific) each thread structure in array
- */
- for (i = 0; i < nThreads; ++i)
- {
- SCSI_THREAD * pThread = (SCSI_THREAD *) (pArray +
- i * pScsiCtrl->threadSize);
-
- if ((*pScsiCtrl->scsiThreadInit) (pScsiCtrl, pThread) != OK)
- {
- SCSI_DEBUG_MSG ("scsiThreadArrayCreate: can't initialise threadn",
- 0, 0, 0, 0, 0, 0);
- free (pArray);
- return (0);
- }
- }
- return (pArray);
- }
- /*******************************************************************************
- *
- * scsiThreadInit - perform generic SCSI thread initialization
- *
- * This routine initializes the controller-independent parts of a thread
- * structure, which are specific to the SCSI manager.
- *
- * NOTE:
- * This function should not be called by application programs. It is intended
- * to be used by SCSI controller drivers.
- *
- * RETURNS: OK, or ERROR if the thread cannot be initialized.
- */
- STATUS scsiThreadInit
- (
- SCSI_THREAD * pThread
- )
- {
- if ((pThread->replyQ = msgQCreate (1, sizeof (SCSI_REPLY),
- scsiThreadReplyQOptions)) == 0)
- {
- SCSI_DEBUG_MSG ("scsiThreadInit: can't create thread's reply queue.n",
- 0, 0, 0, 0, 0, 0);
- return (ERROR);
- }
- if ((pThread->wdog = wdCreate ()) == 0)
- {
- SCSI_DEBUG_MSG ("scsiThreadInit: can't create thread's watchdog.n",
- 0, 0, 0, 0, 0, 0);
- return (ERROR);
- }
-
- return (OK);
- }
- /*******************************************************************************
- *
- * scsiThreadExecute - execute the specified thread
- *
- * Build an activation request and execute it via the SCSI manager. Parse the
- * return status in the SCSI manager's reply message.
- *
- * RETURNS: OK, or ERROR if the thread could not be executed, or failed
- */
- LOCAL STATUS scsiThreadExecute
- (
- SCSI_THREAD * pThread /* thread to be executed */
- )
- {
- SCSI_CTRL * pScsiCtrl; /* ptr to controller info */
- SCSI_REQUEST request; /* thread activation request msg */
- SCSI_REPLY reply; /* thread completion reply msg */
- pScsiCtrl = pThread->pScsiCtrl;
- /*
- * Build thread activation request
- */
- request.type = SCSI_REQUEST_ACTIVATE;
- request.thread = pThread;
- /*
- * Execute thread activation request
- */
- if (scsiMgrRequestExecute (pScsiCtrl, &request, &reply) != OK)
- {
- SCSI_DEBUG_MSG ("scsiThreadExecute: error executing request.n",
- 0, 0, 0, 0, 0, 0);
- return (ERROR);
- }
- /*
- * Check reply type, set errno if necessary
- */
- switch (reply.type)
- {
- case SCSI_REPLY_COMPLETE:
- break;
- default:
- logMsg ("scsiThreadExecute: invalid reply type (%d)n",
- reply.type, 0, 0, 0, 0, 0);
- return (ERROR);
- }
- if (reply.status != OK)
- errnoSet (reply.errNum);
- return (reply.status);
- }
- /*******************************************************************************
- *
- * scsiCommand - execute a single SCSI command
- *
- * This routine executes a single SCSI command. It retries while the device
- * is unable to execute the command (busy or queue full status), and returns
- * when the target has completed (or failed) the command, at which time the
- * status byte is available in the transaction structure.
- *
- * RETURNS: OK, or ERROR if not successful for any reason.
- */
- LOCAL STATUS scsiCommand
- (
- SCSI_PHYS_DEV * pScsiPhysDev, /* ptr to physical device info */
- SCSI_TRANSACTION * pScsiXaction /* ptr to transaction info */
- )
- {
- SCSI_THREAD * pThread;
- SCSI_CTRL * pScsiCtrl; /* ptr to SCSI controller info */
- STATUS status; /* return status */
- BOOL retry; /* TRUE if command must be retried */
- pScsiCtrl = pScsiPhysDev->pScsiCtrl;
- /*
- * Validate target device's bus ID
- */
- if (pScsiPhysDev->pScsiTarget->scsiDevBusId == pScsiCtrl->scsiCtrlBusId)
- {
- errnoSet (S_scsiLib_ILLEGAL_BUS_ID);
- return (ERROR);
- }
- if ((pThread = scsiThreadCreate (pScsiPhysDev, pScsiXaction)) == 0)
- {
- SCSI_DEBUG_MSG ("scsiCommand: can't create thread.n",
- 0, 0, 0, 0, 0, 0);
- return (ERROR);
- }
- do
- {
- retry = FALSE;
-
- if ((status = scsiThreadExecute (pThread)) != OK)
- {
- SCSI_DEBUG_MSG ("scsiCommand: thread execution failedn",
- 0, 0, 0, 0, 0, 0);
- }
- else
- {
- switch (pScsiXaction->statusByte)
- {
- case SCSI_STATUS_BUSY:
- SCSI_DEBUG_MSG ("scsiCommand: device busy - retryingn",
- 0, 0, 0, 0, 0, 0);
- retry = TRUE;
- break;
- case SCSI_STATUS_QUEUE_FULL:
- SCSI_DEBUG_MSG ("scsiCommand: queue full - retryingn",
- 0, 0, 0, 0, 0, 0);
- retry = TRUE;
- break;
- default:
- break;
- }
- }
- }
- while (retry);
- (void) scsiThreadDelete (pThread);
- return (status);
- }
- /*******************************************************************************
- *
- * scsi2Transact - execute a SCSI transaction
- *
- * This routine calls scsiCommand() to execute the command specified.
- * If there are physical path management errors, then this routine returns
- * ERROR. If not, then the status returned from the command is checked. If
- * it is "Check Condition", then a "Request Sense" CCS command is executed
- * and the sense key is examined. An indication of the success of the
- * command is returned (OK or ERROR).
- *
- * RETURNS: OK, or ERROR if a path management error occurs
- * or the status or sense information indicates an error.
- */
- LOCAL STATUS scsi2Transact
- (
- SCSI_PHYS_DEV *pScsiPhysDev, /* ptr to the target device */
- SCSI_TRANSACTION *pScsiXaction /* ptr to the transaction info */
- )
- {
- STATUS status; /* routine return status */
- int senseKey; /* extended sense key from target */
- int addSenseCode; /* additional sense code from target */
- int addSenseCodeQual; /* additional sense code qualifier */
- SCSI_DEBUG_MSG ("scsi2Transact:n",0, 0, 0, 0, 0, 0);
- if ((status = scsiCommand (pScsiPhysDev, pScsiXaction)) == ERROR)
- {
- SCSI_DEBUG_MSG ("scsiTransact: scsiCommand ERROR.n",
- 0, 0, 0, 0, 0, 0);
- goto cleanExit;
- }
- /* check device status and take appropriate action */
- switch (pScsiXaction->statusByte & SCSI_STATUS_MASK)
- {
- case SCSI_STATUS_GOOD:
- status = OK;
- pScsiPhysDev->lastSenseKey = SCSI_SENSE_KEY_NO_SENSE;
- pScsiPhysDev->lastAddSenseCode = (UINT8) 0;
- goto cleanExit;
- case SCSI_STATUS_CHECK_CONDITION:
- {
- SCSI_COMMAND reqSenseCmd; /* REQUEST SENSE command */
- SCSI_TRANSACTION reqSenseXaction; /* REQUEST SENSE xaction */
- /* REQUEST SENSE buffer */
- UINT8 reqSenseData [REQ_SENSE_ADD_LENGTH_BYTE + 1];
- /* build a REQUEST SENSE command and transact it */
- (void) scsiCmdBuild (reqSenseCmd, &reqSenseXaction.cmdLength,
- SCSI_OPCODE_REQUEST_SENSE,
- pScsiPhysDev->scsiDevLUN, FALSE, 0,
- pScsiPhysDev->reqSenseDataLength, (UINT8) 0);
- reqSenseXaction.cmdAddress = (UINT8 *) reqSenseCmd;
- /* if there is no user request sense buffer, supply it */
- if ( pScsiPhysDev->pReqSenseData == (UINT8 *)NULL )
- {
- reqSenseXaction.dataAddress = reqSenseData;
- if (!pScsiPhysDev->extendedSense)
- reqSenseXaction.dataLength = NON_EXT_SENSE_DATA_LENGTH;
- else
- reqSenseXaction.dataLength = REQ_SENSE_ADD_LENGTH_BYTE + 1;
- }
- else
- {
- reqSenseXaction.dataAddress = pScsiPhysDev->pReqSenseData;
- reqSenseXaction.dataLength =
- pScsiPhysDev->reqSenseDataLength;
- }
- reqSenseXaction.dataDirection = O_RDONLY;
- reqSenseXaction.addLengthByte = REQ_SENSE_ADD_LENGTH_BYTE;
- reqSenseXaction.cmdTimeout = SCSI_TIMEOUT_5SEC;
- reqSenseXaction.tagType = SCSI_TAG_SENSE_RECOVERY;
- reqSenseXaction.priority = SCSI_THREAD_MAX_PRIORITY;
- SCSI_DEBUG_MSG ("scsiTransact: issuing a REQUEST SENSE command.n",
- 0, 0, 0, 0, 0, 0);
- if ((status = scsiCommand (pScsiPhysDev, &reqSenseXaction))
- == ERROR)
- {
- SCSI_DEBUG_MSG ("scsiTransact: scsiCommand ERROR.n",
- 0, 0, 0, 0, 0, 0);
- goto cleanExit;
- }
- /* REQUEST SENSE command status != GOOD indicates fatal error */
- if (reqSenseXaction.statusByte != SCSI_STATUS_GOOD)
- {
- SCSI_DEBUG_MSG ("scsiTransact: non-zero REQ SENSE status.n",
- 0, 0, 0, 0, 0, 0);
- errnoSet (S_scsiLib_REQ_SENSE_ERROR);
- status = ERROR;
- goto cleanExit;
- }
- /* if device uses Nonextended Sense Data Format, return now */
- if (!pScsiPhysDev->extendedSense)
- {
- status = ERROR;
- goto cleanExit;
- }
- /* check sense key and take appropriate action */
- pScsiPhysDev->lastSenseKey =
- (pScsiPhysDev->pReqSenseData)[2] & SCSI_SENSE_KEY_MASK;
- pScsiPhysDev->lastAddSenseCode = (pScsiPhysDev->pReqSenseData)[12];
- addSenseCode = (int) pScsiPhysDev->lastAddSenseCode;
- /* store additional sense code qualifier */
- addSenseCodeQual = (pScsiPhysDev->pReqSenseData)[13];
- switch (senseKey = (int) pScsiPhysDev->lastSenseKey)
- {
- case SCSI_SENSE_KEY_NO_SENSE:
- {
- SCSI_DEBUG_MSG ("scsiTransact: No Sensen",
- 0, 0, 0, 0, 0, 0);
- status = OK;
- goto cleanExit;
- }
- case SCSI_SENSE_KEY_RECOVERED_ERROR:
- {
- SCSI_DEBUG_MSG ("scsiTransact: Recovered Error Sense,",
- 0, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("Additional Sense Code = 0x%02xn",
- addSenseCode, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("ASCQ = 0x%02xn",
- addSenseCodeQual, 0, 0, 0, 0, 0);
- status = OK;
- goto cleanExit;
- }
- case SCSI_SENSE_KEY_NOT_READY:
- {
- SCSI_DEBUG_MSG ("scsiTransact: Not Ready Sense,",
- 0, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("Additional Sense Code = 0x%02xn",
- addSenseCode, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("ASCQ = 0x%02xn",
- addSenseCodeQual, 0, 0, 0, 0, 0);
- errnoSet (S_scsiLib_DEV_NOT_READY);
- status = ERROR;
- goto cleanExit;
- }
- case SCSI_SENSE_KEY_MEDIUM_ERROR:
- {
- SCSI_DEBUG_MSG ("scsiTransact: Not Ready Sense,",
- 0, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("Additional Sense Code = 0x%02xn",
- addSenseCode, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("ASCQ = 0x%02xn",
- addSenseCodeQual, 0, 0, 0, 0, 0);
- errnoSet (S_scsiLib_MEDIUM_ERROR);
- status = ERROR;
- goto cleanExit;
- }
- case SCSI_SENSE_KEY_HARDWARE_ERROR:
- {
- SCSI_DEBUG_MSG ("scsiTransact: Hardware Error Sense,",
- 0, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("Additional Sense Code = 0x%02xn",
- addSenseCode, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("ASCQ = 0x%02xn",
- addSenseCodeQual, 0, 0, 0, 0, 0);
- errnoSet (S_scsiLib_HARDWARE_ERROR);
- status = ERROR;
- goto cleanExit;
- }
- case SCSI_SENSE_KEY_ILLEGAL_REQUEST:
- {
- SCSI_DEBUG_MSG ("scsiTransact: Illegal Request Sense,",
- 0, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("Additional Sense Code = 0x%02xn",
- addSenseCode, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("ASCQ = 0x%02xn",
- addSenseCodeQual, 0, 0, 0, 0, 0);
- errnoSet (S_scsiLib_ILLEGAL_REQUEST);
- status = ERROR;
- goto cleanExit;
- }
- /*
- * A UNIT ATTENTION occurs because of the following main
- * conditions:
- * 1. Device reset (SCSI command or hard reset)
- * 2. Removable medium has been changed
- * 3. Another initiator changed mode params or cleared
- * tagged commnads
- * 4. Others
- * A medium change has to be reported the BLK_DEV or SEQ_DEV
- * structure.
- */
- case SCSI_SENSE_KEY_UNIT_ATTENTION:
- {
- SCSI_DEBUG_MSG ("scsiTransact: Unit Attention Sense,",
- 0, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("Additional Sense Code = 0x%02xn",
- addSenseCode, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("ASCQ = 0x%02xn",
- addSenseCodeQual, 0, 0, 0, 0, 0);
- /* check for medium change and WP bit */
- chkMedChangeAndWP (addSenseCode, pScsiPhysDev);
- errnoSet (S_scsiLib_UNIT_ATTENTION);
- status = ERROR;
- /* retry the command sequence */
-
- if (addSenseCode == SCSI_ADD_SENSE_DEVICE_RESET)
- status = scsiCommand (pScsiPhysDev, pScsiXaction);
-
- goto cleanExit;
- }
- /*
- * Indicates that a command that reads or writes the medium
- * was attempted on a block that is protected from this
- * operation. The read/write operation is not performed.
- * In simple terms, we get this sense for write protected
- * media
- */
- case SCSI_SENSE_KEY_DATA_PROTECT:
- {
- SCSI_DEBUG_MSG ("scsiTransact: Data Protect Sense,",
- 0, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("Additional Sense Code = 0x%02xn",
- addSenseCode, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("ASCQ = 0x%02xn",
- addSenseCodeQual, 0, 0, 0, 0, 0);
- if (addSenseCode == SCSI_ADD_SENSE_WRITE_PROTECTED)
- errnoSet (S_scsiLib_WRITE_PROTECTED);
- status = ERROR;
- goto cleanExit;
- }
- case SCSI_SENSE_KEY_BLANK_CHECK:
- {
- SCSI_DEBUG_MSG ("scsiTransact: Blank Check Sense,",
- 0, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("Additional Sense Code = 0x%02xn",
- addSenseCode, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("ASCQ = 0x%02xn",
- addSenseCodeQual, 0, 0, 0, 0, 0);
- errnoSet (S_scsiLib_BLANK_CHECK);
- status = ERROR;
- goto cleanExit;
- }
- case SCSI_SENSE_KEY_ABORTED_COMMAND:
- {
- SCSI_DEBUG_MSG ("scsiTransact: Aborted Command Sense,",
- 0, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("Additional Sense Code = 0x%02xn",
- addSenseCode, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("ASCQ = 0x%02xn",
- addSenseCodeQual, 0, 0, 0, 0, 0);
- errnoSet (S_scsiLib_ABORTED_COMMAND);
- status = ERROR;
- goto cleanExit;
- }
- case SCSI_SENSE_KEY_VOLUME_OVERFLOW:
- {
- SCSI_DEBUG_MSG ("scsiTransact: Volume Overflow Sense,",
- 0, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("Additional Sense Code = 0x%02xn",
- addSenseCode, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("ASCQ = 0x%02xn",
- addSenseCodeQual, 0, 0, 0, 0, 0);
- errnoSet (S_scsiLib_VOLUME_OVERFLOW);
- status = ERROR;
- goto cleanExit;
- }
- default:
- {
- SCSI_DEBUG_MSG ("scsiTransact: Sense = %x,",
- senseKey, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("Additional Sense Code = 0x%02xn",
- addSenseCode, 0, 0, 0, 0, 0);
- SCSI_DEBUG_MSG ("ASCQ = 0x%02xn",
- addSenseCodeQual, 0, 0, 0, 0, 0);
- status = ERROR;
- errnoSet (S_scsiLib_UNKNOWN_SENSE_DATA);
- goto cleanExit;
- }
- }
- }
- case SCSI_STATUS_BUSY:
- case SCSI_STATUS_QUEUE_FULL:
- /* NOTE: should never occur - see "scsiCommand()" */
- /* FALLTHROUGH */
- default:
- SCSI_ERROR_MSG ("scsiTransact: unsupported status (0x%02x)n",
- pScsiXaction->statusByte & SCSI_STATUS_MASK,
- 0, 0, 0, 0, 0);
- status = ERROR;
- break;
- }
- cleanExit:
- return (status);
- }
- /*******************************************************************************
- *
- * chkMedChangeAndWP - check if medium has changed and if it is write protected
- *
- * Look at the sense information from a request sense command and determine if
- * the medium has been changed. Issue a MODE SENSE command, in order to check
- * if the medium is write protected. Set the appropriate values in either a
- * BLK_DEV structure or a SEQ_DEV structure, depending upon the type of device.
- *
- * RETURN: N/A
- */
- LOCAL void chkMedChangeAndWP
- (
- int addSenseCode, /* additional sense code */
- SCSI_PHYS_DEV * pScsiPhysDev /* ptr to a SCSI physcial device */
- )
- {
- SCSI_BLK_DEV_NODE *pBlkDevNode; /* ptr for looping through BLK_DEV list */
- SCSI_SEQ_DEV *pScsiSeqDev; /* ptr to SCSI_SEQ_DEV */
- UINT8 modeSenseHeader [4]; /* mode sense data header */
- SCSI_COMMAND modeSenseCommand;/* SCSI command byte array */
- SCSI_TRANSACTION scsiXaction; /* info on a SCSI xaction */
- int status = OK;
- /* Has there been a change in removable medium ? */
- if (addSenseCode == SCSI_ADD_SENSE_MEDIUM_CHANGED)
- {
- semTake (pScsiPhysDev->mutexSem, WAIT_FOREVER);
- if (pScsiPhysDev->scsiDevType == SCSI_DEV_SEQ_ACCESS)
- {
- pScsiSeqDev = pScsiPhysDev->pScsiSeqDev;
- pScsiSeqDev->seqDev.sd_readyChanged = TRUE;
- }
- else /* it is a block device */
- {
- for (pBlkDevNode = (SCSI_BLK_DEV_NODE *)
- lstFirst (&pScsiPhysDev->blkDevList);
- pBlkDevNode != NULL;
- pBlkDevNode = (SCSI_BLK_DEV_NODE *)
- lstNext (&pBlkDevNode->blkDevNode))
- {
- pBlkDevNode->scsiBlkDev.blkDev.bd_readyChanged = TRUE;
- }
- }
- semGive (pScsiPhysDev->mutexSem);
- }
- /* Was there some kind of a device reset ? */
- else if (addSenseCode == SCSI_ADD_SENSE_DEVICE_RESET)
- {
- pScsiPhysDev->resetFlag = TRUE;
- }
- /* issue a MODE SENSE command */
- /* The objective of issuing a MODE SENSE command is to
- * determine if the media was write protected.
- *
- * XXX - This may not be the best way to tell if we have
- * read-only or read-write media. Sense key 0x7 and
- * additionaly sense key of 0x27 could do the job
- * as well. This should be verified.
- */
- if (scsiCmdBuild (modeSenseCommand, &scsiXaction.cmdLength,
- SCSI_OPCODE_MODE_SENSE, pScsiPhysDev->scsiDevLUN, FALSE,
- 0, sizeof (modeSenseHeader), (UINT8) 0)
- == ERROR)
- return;
- scsiXaction.cmdAddress = modeSenseCommand;
- scsiXaction.dataAddress = modeSenseHeader;
- scsiXaction.dataDirection = O_RDONLY;
- scsiXaction.dataLength = sizeof (modeSenseHeader);
- scsiXaction.addLengthByte = MODE_SENSE_ADD_LENGTH_BYTE;
- scsiXaction.cmdTimeout = SCSI_TIMEOUT_5SEC;
- scsiXaction.tagType = SCSI_TAG_DEFAULT;
- scsiXaction.priority = SCSI_THREAD_TASK_PRIORITY;
- SCSI_DEBUG_MSG ("scsiTransact: issuing a MODE SENSE cmd.n",
- 0, 0, 0, 0, 0, 0);
- if ((status = scsiCommand (pScsiPhysDev, &scsiXaction)) == ERROR)
- {
- SCSI_DEBUG_MSG ("scsiCommand returned ERRORn", 0, 0, 0, 0, 0, 0);
- return;
- }
- /* MODE SENSE command status != GOOD indicates
- * fatal error
- */
- if (scsiXaction.statusByte != SCSI_STATUS_GOOD)
- {
- SCSI_DEBUG_MSG ("scsiTransact: bad MODE SELECT stat.n",
- 0, 0, 0, 0, 0, 0);
- return;
- }
- else /* MODE SENSE returned successfully */
- {
- semTake (pScsiPhysDev->mutexSem, WAIT_FOREVER);
- /*
- * if the WP bit of the device specific parameter
- * of the Mode parameter header is set then the
- * medium is write protected
- */
- if (pScsiPhysDev->scsiDevType == SCSI_DEV_SEQ_ACCESS)
- {
- pScsiSeqDev = pScsiPhysDev->pScsiSeqDev;
- pScsiSeqDev->seqDev.sd_mode =
- ( modeSenseHeader [SCSI_MODE_DEV_SPECIFIC_PARAM] &
- (UINT8) SCSI_DEV_SPECIFIC_WP_MASK
- ) ? O_RDONLY : O_RDWR;
- }
- else /* it is a block device */
- {
- for (pBlkDevNode = (SCSI_BLK_DEV_NODE *)
- lstFirst (&pScsiPhysDev->blkDevList);
- pBlkDevNode != NULL;
- pBlkDevNode = (SCSI_BLK_DEV_NODE *)
- lstNext (&pBlkDevNode->blkDevNode))
- {
- pBlkDevNode->scsiBlkDev.blkDev.bd_mode =
- ( modeSenseHeader [SCSI_MODE_DEV_SPECIFIC_PARAM] &
- (UINT8) SCSI_DEV_SPECIFIC_WP_MASK
- ) ? O_RDONLY : O_RDWR;
- }
- }
- semGive (pScsiPhysDev->mutexSem);
- SCSI_DEBUG_MSG ("Write-protect bit = %x.n",
- ( modeSenseHeader [SCSI_MODE_DEV_SPECIFIC_PARAM] &
- (UINT8) SCSI_DEV_SPECIFIC_WP_MASK
- ), 0, 0, 0, 0, 0);
- }
- }
- /*******************************************************************************
- *
- * scsi2Ioctl - perform a device-specific control function
- *
- * This routine performs a specified function using a specified SCSI physical
- * device.
- *
- * RETURNS: The status of the request, or ERROR if the request is unsupported.
- */
- LOCAL STATUS scsi2Ioctl
- (
- SCSI_PHYS_DEV *pScsiPhysDev,/* ptr to SCSI physical device info */
- int function, /* function code */
- int arg /* argument to pass called function */
- )
- {
- switch (function)
- {
- case FIOSCSICOMMAND:
- return ((*pScsiPhysDev->pScsiCtrl->scsiTransact)
- (pScsiPhysDev, (SCSI_TRANSACTION *) arg));
- case FIODISKFORMAT:
- /* issue a FORMAT UNIT command with default parameters */
- return (scsiFormatUnit (pScsiPhysDev, 0, 0, 0, 0,
- (char *) NULL, 0));
- case FIOERASE:
- /* issue a tape erase command */
- return (scsiErase (pScsiPhysDev, arg));
- case FIODENSITYSET:
- case FIODENSITYGET:
- case FIOBLKSIZESET:
- case FIOBLKSIZEGET:
- {
- int tmp;
- tmp = (scsiSeqIoctl ((SCSI_SEQ_DEV *)pScsiPhysDev, function, arg));
- logMsg ("scsiIoctl: blkSize: %dn", tmp,0,0,0,0,0);
- return tmp;
- }
- default:
- errnoSet (S_ioLib_UNKNOWN_REQUEST);
- return (ERROR);
- }
- }
- /*******************************************************************************
- *
- * scsiCacheSnoopEnable - inform SCSI that hardware snooping of caches is enabled
- *
- * This routine informs the SCSI library that hardware snooping is enabled
- * and that scsi2Lib need not execute any cache coherency code.
- * In order to make scsi2Lib aware that hardware
- * snooping is enabled, this routine should be called after all SCSI-2
- * initializations, especially after scsi2CtrlInit().
- *
- * RETURNS: N/A
- */
- void scsiCacheSnoopEnable
- (
- SCSI_CTRL * pScsiCtrl /* pointer to a SCSI_CTRL structure */
- )
- {
- pScsiCtrl->cacheSnooping = TRUE;
- }
- /*******************************************************************************
- *
- * scsiCacheSnoopDisable - inform SCSI that hardware snooping of caches is disabled
- *
- * This routine informs the SCSI library that hardware snooping is disabled
- * and that scsi2Lib should execute any neccessary cache coherency code.
- * In order to make scsi2Lib aware that hardware
- * snooping is disabled, this routine should be called after all SCSI-2
- * initializations, especially after scsi2CtrlInit().
- *
- * RETURNS: N/A
- */
- void scsiCacheSnoopDisable
- (
- SCSI_CTRL * pScsiCtrl /* pointer to a SCSI_CTRL structure */
- )
- {
- pScsiCtrl->cacheSnooping = FALSE;
- }