@param[out] Slot The available slot.\r
\r
@retval EFI_SUCCESS The available slot was found successfully.\r
+ @retval EFI_NOT_READY No slot is available at this moment.\r
\r
**/\r
EFI_STATUS\r
OUT UINT8 *Slot\r
)\r
{\r
+ UINT8 Nutrs;\r
+ UINT8 Index;\r
+ UINT32 Data;\r
+ EFI_STATUS Status;\r
+\r
ASSERT ((Private != NULL) && (Slot != NULL));\r
\r
- //\r
- // The simplest algo to always use slot 0.\r
- // TODO: enhance it to support async transfer with multiple slot.\r
- //\r
- *Slot = 0;\r
+ Status = UfsMmioRead32 (Private, UFS_HC_UTRLDBR_OFFSET, &Data);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
\r
- return EFI_SUCCESS;\r
+ Nutrs = (UINT8)((Private->Capabilities & UFS_HC_CAP_NUTRS) + 1);\r
+\r
+ for (Index = 0; Index < Nutrs; Index++) {\r
+ if ((Data & (BIT0 << Index)) == 0) {\r
+ *Slot = Index;\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_READY;\r
}\r
\r
/**\r
@param[in] Lun The LUN of the UFS device to send the SCSI Request Packet.\r
@param[in, out] Packet A pointer to the SCSI Request Packet to send to a specified Lun of the\r
UFS device.\r
+ @param[in] Event If nonblocking I/O is not supported then Event is ignored, and blocking\r
+ I/O is performed. If Event is NULL, then blocking I/O is performed. If\r
+ Event is not NULL and non blocking I/O is supported, then\r
+ nonblocking I/O is performed, and Event will be signaled when the\r
+ SCSI Request Packet completes.\r
\r
@retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional\r
commands, InTransferLength bytes were transferred from\r
UfsExecScsiCmds (\r
IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
IN UINT8 Lun,\r
- IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
+ IN EFI_EVENT Event OPTIONAL\r
)\r
{\r
EFI_STATUS Status;\r
- UINT8 Slot;\r
- UTP_TRD *Trd;\r
- UINT32 CmdDescSize;\r
UTP_RESPONSE_UPIU *Response;\r
UINT16 SenseDataLen;\r
UINT32 ResTranCount;\r
- VOID *CmdDescHost;\r
- VOID *CmdDescMapping;\r
- VOID *DataBufMapping;\r
VOID *DataBuf;\r
EFI_PHYSICAL_ADDRESS DataBufPhyAddr;\r
UINT32 DataLen;\r
EDKII_UFS_HOST_CONTROLLER_OPERATION Flag;\r
UFS_DATA_DIRECTION DataDirection;\r
UTP_TR_PRD *PrdtBase;\r
+ EFI_TPL OldTpl;\r
+ UFS_PASS_THRU_TRANS_REQ *TransReq;\r
\r
- Trd = NULL;\r
- CmdDescHost = NULL;\r
- CmdDescMapping = NULL;\r
- DataBufMapping = NULL;\r
+ TransReq = AllocateZeroPool (sizeof (UFS_PASS_THRU_TRANS_REQ));\r
+ if (TransReq == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ TransReq->Signature = UFS_PASS_THRU_TRANS_REQ_SIG;\r
+ TransReq->TimeoutRemain = Packet->Timeout;\r
DataBufPhyAddr = 0;\r
UfsHc = Private->UfsHostController;\r
//\r
// Find out which slot of transfer request list is available.\r
//\r
- Status = UfsFindAvailableSlotInTrl (Private, &Slot);\r
+ Status = UfsFindAvailableSlotInTrl (Private, &TransReq->Slot);\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
\r
- Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot;\r
+ TransReq->Trd = ((UTP_TRD*)Private->UtpTrlBase) + TransReq->Slot;\r
\r
//\r
// Fill transfer request descriptor to this slot.\r
//\r
- Status = UfsCreateScsiCommandDesc (Private, Lun, Packet, Trd, &CmdDescHost, &CmdDescMapping);\r
+ Status = UfsCreateScsiCommandDesc (\r
+ Private,\r
+ Lun,\r
+ Packet,\r
+ TransReq->Trd,\r
+ &TransReq->CmdDescHost,\r
+ &TransReq->CmdDescMapping\r
+ );\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
\r
- CmdDescSize = Trd->PrdtO * sizeof (UINT32) + Trd->PrdtL * sizeof (UTP_TR_PRD);\r
+ TransReq->CmdDescSize = TransReq->Trd->PrdtO * sizeof (UINT32) + TransReq->Trd->PrdtL * sizeof (UTP_TR_PRD);\r
\r
if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
DataBuf = Packet->InDataBuffer;\r
DataBuf,\r
&MapLength,\r
&DataBufPhyAddr,\r
- &DataBufMapping\r
+ &TransReq->DataBufMapping\r
);\r
\r
if (EFI_ERROR (Status) || (DataLen != MapLength)) {\r
//\r
// Fill PRDT table of Command UPIU for executed SCSI cmd.\r
//\r
- PrdtBase = (UTP_TR_PRD*)((UINT8*)CmdDescHost + ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)));\r
+ PrdtBase = (UTP_TR_PRD*)((UINT8*)TransReq->CmdDescHost + ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)));\r
ASSERT (PrdtBase != NULL);\r
UfsInitUtpPrdt (PrdtBase, (VOID*)(UINTN)DataBufPhyAddr, DataLen);\r
\r
+ //\r
+ // Insert the async SCSI cmd to the Async I/O list\r
+ //\r
+ if (Event != NULL) {\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ TransReq->Packet = Packet;\r
+ TransReq->CallerEvent = Event;\r
+ InsertTailList (&Private->Queue, &TransReq->TransferList);\r
+ gBS->RestoreTPL (OldTpl);\r
+ }\r
+\r
//\r
// Start to execute the transfer request.\r
//\r
- UfsStartExecCmd (Private, Slot);\r
+ UfsStartExecCmd (Private, TransReq->Slot);\r
+\r
+ //\r
+ // Immediately return for async I/O.\r
+ //\r
+ if (Event != NULL) {\r
+ return EFI_SUCCESS;\r
+ }\r
\r
//\r
// Wait for the completion of the transfer request.\r
//\r
// Get sense data if exists\r
//\r
- Response = (UTP_RESPONSE_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32));\r
+ Response = (UTP_RESPONSE_UPIU*)((UINT8*)TransReq->CmdDescHost + TransReq->Trd->RuO * sizeof (UINT32));\r
ASSERT (Response != NULL);\r
SenseDataLen = Response->SenseDataLen;\r
SwapLittleEndianToBigEndian ((UINT8*)&SenseDataLen, sizeof (UINT16));\r
goto Exit;\r
}\r
\r
- if (Trd->Ocs == 0) {\r
+ if (TransReq->Trd->Ocs == 0) {\r
if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
if ((Response->Flags & BIT5) == BIT5) {\r
ResTranCount = Response->ResTranCount;\r
Exit:\r
UfsHc->Flush (UfsHc);\r
\r
- UfsStopExecCmd (Private, Slot);\r
+ UfsStopExecCmd (Private, TransReq->Slot);\r
\r
- if (DataBufMapping != NULL) {\r
- UfsHc->Unmap (UfsHc, DataBufMapping);\r
+ if (TransReq->DataBufMapping != NULL) {\r
+ UfsHc->Unmap (UfsHc, TransReq->DataBufMapping);\r
}\r
\r
Exit1:\r
- if (CmdDescMapping != NULL) {\r
- UfsHc->Unmap (UfsHc, CmdDescMapping);\r
+ if (TransReq->CmdDescMapping != NULL) {\r
+ UfsHc->Unmap (UfsHc, TransReq->CmdDescMapping);\r
}\r
- if (CmdDescHost != NULL) {\r
- UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost);\r
+ if (TransReq->CmdDescHost != NULL) {\r
+ UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (TransReq->CmdDescSize), TransReq->CmdDescHost);\r
+ }\r
+ if (TransReq != NULL) {\r
+ FreePool (TransReq);\r
}\r
return Status;\r
}\r
return EFI_SUCCESS;\r
}\r
\r
+\r
+/**\r
+ Internal helper function which will signal the caller event and clean up\r
+ resources.\r
+\r
+ @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data\r
+ structure.\r
+ @param[in] TransReq The pointer to the UFS_PASS_THRU_TRANS_REQ data\r
+ structure.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+SignalCallerEvent (\r
+ IN UFS_PASS_THRU_PRIVATE_DATA *Private,\r
+ IN UFS_PASS_THRU_TRANS_REQ *TransReq\r
+ )\r
+{\r
+ EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc;\r
+ EFI_EVENT CallerEvent;\r
+\r
+ UfsHc = Private->UfsHostController;\r
+ CallerEvent = TransReq->CallerEvent;\r
+\r
+ RemoveEntryList (&TransReq->TransferList);\r
+\r
+ UfsHc->Flush (UfsHc);\r
+\r
+ UfsStopExecCmd (Private, TransReq->Slot);\r
+\r
+ if (TransReq->DataBufMapping != NULL) {\r
+ UfsHc->Unmap (UfsHc, TransReq->DataBufMapping);\r
+ }\r
+\r
+ if (TransReq->CmdDescMapping != NULL) {\r
+ UfsHc->Unmap (UfsHc, TransReq->CmdDescMapping);\r
+ }\r
+ if (TransReq->CmdDescHost != NULL) {\r
+ UfsHc->FreeBuffer (\r
+ UfsHc,\r
+ EFI_SIZE_TO_PAGES (TransReq->CmdDescSize),\r
+ TransReq->CmdDescHost\r
+ );\r
+ }\r
+ if (TransReq != NULL) {\r
+ FreePool (TransReq);\r
+ }\r
+\r
+ gBS->SignalEvent (CallerEvent);\r
+ return;\r
+}\r
+\r
+/**\r
+ Call back function when the timer event is signaled.\r
+\r
+ @param[in] Event The Event this notify function registered to.\r
+ @param[in] Context Pointer to the context data registered to the Event.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+ProcessAsyncTaskList (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ UFS_PASS_THRU_PRIVATE_DATA *Private;\r
+ LIST_ENTRY *Entry;\r
+ LIST_ENTRY *NextEntry;\r
+ UFS_PASS_THRU_TRANS_REQ *TransReq;\r
+ EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet;\r
+ UTP_RESPONSE_UPIU *Response;\r
+ UINT16 SenseDataLen;\r
+ UINT32 ResTranCount;\r
+ UINT32 SlotsMap;\r
+ UINT32 Value;\r
+ EFI_STATUS Status;\r
+\r
+ Private = (UFS_PASS_THRU_PRIVATE_DATA*) Context;\r
+ SlotsMap = 0;\r
+\r
+ //\r
+ // Check the entries in the async I/O queue are done or not.\r
+ //\r
+ if (!IsListEmpty(&Private->Queue)) {\r
+ EFI_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->Queue) {\r
+ TransReq = UFS_PASS_THRU_TRANS_REQ_FROM_THIS (Entry);\r
+ Packet = TransReq->Packet;\r
+\r
+ if ((SlotsMap & (BIT0 << TransReq->Slot)) != 0) {\r
+ return;\r
+ }\r
+ SlotsMap |= BIT0 << TransReq->Slot;\r
+\r
+ Status = UfsMmioRead32 (Private, UFS_HC_UTRLDBR_OFFSET, &Value);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // TODO: Should find/add a proper host adapter return status for this\r
+ // case.\r
+ //\r
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR;\r
+ DEBUG ((EFI_D_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p UfsMmioRead32() Error.\n", TransReq->CallerEvent));\r
+ SignalCallerEvent (Private, TransReq);\r
+ continue;\r
+ }\r
+\r
+ if ((Value & (BIT0 << TransReq->Slot)) != 0) {\r
+ //\r
+ // Scsi cmd not finished yet.\r
+ //\r
+ if (TransReq->TimeoutRemain > UFS_HC_ASYNC_TIMER) {\r
+ TransReq->TimeoutRemain -= UFS_HC_ASYNC_TIMER;\r
+ continue;\r
+ } else {\r
+ //\r
+ // Timeout occurs.\r
+ //\r
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND;\r
+ DEBUG ((EFI_D_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p EFI_TIMEOUT.\n", TransReq->CallerEvent));\r
+ SignalCallerEvent (Private, TransReq);\r
+ continue;\r
+ }\r
+ } else {\r
+ //\r
+ // Scsi cmd finished.\r
+ //\r
+ // Get sense data if exists\r
+ //\r
+ Response = (UTP_RESPONSE_UPIU*)((UINT8*)TransReq->CmdDescHost + TransReq->Trd->RuO * sizeof (UINT32));\r
+ ASSERT (Response != NULL);\r
+ SenseDataLen = Response->SenseDataLen;\r
+ SwapLittleEndianToBigEndian ((UINT8*)&SenseDataLen, sizeof (UINT16));\r
+\r
+ if ((Packet->SenseDataLength != 0) && (Packet->SenseData != NULL)) {\r
+ CopyMem (Packet->SenseData, Response->SenseData, SenseDataLen);\r
+ Packet->SenseDataLength = (UINT8)SenseDataLen;\r
+ }\r
+\r
+ //\r
+ // Check the transfer request result.\r
+ //\r
+ Packet->TargetStatus = Response->Status;\r
+ if (Response->Response != 0) {\r
+ DEBUG ((EFI_D_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p Target Failure.\n", TransReq->CallerEvent));\r
+ SignalCallerEvent (Private, TransReq);\r
+ continue;\r
+ }\r
+\r
+ if (TransReq->Trd->Ocs == 0) {\r
+ if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
+ if ((Response->Flags & BIT5) == BIT5) {\r
+ ResTranCount = Response->ResTranCount;\r
+ SwapLittleEndianToBigEndian ((UINT8*)&ResTranCount, sizeof (UINT32));\r
+ Packet->InTransferLength -= ResTranCount;\r
+ }\r
+ } else {\r
+ if ((Response->Flags & BIT5) == BIT5) {\r
+ ResTranCount = Response->ResTranCount;\r
+ SwapLittleEndianToBigEndian ((UINT8*)&ResTranCount, sizeof (UINT32));\r
+ Packet->OutTransferLength -= ResTranCount;\r
+ }\r
+ }\r
+ } else {\r
+ DEBUG ((EFI_D_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p Target Device Error.\n", TransReq->CallerEvent));\r
+ SignalCallerEvent (Private, TransReq);\r
+ continue;\r
+ }\r
+\r
+ DEBUG ((EFI_D_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p Success.\n", TransReq->CallerEvent));\r
+ SignalCallerEvent (Private, TransReq);\r
+ }\r
+ }\r
+ }\r
+}\r
+\r