]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.c
MdeModulePkg: AtaAtapiPassThru: select master/slave around DIAG command
[mirror_edk2.git] / MdeModulePkg / Bus / Ata / AtaAtapiPassThru / AtaAtapiPassThru.c
index e5bfc39800e37ba6993e972aa16a439c7e34d871..870900f1060db9310d4008e4bf1e1efae89dd341 100644 (file)
@@ -2,7 +2,7 @@
   This file implements ATA_PASSTHRU_PROCTOCOL and EXT_SCSI_PASSTHRU_PROTOCOL interfaces\r
   for managed ATA controllers.\r
 \r
-  Copyright (c) 2010 - 2012, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2010 - 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
@@ -717,7 +717,7 @@ AtaAtapiPassThruStart (
                     &Supports\r
                     );\r
   if (!EFI_ERROR (Status)) {\r
-    Supports &= EFI_PCI_DEVICE_ENABLE;\r
+    Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE;\r
     Status = PciIo->Attributes (\r
                       PciIo,\r
                       EfiPciIoAttributeOperationEnable,\r
@@ -870,53 +870,17 @@ AtaAtapiPassThruStop (
 \r
   Instance = ATA_PASS_THRU_PRIVATE_DATA_FROM_THIS (AtaPassThru);\r
 \r
-  //\r
-  // Close Non-Blocking timer and free Task list.\r
-  //\r
-  if (Instance->TimerEvent != NULL) {\r
-    gBS->CloseEvent (Instance->TimerEvent);\r
-    Instance->TimerEvent = NULL;\r
-  }\r
-  DestroyAsynTaskList (Instance, FALSE);\r
+  Status = gBS->UninstallMultipleProtocolInterfaces (\r
+                  Controller,\r
+                  &gEfiAtaPassThruProtocolGuid, &(Instance->AtaPassThru),\r
+                  &gEfiExtScsiPassThruProtocolGuid, &(Instance->ExtScsiPassThru),\r
+                  NULL\r
+                  );\r
 \r
-  //\r
-  // Disable this ATA host controller.\r
-  //\r
-  PciIo  = Instance->PciIo;\r
-  Status = PciIo->Attributes (\r
-                    PciIo,\r
-                    EfiPciIoAttributeOperationSupported,\r
-                    0,\r
-                    &Supports\r
-                    );\r
-  if (!EFI_ERROR (Status)) {\r
-    Supports &= EFI_PCI_DEVICE_ENABLE;\r
-    PciIo->Attributes (\r
-             PciIo,\r
-             EfiPciIoAttributeOperationDisable,\r
-             Supports,\r
-             NULL\r
-             );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_DEVICE_ERROR;\r
   }\r
 \r
-  //\r
-  // Restore original PCI attributes\r
-  //\r
-  Status = PciIo->Attributes (\r
-                    PciIo,\r
-                    EfiPciIoAttributeOperationSet,\r
-                    Instance->OriginalPciAttributes,\r
-                    NULL\r
-                    );\r
-  ASSERT_EFI_ERROR (Status);\r
-\r
-  gBS->UninstallMultipleProtocolInterfaces (\r
-         Controller,\r
-         &gEfiAtaPassThruProtocolGuid, &(Instance->AtaPassThru),\r
-         &gEfiExtScsiPassThruProtocolGuid, &(Instance->ExtScsiPassThru),\r
-         NULL\r
-         );\r
-\r
   //\r
   // Close protocols opened by AtaAtapiPassThru controller driver\r
   //\r
@@ -927,15 +891,25 @@ AtaAtapiPassThruStop (
          Controller\r
          );\r
 \r
+  //\r
+  // Close Non-Blocking timer and free Task list.\r
+  //\r
+  if (Instance->TimerEvent != NULL) {\r
+    gBS->CloseEvent (Instance->TimerEvent);\r
+    Instance->TimerEvent = NULL;\r
+  }\r
+  DestroyAsynTaskList (Instance, FALSE);\r
   //\r
   // Free allocated resource\r
   //\r
-  DestroyDeviceInfoList(Instance);\r
+  DestroyDeviceInfoList (Instance);\r
 \r
   //\r
   // If the current working mode is AHCI mode, then pre-allocated resource\r
   // for AHCI initialization should be released.\r
   //\r
+  PciIo = Instance->PciIo;\r
+\r
   if (Instance->Mode == EfiAtaAhciMode) {\r
     AhciRegisters = &Instance->AhciRegisters;\r
     PciIo->Unmap (\r
@@ -966,6 +940,37 @@ AtaAtapiPassThruStop (
              AhciRegisters->AhciRFis\r
              );\r
   }\r
+\r
+  //\r
+  // Disable this ATA host controller.\r
+  //\r
+  Status = PciIo->Attributes (\r
+                    PciIo,\r
+                    EfiPciIoAttributeOperationSupported,\r
+                    0,\r
+                    &Supports\r
+                    );\r
+  if (!EFI_ERROR (Status)) {\r
+    Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE;\r
+    PciIo->Attributes (\r
+             PciIo,\r
+             EfiPciIoAttributeOperationDisable,\r
+             Supports,\r
+             NULL\r
+             );\r
+  }\r
+\r
+  //\r
+  // Restore original PCI attributes\r
+  //\r
+  Status = PciIo->Attributes (\r
+                    PciIo,\r
+                    EfiPciIoAttributeOperationSet,\r
+                    Instance->OriginalPciAttributes,\r
+                    NULL\r
+                    );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
   FreePool (Instance);\r
 \r
   return Status;\r
@@ -1250,6 +1255,7 @@ AtaPassThruPassThru (
   UINT32                          MaxSectorCount;\r
   ATA_NONBLOCK_TASK               *Task;\r
   EFI_TPL                         OldTpl;\r
+  UINT32                          BlockSize;\r
 \r
   Instance = ATA_PASS_THRU_PRIVATE_DATA_FROM_THIS (This);\r
 \r
@@ -1265,26 +1271,13 @@ AtaPassThruPassThru (
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  //\r
-  // convert the transfer length from sector count to byte.\r
-  //\r
-  if (((Packet->Length & EFI_ATA_PASS_THRU_LENGTH_BYTES) == 0) &&\r
-       (Packet->InTransferLength != 0)) {\r
-    Packet->InTransferLength = Packet->InTransferLength * 0x200;\r
-  }\r
-\r
-  //\r
-  // convert the transfer length from sector count to byte.\r
-  //\r
-  if (((Packet->Length & EFI_ATA_PASS_THRU_LENGTH_BYTES) == 0) &&\r
-       (Packet->OutTransferLength != 0)) {\r
-    Packet->OutTransferLength = Packet->OutTransferLength * 0x200;\r
-  }\r
-\r
   Node = SearchDeviceInfoList (Instance, Port, PortMultiplierPort, EfiIdeHarddisk);\r
 \r
   if (Node == NULL) {\r
-    return EFI_INVALID_PARAMETER;\r
+    Node = SearchDeviceInfoList(Instance, Port, PortMultiplierPort, EfiIdeCdrom);\r
+    if (Node == NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
   }\r
 \r
   //\r
@@ -1306,13 +1299,39 @@ AtaPassThruPassThru (
     }\r
   }\r
 \r
+  BlockSize = 0x200;\r
+  if ((IdentifyData->AtaData.phy_logic_sector_support & (BIT14 | BIT15)) == BIT14) {\r
+    //\r
+    // Check logical block size\r
+    //\r
+    if ((IdentifyData->AtaData.phy_logic_sector_support & BIT12) != 0) {\r
+      BlockSize = (UINT32) (((IdentifyData->AtaData.logic_sector_size_hi << 16) | IdentifyData->AtaData.logic_sector_size_lo) * sizeof (UINT16));\r
+    }\r
+  }\r
+\r
+  //\r
+  // convert the transfer length from sector count to byte.\r
+  //\r
+  if (((Packet->Length & EFI_ATA_PASS_THRU_LENGTH_BYTES) == 0) &&\r
+       (Packet->InTransferLength != 0)) {\r
+    Packet->InTransferLength = Packet->InTransferLength * BlockSize;\r
+  }\r
+\r
+  //\r
+  // convert the transfer length from sector count to byte.\r
+  //\r
+  if (((Packet->Length & EFI_ATA_PASS_THRU_LENGTH_BYTES) == 0) &&\r
+       (Packet->OutTransferLength != 0)) {\r
+    Packet->OutTransferLength = Packet->OutTransferLength * BlockSize;\r
+  }\r
+\r
   //\r
   // If the data buffer described by InDataBuffer/OutDataBuffer and InTransferLength/OutTransferLength\r
   // is too big to be transferred in a single command, then no data is transferred and EFI_BAD_BUFFER_SIZE\r
   // is returned.\r
   //\r
-  if (((Packet->InTransferLength != 0) && (Packet->InTransferLength > MaxSectorCount * 0x200)) ||\r
-      ((Packet->OutTransferLength != 0) && (Packet->OutTransferLength > MaxSectorCount * 0x200))) {\r
+  if (((Packet->InTransferLength != 0) && (Packet->InTransferLength > MaxSectorCount * BlockSize)) ||\r
+      ((Packet->OutTransferLength != 0) && (Packet->OutTransferLength > MaxSectorCount * BlockSize))) {\r
     return EFI_BAD_BUFFER_SIZE;\r
   }\r
 \r
@@ -1331,7 +1350,12 @@ AtaPassThruPassThru (
     Task->Packet         = Packet;\r
     Task->Event          = Event;\r
     Task->IsStart        = FALSE;\r
-    Task->RetryTimes     = 0;\r
+    Task->RetryTimes     = DivU64x32(Packet->Timeout, 1000) + 1;\r
+    if (Packet->Timeout == 0) {\r
+      Task->InfiniteWait = TRUE;\r
+    } else {\r
+      Task->InfiniteWait = FALSE;\r
+    }\r
 \r
     OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
     InsertTailList (&Instance->NonBlockingTaskList, &Task->Link);\r
@@ -1761,7 +1785,10 @@ AtaPassThruResetPort (
   IN UINT16                     Port\r
   )\r
 {\r
-  return EFI_UNSUPPORTED;\r
+  //\r
+  // Return success directly then upper layer driver could think reset port operation is done.\r
+  //\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
@@ -1803,7 +1830,72 @@ AtaPassThruResetDevice (
   IN UINT16                     PortMultiplierPort\r
   )\r
 {\r
-  return EFI_UNSUPPORTED;\r
+  ATA_ATAPI_PASS_THRU_INSTANCE    *Instance;\r
+  LIST_ENTRY                      *Node;\r
+\r
+  Instance = ATA_PASS_THRU_PRIVATE_DATA_FROM_THIS (This);\r
+\r
+  Node = SearchDeviceInfoList (Instance, Port, PortMultiplierPort, EfiIdeHarddisk);\r
+\r
+  if (Node == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Return success directly then upper layer driver could think reset device operation is done.\r
+  //\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Sumbit ATAPI request sense command.\r
+\r
+  @param[in] This            A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.\r
+  @param[in] Target          The Target is an array of size TARGET_MAX_BYTES and it represents\r
+                             the id of the SCSI device to send the SCSI Request Packet. Each\r
+                             transport driver may choose to utilize a subset of this size to suit the needs\r
+                             of transport target representation. For example, a Fibre Channel driver\r
+                             may use only 8 bytes (WWN) to represent an FC target.\r
+  @param[in] Lun             The LUN of the SCSI device to send the SCSI Request Packet.\r
+  @param[in] SenseData       A pointer to store sense data.\r
+  @param[in] SenseDataLength The sense data length.\r
+  @param[in] Timeout         The timeout value to execute this cmd, uses 100ns as a unit.\r
+\r
+  @retval EFI_SUCCESS        Send out the ATAPI packet command successfully.\r
+  @retval EFI_DEVICE_ERROR   The device failed to send data.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AtaPacketRequestSense (\r
+  IN  EFI_EXT_SCSI_PASS_THRU_PROTOCOL         *This,\r
+  IN  UINT8                                   *Target,\r
+  IN  UINT64                                  Lun,\r
+  IN  VOID                                    *SenseData,\r
+  IN  UINT8                                   SenseDataLength,\r
+  IN  UINT64                                  Timeout\r
+  )\r
+{\r
+  EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  Packet;\r
+  UINT8                                       Cdb[12];\r
+  EFI_STATUS                                  Status;\r
+\r
+  ZeroMem (&Packet, sizeof (EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET));\r
+  ZeroMem (Cdb, 12);\r
+\r
+  Cdb[0] = ATA_CMD_REQUEST_SENSE;\r
+  Cdb[4] = SenseDataLength;\r
+\r
+  Packet.Timeout          = Timeout;\r
+  Packet.Cdb              = Cdb;\r
+  Packet.CdbLength        = 12;\r
+  Packet.DataDirection    = EFI_EXT_SCSI_DATA_DIRECTION_READ;\r
+  Packet.InDataBuffer     = SenseData;\r
+  Packet.InTransferLength = SenseDataLength;\r
+\r
+  Status = ExtScsiPassThruPassThru (This, Target, Lun, &Packet, NULL);\r
+\r
+  return Status;\r
 }\r
 \r
 /**\r
@@ -1864,8 +1956,13 @@ ExtScsiPassThruPassThru (
   EFI_ATA_HC_WORK_MODE            Mode;\r
   LIST_ENTRY                      *Node;\r
   EFI_ATA_DEVICE_INFO             *DeviceInfo;\r
+  BOOLEAN                         SenseReq;\r
+  EFI_SCSI_SENSE_DATA             *PtrSenseData;\r
+  UINTN                           SenseDataLen;\r
+  EFI_STATUS                      SenseStatus;\r
 \r
-  Instance = EXT_SCSI_PASS_THRU_PRIVATE_DATA_FROM_THIS (This);\r
+  SenseDataLen = 0;\r
+  Instance     = EXT_SCSI_PASS_THRU_PRIVATE_DATA_FROM_THIS (This);\r
 \r
   if ((Packet == NULL) || (Packet->Cdb == NULL)) {\r
     return EFI_INVALID_PARAMETER;\r
@@ -1879,6 +1976,10 @@ ExtScsiPassThruPassThru (
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
+  if ((Packet->SenseDataLength != 0) && (Packet->SenseData == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
   if ((This->Mode->IoAlign > 1) && !IS_ALIGNED(Packet->InDataBuffer, This->Mode->IoAlign)) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
@@ -1926,6 +2027,10 @@ ExtScsiPassThruPassThru (
   //\r
   if (*((UINT8*)Packet->Cdb) == ATA_CMD_IDENTIFY_DEVICE) {\r
     CopyMem (Packet->InDataBuffer, DeviceInfo->IdentifyData, sizeof (EFI_IDENTIFY_DATA));\r
+    //\r
+    // For IDENTIFY DEVICE cmd, we don't need to get sense data.\r
+    //\r
+    Packet->SenseDataLength = 0;\r
     return EFI_SUCCESS;\r
   }\r
 \r
@@ -1951,6 +2056,46 @@ ExtScsiPassThruPassThru (
       break;\r
   }\r
 \r
+  //\r
+  // If the cmd doesn't get executed correctly, then check sense data.\r
+  //\r
+  if (EFI_ERROR (Status) && (Packet->SenseDataLength != 0) && (*((UINT8*)Packet->Cdb) != ATA_CMD_REQUEST_SENSE)) {\r
+    PtrSenseData = AllocateAlignedPages (EFI_SIZE_TO_PAGES (sizeof (EFI_SCSI_SENSE_DATA)), This->Mode->IoAlign);\r
+    if (PtrSenseData == NULL) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    for (SenseReq = TRUE; SenseReq;) {\r
+      SenseStatus = AtaPacketRequestSense (\r
+                      This,\r
+                      Target,\r
+                      Lun,\r
+                      PtrSenseData,\r
+                      sizeof (EFI_SCSI_SENSE_DATA),\r
+                      Packet->Timeout\r
+                      );\r
+      if (EFI_ERROR (SenseStatus)) {\r
+        break;\r
+      }\r
+\r
+      CopyMem ((UINT8*)Packet->SenseData + SenseDataLen, PtrSenseData, sizeof (EFI_SCSI_SENSE_DATA));\r
+      SenseDataLen += sizeof (EFI_SCSI_SENSE_DATA);\r
+\r
+      //\r
+      // no more sense key or number of sense keys exceeds predefined,\r
+      // skip the loop.\r
+      //\r
+      if ((PtrSenseData->Sense_Key == EFI_SCSI_SK_NO_SENSE) ||\r
+          (SenseDataLen + sizeof (EFI_SCSI_SENSE_DATA) > Packet->SenseDataLength)) {\r
+        SenseReq = FALSE;\r
+      }\r
+    }\r
+    FreeAlignedPages (PtrSenseData, EFI_SIZE_TO_PAGES (sizeof (EFI_SCSI_SENSE_DATA)));\r
+  }\r
+  //\r
+  // Update the SenseDataLength field to the data length received.\r
+  //\r
+  Packet->SenseDataLength = (UINT8)SenseDataLen;\r
   return Status;\r
 }\r
 \r
@@ -2271,7 +2416,10 @@ ExtScsiPassThruResetChannel (
   IN  EFI_EXT_SCSI_PASS_THRU_PROTOCOL   *This\r
   )\r
 {\r
-  return EFI_UNSUPPORTED;\r
+  //\r
+  // Return success directly then upper layer driver could think reset channel operation is done.\r
+  //\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
@@ -2301,7 +2449,41 @@ ExtScsiPassThruResetTargetLun (
   IN UINT64                             Lun\r
   )\r
 {\r
-  return EFI_UNSUPPORTED;\r
+  ATA_ATAPI_PASS_THRU_INSTANCE    *Instance;\r
+  LIST_ENTRY                      *Node;\r
+  UINT8                           Port;\r
+  UINT8                           PortMultiplier;\r
+\r
+  Instance = EXT_SCSI_PASS_THRU_PRIVATE_DATA_FROM_THIS (This);\r
+  //\r
+  // For ATAPI device, doesn't support multiple LUN device.\r
+  //\r
+  if (Lun != 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // The layout of Target array:\r
+  //  ________________________________________________________________________\r
+  // |       Byte 0        |       Byte 1        | ... | TARGET_MAX_BYTES - 1 |\r
+  // |_____________________|_____________________|_____|______________________|\r
+  // |                     | The port multiplier |     |                      |\r
+  // |   The port number   |    port number      | N/A |         N/A          |\r
+  // |_____________________|_____________________|_____|______________________|\r
+  //\r
+  // For ATAPI device, 2 bytes is enough to represent the location of SCSI device.\r
+  //\r
+  Port           = Target[0];\r
+  PortMultiplier = Target[1];\r
+\r
+  Node = SearchDeviceInfoList(Instance, Port, PortMultiplier, EfiIdeCdrom);\r
+  if (Node == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Return success directly then upper layer driver could think reset target LUN operation is done.\r
+  //\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r