]> git.proxmox.com Git - mirror_edk2.git/commitdiff
MdeModulePkg UfsPassThruDxe: Add Non-blocking I/O Support
authorHao Wu <hao.a.wu@intel.com>
Fri, 11 Dec 2015 01:58:45 +0000 (01:58 +0000)
committerhwu1225 <hwu1225@Edk2>
Fri, 11 Dec 2015 01:58:45 +0000 (01:58 +0000)
Previously, UfsPassThruPassThru function does not handle the 'Event'
parameter and blocking read/write operations are always executed.

This commit enables non-blocking read/write feature for UFS devices.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Hao Wu <hao.a.wu@intel.com>
Reviewed-by: Feng Tian <feng.tian@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@19216 6f19259b-4bc3-4df7-8a09-765794883524

MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.c
MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h
MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c

index 306fd37a2979d24c5370abea9ba986a520f20a37..aa40e2747097b40d286b2ebfc7635b38d70a860c 100644 (file)
@@ -1,6 +1,6 @@
 /** @file\r
 \r
 /** @file\r
 \r
-  Copyright (c) 2014, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.<BR>\r
   This program and the accompanying materials\r
   are licensed and made available under the terms and conditions of the BSD License\r
   which accompanies this distribution.  The full text of the license may be found at\r
   This program and the accompanying materials\r
   are licensed and made available under the terms and conditions of the BSD License\r
   which accompanies this distribution.  The full text of the license may be found at\r
@@ -21,10 +21,7 @@ UFS_PASS_THRU_PRIVATE_DATA gUfsPassThruTemplate = {
   NULL,                           // Handle  \r
   {                               // ExtScsiPassThruMode\r
     0xFFFFFFFF,\r
   NULL,                           // Handle  \r
   {                               // ExtScsiPassThruMode\r
     0xFFFFFFFF,\r
-    //\r
-    // Note that the driver doesn't support ExtScsiPassThru non blocking I/O.\r
-    //\r
-    EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL,\r
+    EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_NONBLOCKIO,\r
     sizeof (UINTN)\r
   },\r
   {                               // ExtScsiPassThru\r
     sizeof (UINTN)\r
   },\r
   {                               // ExtScsiPassThru\r
@@ -64,6 +61,11 @@ UFS_PASS_THRU_PRIVATE_DATA gUfsPassThruTemplate = {
     },\r
     0x0000,                           // By default don't expose any Luns.\r
     0x0\r
     },\r
     0x0000,                           // By default don't expose any Luns.\r
     0x0\r
+  },\r
+  NULL,                           // TimerEvent\r
+  {                               // Queue\r
+    NULL,\r
+    NULL\r
   }\r
 };\r
 \r
   }\r
 };\r
 \r
@@ -212,7 +214,7 @@ UfsPassThruPassThru (
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  Status = UfsExecScsiCmds (Private, UfsLun, Packet);\r
+  Status = UfsExecScsiCmds (Private, UfsLun, Packet, Event);\r
 \r
   return Status;\r
 }\r
 \r
   return Status;\r
 }\r
@@ -816,6 +818,7 @@ UfsPassThruDriverBindingStart (
   //\r
   Private = AllocateCopyPool (sizeof (UFS_PASS_THRU_PRIVATE_DATA), &gUfsPassThruTemplate);\r
   if (Private == NULL) {\r
   //\r
   Private = AllocateCopyPool (sizeof (UFS_PASS_THRU_PRIVATE_DATA), &gUfsPassThruTemplate);\r
   if (Private == NULL) {\r
+    DEBUG ((EFI_D_ERROR, "Unable to allocate Ufs Pass Thru private data\n"));\r
     Status = EFI_OUT_OF_RESOURCES;\r
     goto Error;\r
   }\r
     Status = EFI_OUT_OF_RESOURCES;\r
     goto Error;\r
   }\r
@@ -823,6 +826,7 @@ UfsPassThruDriverBindingStart (
   Private->ExtScsiPassThru.Mode = &Private->ExtScsiPassThruMode;\r
   Private->UfsHostController    = UfsHc;\r
   Private->UfsHcBase            = UfsHcBase;\r
   Private->ExtScsiPassThru.Mode = &Private->ExtScsiPassThruMode;\r
   Private->UfsHostController    = UfsHc;\r
   Private->UfsHcBase            = UfsHcBase;\r
+  InitializeListHead (&Private->Queue);\r
 \r
   //\r
   // Initialize UFS Host Controller H/W.\r
 \r
   //\r
   // Initialize UFS Host Controller H/W.\r
@@ -873,6 +877,31 @@ UfsPassThruDriverBindingStart (
     }\r
   }\r
 \r
     }\r
   }\r
 \r
+  //\r
+  // Start the asynchronous interrupt monitor\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  ProcessAsyncTaskList,\r
+                  Private,\r
+                  &Private->TimerEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "Ufs Create Async Tasks Event Error, Status = %r\n", Status));\r
+    goto Error;\r
+  }\r
+\r
+  Status = gBS->SetTimer (\r
+                  Private->TimerEvent,\r
+                  TimerPeriodic,\r
+                  UFS_HC_ASYNC_TIMER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "Ufs Set Periodic Timer Error, Status = %r\n", Status));\r
+    goto Error;\r
+  }\r
+\r
   Status = gBS->InstallProtocolInterface (\r
                   &Controller,\r
                   &gEfiExtScsiPassThruProtocolGuid,\r
   Status = gBS->InstallProtocolInterface (\r
                   &Controller,\r
                   &gEfiExtScsiPassThruProtocolGuid,\r
@@ -899,6 +928,10 @@ Error:
       UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (Private->Nutrs * sizeof (UTP_TMRD)), Private->UtpTrlBase);\r
     }\r
 \r
       UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (Private->Nutrs * sizeof (UTP_TMRD)), Private->UtpTrlBase);\r
     }\r
 \r
+    if (Private->TimerEvent != NULL) {\r
+      gBS->CloseEvent (Private->TimerEvent);\r
+    }\r
+\r
     FreePool (Private);\r
   }\r
 \r
     FreePool (Private);\r
   }\r
 \r
@@ -953,6 +986,9 @@ UfsPassThruDriverBindingStop (
   UFS_PASS_THRU_PRIVATE_DATA            *Private;\r
   EFI_EXT_SCSI_PASS_THRU_PROTOCOL       *ExtScsiPassThru;\r
   EDKII_UFS_HOST_CONTROLLER_PROTOCOL    *UfsHc;\r
   UFS_PASS_THRU_PRIVATE_DATA            *Private;\r
   EFI_EXT_SCSI_PASS_THRU_PROTOCOL       *ExtScsiPassThru;\r
   EDKII_UFS_HOST_CONTROLLER_PROTOCOL    *UfsHc;\r
+  UFS_PASS_THRU_TRANS_REQ               *TransReq;\r
+  LIST_ENTRY                            *Entry;\r
+  LIST_ENTRY                            *NextEntry;\r
 \r
   DEBUG ((EFI_D_INFO, "==UfsPassThru Stop== Controller Controller = %x\n", Controller));\r
 \r
 \r
   DEBUG ((EFI_D_INFO, "==UfsPassThru Stop== Controller Controller = %x\n", Controller));\r
 \r
@@ -972,6 +1008,24 @@ UfsPassThruDriverBindingStop (
   Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS (ExtScsiPassThru);\r
   UfsHc   = Private->UfsHostController;\r
 \r
   Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS (ExtScsiPassThru);\r
   UfsHc   = Private->UfsHostController;\r
 \r
+  //\r
+  // Cleanup the resources of I/O requests in the async I/O queue\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
+\r
+      //\r
+      // TODO: Should find/add a proper host adapter return status for this\r
+      // case.\r
+      //\r
+      TransReq->Packet->HostAdapterStatus =\r
+        EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR;\r
+\r
+      SignalCallerEvent (Private, TransReq);\r
+    }\r
+  }\r
+\r
   Status = gBS->UninstallProtocolInterface (\r
                   Controller,\r
                   &gEfiExtScsiPassThruProtocolGuid,\r
   Status = gBS->UninstallProtocolInterface (\r
                   Controller,\r
                   &gEfiExtScsiPassThruProtocolGuid,\r
@@ -1002,6 +1056,10 @@ UfsPassThruDriverBindingStop (
     UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (Private->Nutrs * sizeof (UTP_TMRD)), Private->UtpTrlBase);\r
   }\r
 \r
     UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (Private->Nutrs * sizeof (UTP_TMRD)), Private->UtpTrlBase);\r
   }\r
 \r
+  if (Private->TimerEvent != NULL) {\r
+    gBS->CloseEvent (Private->TimerEvent);\r
+  }\r
+\r
   FreePool (Private);\r
 \r
   //\r
   FreePool (Private);\r
 \r
   //\r
index ce8066f71e84867bb77e96e6a038043b18ef30fb..4f7087f44f42022271e497a2f3b6f202b5b00973 100644 (file)
@@ -50,6 +50,14 @@ typedef struct {
   UINT16   Rsvd:4;\r
 } UFS_EXPOSED_LUNS;\r
 \r
   UINT16   Rsvd:4;\r
 } UFS_EXPOSED_LUNS;\r
 \r
+//\r
+// Iterate through the doule linked list. This is delete-safe.\r
+// Do not touch NextEntry\r
+//\r
+#define EFI_LIST_FOR_EACH_SAFE(Entry, NextEntry, ListHead)            \\r
+  for(Entry = (ListHead)->ForwardLink, NextEntry = Entry->ForwardLink;\\r
+      Entry != (ListHead); Entry = NextEntry, NextEntry = Entry->ForwardLink)\r
+\r
 typedef struct _UFS_PASS_THRU_PRIVATE_DATA {  \r
   UINT32                              Signature;\r
   EFI_HANDLE                          Handle;\r
 typedef struct _UFS_PASS_THRU_PRIVATE_DATA {  \r
   UINT32                              Signature;\r
   EFI_HANDLE                          Handle;\r
@@ -69,9 +77,37 @@ typedef struct _UFS_PASS_THRU_PRIVATE_DATA {
   VOID                                *TmrlMapping;\r
 \r
   UFS_EXPOSED_LUNS                    Luns;\r
   VOID                                *TmrlMapping;\r
 \r
   UFS_EXPOSED_LUNS                    Luns;\r
+\r
+  //\r
+  // For Non-blocking operation.\r
+  //\r
+  EFI_EVENT                           TimerEvent;\r
+  LIST_ENTRY                          Queue;\r
 } UFS_PASS_THRU_PRIVATE_DATA;\r
 \r
 } UFS_PASS_THRU_PRIVATE_DATA;\r
 \r
+#define UFS_PASS_THRU_TRANS_REQ_SIG   SIGNATURE_32 ('U', 'F', 'S', 'T')\r
+\r
+typedef struct {\r
+  UINT32                                        Signature;\r
+  LIST_ENTRY                                    TransferList;\r
+\r
+  UINT8                                         Slot;\r
+  UTP_TRD                                       *Trd;\r
+  UINT32                                        CmdDescSize;\r
+  VOID                                          *CmdDescHost;\r
+  VOID                                          *CmdDescMapping;\r
+  VOID                                          *DataBufMapping;\r
+\r
+  EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET    *Packet;\r
+  UINT64                                        TimeoutRemain;\r
+  EFI_EVENT                                     CallerEvent;\r
+} UFS_PASS_THRU_TRANS_REQ;\r
+\r
+#define UFS_PASS_THRU_TRANS_REQ_FROM_THIS(a) \\r
+    CR(a, UFS_PASS_THRU_TRANS_REQ, TransferList, UFS_PASS_THRU_TRANS_REQ_SIG)\r
+\r
 #define UFS_TIMEOUT                   EFI_TIMER_PERIOD_SECONDS(3)\r
 #define UFS_TIMEOUT                   EFI_TIMER_PERIOD_SECONDS(3)\r
+#define UFS_HC_ASYNC_TIMER            EFI_TIMER_PERIOD_MILLISECONDS(1)\r
 \r
 #define ROUNDUP8(x) (((x) % 8 == 0) ? (x) : ((x) / 8 + 1) * 8)\r
 \r
 \r
 #define ROUNDUP8(x) (((x) % 8 == 0) ? (x) : ((x) / 8 + 1) * 8)\r
 \r
@@ -587,6 +623,11 @@ UfsPassThruGetNextTarget (
   @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]      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
 \r
   @retval EFI_SUCCESS           The SCSI Request Packet was sent by the host. For bi-directional\r
                                 commands, InTransferLength bytes were transferred from\r
@@ -603,7 +644,8 @@ EFI_STATUS
 UfsExecScsiCmds (\r
   IN     UFS_PASS_THRU_PRIVATE_DATA                  *Private,\r
   IN     UINT8                                       Lun,\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
 /**\r
   );\r
 \r
 /**\r
@@ -719,6 +761,37 @@ UfsExecNopCmds (
   IN  UFS_PASS_THRU_PRIVATE_DATA       *Private\r
   );\r
 \r
   IN  UFS_PASS_THRU_PRIVATE_DATA       *Private\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
+/**\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
 extern EFI_COMPONENT_NAME_PROTOCOL  gUfsPassThruComponentName;\r
 extern EFI_COMPONENT_NAME2_PROTOCOL gUfsPassThruComponentName2;\r
 extern EFI_DRIVER_BINDING_PROTOCOL  gUfsPassThruDriverBinding;\r
 extern EFI_COMPONENT_NAME_PROTOCOL  gUfsPassThruComponentName;\r
 extern EFI_COMPONENT_NAME2_PROTOCOL gUfsPassThruComponentName2;\r
 extern EFI_DRIVER_BINDING_PROTOCOL  gUfsPassThruDriverBinding;\r
index 9f9abab40fded3ea72fc10e08669bcdfcd04ea38..4fbe199390f404ff1cd80e134750a9923073a2c1 100644 (file)
@@ -729,6 +729,7 @@ UfsCreateNopCommandDesc (
   @param[out] Slot          The available slot.\r
 \r
   @retval EFI_SUCCESS       The available slot was found successfully.\r
   @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
 \r
 **/\r
 EFI_STATUS\r
@@ -737,15 +738,28 @@ UfsFindAvailableSlotInTrl (
      OUT UINT8                        *Slot\r
   )\r
 {\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
   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
 \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
 }\r
 \r
 /**\r
@@ -1382,6 +1396,11 @@ Exit:
   @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]      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
 \r
   @retval EFI_SUCCESS           The SCSI Request Packet was sent by the host. For bi-directional\r
                                 commands, InTransferLength bytes were transferred from\r
@@ -1398,19 +1417,14 @@ EFI_STATUS
 UfsExecScsiCmds (\r
   IN     UFS_PASS_THRU_PRIVATE_DATA                  *Private,\r
   IN     UINT8                                       Lun,\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
   )\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
   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
   VOID                                 *DataBuf;\r
   EFI_PHYSICAL_ADDRESS                 DataBufPhyAddr;\r
   UINT32                               DataLen;\r
@@ -1419,32 +1433,44 @@ UfsExecScsiCmds (
   EDKII_UFS_HOST_CONTROLLER_OPERATION  Flag;\r
   UFS_DATA_DIRECTION                   DataDirection;\r
   UTP_TR_PRD                           *PrdtBase;\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
 \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
   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
   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
 \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
   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
 \r
   if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
     DataBuf       = Packet->InDataBuffer;\r
@@ -1468,7 +1494,7 @@ UfsExecScsiCmds (
                          DataBuf,\r
                          &MapLength,\r
                          &DataBufPhyAddr,\r
                          DataBuf,\r
                          &MapLength,\r
                          &DataBufPhyAddr,\r
-                         &DataBufMapping\r
+                         &TransReq->DataBufMapping\r
                          );\r
 \r
     if (EFI_ERROR (Status) || (DataLen != MapLength)) {\r
                          );\r
 \r
     if (EFI_ERROR (Status) || (DataLen != MapLength)) {\r
@@ -1478,14 +1504,32 @@ UfsExecScsiCmds (
   //\r
   // Fill PRDT table of Command UPIU for executed SCSI cmd.\r
   //\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
   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
   //\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
   //\r
   // Wait for the completion of the transfer request.\r
@@ -1498,7 +1542,7 @@ UfsExecScsiCmds (
   //\r
   // Get sense data if exists\r
   //\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
   ASSERT (Response != NULL);\r
   SenseDataLen = Response->SenseDataLen;\r
   SwapLittleEndianToBigEndian ((UINT8*)&SenseDataLen, sizeof (UINT16));\r
@@ -1518,7 +1562,7 @@ UfsExecScsiCmds (
     goto Exit;\r
   }\r
 \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
     if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
       if ((Response->Flags & BIT5) == BIT5) {\r
         ResTranCount = Response->ResTranCount;\r
@@ -1539,18 +1583,21 @@ UfsExecScsiCmds (
 Exit:\r
   UfsHc->Flush (UfsHc);\r
 \r
 Exit:\r
   UfsHc->Flush (UfsHc);\r
 \r
-  UfsStopExecCmd (Private, Slot);\r
+  UfsStopExecCmd (Private, TransReq->Slot);\r
 \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
   }\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
   }\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
   }\r
   return Status;\r
 }\r
@@ -2123,3 +2170,178 @@ UfsControllerStop (
   return EFI_SUCCESS;\r
 }\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