📄 scsimgrlib.c
字号:
break; } }/********************************************************************************* scsiMgrReplyProc - reply to a client** Parse the reply type and act accordingly.** NOTE* Currently only one reply type (transaction complete) is accepted, however* this routine is intended to allow other reply types in future (e.g., to* support SCSI target mode).** RETURNS: N/A*/LOCAL void scsiMgrReplyProc ( SCSI_CTRL * pScsiCtrl, SCSI_REPLY * pReply ) { SCSI_REPLY_TYPE type = pReply->type; SCSI_DEBUG_MSG ("scsiMgrReplyProc: client reply: %d\n", type, 0, 0, 0, 0, 0); switch (type) { case SCSI_REPLY_COMPLETE: scsiMgrCompleteReply (pReply); break; default: logMsg ("scsiMgrReplyProc: invalid action (%d) requested\n", type, 0, 0, 0, 0, 0); break; } }/********************************************************************************* scsiMgrActivateRequest - process an "Activate" request from a client** Start a watchdog timer for the transaction, then add the thread to the* target device's wait queue. It will be despatched at the next opportunity.** RETURNS: N/A*/LOCAL void scsiMgrActivateRequest ( SCSI_REQUEST * pRequest ) { SCSI_THREAD * pThread = pRequest->thread; SCSI_DEBUG_MSG ("scsiMgrActivateRequest: thread 0x%08x\n", (int) pThread, 0, 0, 0, 0, 0); wdStart (pThread->wdog, pThread->timeout, (FUNCPTR) scsiMgrTimeoutNotify, (int) pThread); scsiMgrPhysDevWaitQAdd (pThread->pScsiPhysDev, pThread); pThread->state = SCSI_THREAD_WAITING; }/********************************************************************************* scsiMgrRequestComplete - terminate processing of an "Activate" request** Cancel the watchdog timer associated with the thread, and post a message* requesting the client be sent a reply.** NOTE:* It might seem peculiar not to just reply directly to the client here.* The current approach allows the SCSI manager to better prioritise its* activities, and avoids a subtle race condition which could otherwise occur.** (The exact scenario is as follows: suppose a client task of higher priority* than the SCSI manager issues a SCSI command and then deletes the physical* device. If this routine unblocked the client rather than deferring this* to the top-level loop of the SCSI manager, the client would pre-empt the* SCSI manager and could then delete a device for which the SCSI manager may* still have some outstanding housekeeping to carry out. This would most* likely cause the SCSI manager task to fail. Although this sounds like a* contrived and unlikely scenario, it would actually happen during SCSI* auto-configuration by the root task or the shell.)*/LOCAL void scsiMgrRequestComplete ( SCSI_THREAD * pThread ) { SCSI_REPLY reply; SCSI_CTRL * pScsiCtrl = pThread->pScsiCtrl; wdCancel (pThread->wdog); reply.type = SCSI_REPLY_COMPLETE; reply.thread = pThread; reply.status = pThread->status; reply.errNum = pThread->errNum; if (rngBufPut (pScsiCtrl->replyQ, (char *) &reply, sizeof (reply)) != sizeof (reply)) { logMsg ("scsiMgrRequestComplete: rngBufPut failed\n", 0, 0, 0, 0, 0, 0); } }/******************************************************************************** scsiMgrCompleteReply - process a "Complete" reply to be sent to a client** Build an appropriate reply message and send it on the thread's message* queue. This unblocks the client task. Th thread contains status and* errno values associated with the reply.** RETURNS: N/A*/LOCAL void scsiMgrCompleteReply ( SCSI_REPLY * pReply ) { SCSI_THREAD * pThread = pReply->thread; SCSI_DEBUG_MSG ("scsiMgrCompleteReply: thread 0x%08x " "(status: %d, errno: %d)\n", (int) pThread, pThread->status, pThread->errNum, 0, 0, 0); if (msgQSend (pThread->replyQ, (char *) pReply, sizeof (SCSI_REPLY), NO_WAIT, MSG_PRI_NORMAL) != OK) { SCSI_ERROR_MSG ("scsiMgrCompleteReply: can't post reply message " "(errno = %d)\n", errno, 0, 0, 0, 0, 0); } }/******************************************************************************** scsiMgrRequestExecute - execute a SCSI request** (This routine is called by a client task to have the SCSI manager execute a* request.)** Post the request message on the SCSI manager's request queue, notifying it* that there is something there by giving its semaphore. Wait for the SCSI* manager's reply using the message queue associated with the thread.** NOTE:* Since the SCSI manager's request queue is implemented using a ring buffer,* access by multiple clients is serialised within this routine. This is* achieved using the controller's mutex semaphore, which is also used to* serialise access to the free thread list. This will not cause deadlock.** The return value of this function tells the caller whether the basic* request/reply mechanism worked or not, not whether or not the request* itself succeeded. In fact, this function should never return ERROR unless* the software has failed or data structures have been corrupted.** This function should not be called directly by application programs.** RETURNS: OK, or ERROR if the request could not be executed.** NOMANUAL*/STATUS scsiMgrRequestExecute ( SCSI_CTRL * pScsiCtrl, SCSI_REQUEST * pRequest, SCSI_REPLY * pReply ) { int requestSize = sizeof (SCSI_REQUEST); int replySize = sizeof (SCSI_REPLY); /* * Send request to SCSI manager task */ semTake (pScsiCtrl->mutexSem, WAIT_FOREVER); if (rngFreeBytes (pScsiCtrl->requestQ) < requestSize) { semGive (pScsiCtrl->mutexSem); logMsg ("scsiMgrRequestExecute: request queue is full\n", 0, 0, 0, 0, 0, 0); return (ERROR); } if (rngBufPut (pScsiCtrl->requestQ, (char *) pRequest, requestSize) != requestSize) { semGive (pScsiCtrl->mutexSem); logMsg ("scsiMgrRequestExecute: rngBufPut failed\n", 0, 0, 0, 0, 0, 0); return (ERROR); } semGive (pScsiCtrl->mutexSem); /* * Notify SCSI manager that request has been posted */ semGive (pScsiCtrl->actionSem); /* * Wait for request to be executed by SCSI manager */ if (msgQReceive (pRequest->thread->replyQ, (char *) pReply, replySize, WAIT_FOREVER) != replySize) { SCSI_DEBUG_MSG ("scsiMgrRequestExecute: msgQReceive failed " "(errno = %d)\n", errno, 0, 0, 0, 0, 0); return (ERROR); } return (OK); }/********************************************************************************* scsiMgrTimeoutNotify - notify the SCSI manager of a transaction timeout** Post a timeout message on the appropriate SCSI manager queue, then notify* the SCSI manager that there is something to do.** NOTE:* No access serialisation is required because timeout messages are only* posted by the timeout watchdog ISR.** RETURNS: N/A*/LOCAL void scsiMgrTimeoutNotify ( SCSI_THREAD * pThread ) { SCSI_CTRL * pScsiCtrl = pThread->pScsiCtrl; SCSI_TIMEOUT timeout; timeout.thread = pThread; if (rngBufPut (pScsiCtrl->timeoutQ, (char *) &timeout, sizeof (timeout)) != sizeof (timeout)) { logMsg ("scsiMgrTimeoutNotify: can't post timeout message\n", 0, 0, 0, 0, 0, 0); } semGive (pScsiCtrl->actionSem); }/********************************************************************************* scsiMgrEventNotify - notify the SCSI manager of a SCSI (controller) event** This routine posts an event message on the appropriate SCSI manager queue,* then notifies the SCSI manager that there is a message to be accepted.** NOTE:* This routine should not be called by application programs.** No access serialization is required, because event messages are only* posted by the SCSI controller ISR. See the reference entry for * scsiBusResetNotify().** RETURNS: OK, or ERROR if the SCSI manager's event queue is full.** SEE ALSO: scsiBusResetNotify()*/STATUS scsiMgrEventNotify ( SCSI_CTRL * pScsiCtrl, /* pointer to SCSI controller structure */ SCSI_EVENT * pEvent, /* pointer to the SCSI event */ int eventSize /* size of the event information */ ) { if (rngBufPut (pScsiCtrl->eventQ, (char *) pEvent, eventSize) != eventSize) { logMsg ("scsiMgrEventNotify: can't post event message\n", 0, 0, 0, 0, 0, 0); return (ERROR); } semGive (pScsiCtrl->actionSem); return (OK); }/********************************************************************************* scsiMgrBusReset - handle a controller-bus reset event ** This routine resets in turn: each attached physical device, each target,* and the controller-finite-state machine. In practice, this routine * implements the SCSI hard reset option.** NOTE:* This routine does not physically reset the SCSI bus; see scsiBusReset().* This routine should not be called by application programs.** RETURNS: N/A*/void scsiMgrBusReset ( SCSI_CTRL * pScsiCtrl /* SCSI ctrlr on which bus reset */ ) { UINT i; /* * Reset all physical devices */ for (i = 0; i < SCSI_MAX_PHYS_DEVS; ++i) { SCSI_PHYS_DEV * pScsiPhysDev = pScsiCtrl->physDevArr[i]; if (pScsiPhysDev != 0) scsiMgrPhysDevReset (pScsiPhysDev); } /* * Reset all SCSI targets */ for (i = 0; i < SCSI_MAX_TARGETS; ++i) scsiTargetReset (pScsiCtrl, i); /* * Reset controller state */ scsiMgrCtrlEvent (pScsiCtrl, SCSI_EVENT_DISCONNECTED); }/********************************************************************************* scsiMgrRunnableThreadGet - find the next thread to run, if any** If the controller is active, no thread can be started. Otherwise, find the* highest priority waiting thread by querying each physical device. If a* runnable thread is found, do not remove it from any wait queue.** NOTE: a simple round-robin scheme is used to avoid unfairly favouring* threads from any particular physical device, when they all have equal* priority.** RETURNS: thread ptr, or 0 if no runnable thread exists*/LOCAL SCSI_THREAD * scsiMgrRunnableThreadGet ( SCSI_CTRL * pScsiCtrl ) { SCSI_PRIORITY bestPriority; SCSI_THREAD * pBestThread; int bestDevIndex; int devIndex; int i; /* * Can't do anything if the controller is active ! */ if (pScsiCtrl->scsiSpecialHandler == NULL) if (pScsiCtrl->active) return (0); /* * Find best available thread on all existing physical devices */ bestPriority = SCSI_THREAD_MIN_PRIORITY; pBestThread = 0; bestDevIndex = 0; devIndex = pScsiCtrl->nextDev; for (i = 0; i < SCSI_MAX_PHYS_DEVS; ++i) { SCSI_PHYS_DEV * pScsiPhysDev = pScsiCtrl->physDevArr [devIndex]; /* * If this phys dev exists, and has a runnable thread with * higher priority than the best so far, remember it. */ if (pScsiPhysDev != 0) { SCSI_THREAD * pThread; pThread = scsiMgrPhysDevRunnableThreadGet (pScsiPhysDev); if ((pThread != 0) && SCSI_IS_HIGHER_PRIORITY (pThread->priority, bestPriority)) { bestDevIndex = devIndex; bestPriority = pThread->priority; pBestThread = pThread; } } if (++devIndex >= SCSI_MAX_PHYS_DEVS) devIndex = 0; } if (pBestThread != 0) { pScsiCtrl->nextDev = bestDevIndex + 1; if (pScsiCtrl->nextDev >= SCSI_MAX_PHYS_DEVS) pScsiCtrl->nextDev = 0; } return (pBestThread); }/********************************************************************************* scsiMgrThreadDespatch - despatch a new thread** Remove the thread from its device's wait queue, activate it and install it* on the device's active queue. If activation fails, complete the thread* with a suitable error code.** RETURNS: N/A*/LOCAL void scsiMgrThreadDespatch ( SCSI_THREAD * pThread ) { SCSI_PHYS_DEV * pScsiPhysDev = pThread->pScsiPhysDev; scsiMgrPhysDevWaitQRemove (pScsiPhysDev, pThread); if (scsiMgrThreadActivate (pThread) != OK) { pThread->status = ERROR; pThread->errNum = errno; scsiMgrThreadComplete (pThread); return; } scsiMgrPhysDevActiveQAdd (pScsiPhysDev, pThread); }/******************************************************************************** scsiMgrThreadComplete - complete an initiator thread** Check for contingent allegiance condition by parsing the SCSI status set* by the target device; set or clear the physical device's contingent* allegiance flag accordingly. (This affects the access serialisation rules* used to schedule threads on the device: for more details see* "scsiMgrPhysDevRunnableThreadGet()".)** Finish processing the client's request (i.e., eventually, reply).
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -