Implemented SMART commands for ide devices.
Modified: trunk/reactos/drivers/storage/atapi/atapi.c
Modified: trunk/reactos/drivers/storage/disk/disk.c
Modified: trunk/reactos/drivers/storage/scsiport/scsiport.c

Modified: trunk/reactos/drivers/storage/atapi/atapi.c
--- trunk/reactos/drivers/storage/atapi/atapi.c	2005-10-16 11:11:35 UTC (rev 18495)
+++ trunk/reactos/drivers/storage/atapi/atapi.c	2005-10-16 11:17:26 UTC (rev 18496)
@@ -221,6 +221,9 @@
 AtapiPacketInterrupt(IN PATAPI_MINIPORT_EXTENSION DevExt);
 
 static BOOLEAN FASTCALL
+AtapiSmartInterrupt(IN PATAPI_MINIPORT_EXTENSION DevExt);
+
+static BOOLEAN FASTCALL
 AtapiReadInterrupt(IN PATAPI_MINIPORT_EXTENSION DevExt);
 
 #ifdef ENABLE_DMA
@@ -266,6 +269,10 @@
 		    IN PSCSI_REQUEST_BLOCK Srb);
 
 static ULONG
+AtapiSendSmartCommand(IN PATAPI_MINIPORT_EXTENSION DeviceExtension,
+                      IN PSCSI_REQUEST_BLOCK Srb);
+
+static ULONG
 AtapiInquiry(IN PATAPI_MINIPORT_EXTENSION DeviceExtension,
 	     IN PSCSI_REQUEST_BLOCK Srb);
 
@@ -941,54 +948,128 @@
       case SRB_FUNCTION_IO_CONTROL:
         {
           PSRB_IO_CONTROL SrbIoControl = (PSRB_IO_CONTROL)Srb->DataBuffer;
-          if (!_strnicmp((char*)SrbIoControl->Signature, "ScsiDisk", 8))
+          if (Srb->DataTransferLength < sizeof(SRB_IO_CONTROL) ||
+              Srb->DataTransferLength < SrbIoControl->Length + sizeof(SRB_IO_CONTROL))
             {
-              switch (SrbIoControl->ControlCode)
+              Result = SRB_STATUS_INVALID_REQUEST;
+            }
+          else
+            {
+              if (!_strnicmp((char*)SrbIoControl->Signature, "ScsiDisk", 8))
                 {
-                  default:
-                    Result = SRB_STATUS_INVALID_REQUEST;
-                    break;
-
-                  case IOCTL_SCSI_MINIPORT_IDENTIFY:
+                  switch (SrbIoControl->ControlCode)
                     {
-                      PSENDCMDOUTPARAMS OutParams = (PSENDCMDOUTPARAMS)((ULONG_PTR)Srb->DataBuffer + sizeof(SRB_IO_CONTROL));
-                      SENDCMDINPARAMS InParams = *(PSENDCMDINPARAMS)OutParams;
-                  
+                      default:
+                        Result = SRB_STATUS_INVALID_REQUEST;
+                        break;
 
-                      RtlZeroMemory(OutParams, Srb->DataTransferLength - sizeof(SRB_IO_CONTROL));
+                      case IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE:
+                      case IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES:
+                      case IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS:
+                      case IOCTL_SCSI_MINIPORT_ENABLE_SMART:
+                      case IOCTL_SCSI_MINIPORT_DISABLE_SMART:
+                      case IOCTL_SCSI_MINIPORT_RETURN_STATUS:
+                      case IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS:
+                      case IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS:
+                      case IOCTL_SCSI_MINIPORT_READ_SMART_LOG:
+#if 0
+                      case IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG:
+#endif
+                        Result = AtapiSendSmartCommand(DevExt, Srb);
+                        break;
 
-                      if (InParams.irDriveRegs.bCommandReg != IDE_CMD_IDENT_ATA_DRV)
+                      case IOCTL_SCSI_MINIPORT_SMART_VERSION:
                         {
-                          DPRINT1("bCommandReg: %x\n", InParams.irDriveRegs.bCommandReg);
-                          OutParams->DriverStatus.bIDEError = 1;
-                          Result = SRB_STATUS_INVALID_REQUEST;
+                          GETVERSIONINPARAMS Version;      
+                          ULONG i;
+
+                          DPRINT("IOCTL_SCSI_MINIPORT_SMART_VERSION\n");
+
+                          RtlZeroMemory(&Version, sizeof(GETVERSIONINPARAMS));
+                          Version.bVersion = 1;
+                          Version.bRevision = 1;
+                          for (i = 0; i < 2; i++)
+                            {
+                              switch (DevExt->DeviceFlags[i] & (DEVICE_PRESENT|DEVICE_ATAPI))
+                                {
+                                  case DEVICE_PRESENT:
+                                    Version.bIDEDeviceMap |= 0x01 << i;
+                                    break;
+/*
+                                  case DEVICE_PRESENT|DEVICE_ATAPI:
+                                    Version.bIDEDeviceMap |= 0x11 << i;
+                                    break;
+*/
+                                }
+                            }
+                          Version.fCapabilities = CAP_ATA_ID_CMD/*|CAP_ATAPI_ID_CMD|CAP_SMART_CMD*/;
+                          SrbIoControl->Length = min(sizeof(GETVERSIONINPARAMS), Srb->DataTransferLength - sizeof(SRB_IO_CONTROL));
+                          memcpy(SrbIoControl + 1, &Version, SrbIoControl->Length);
+                          Result = SRB_STATUS_SUCCESS;
                           break;
                         }
 
-                      if (InParams.bDriveNumber > 1 ||
-                        (DevExt->DeviceFlags[InParams.bDriveNumber] & (DEVICE_PRESENT|DEVICE_ATAPI)) != DEVICE_PRESENT)
+                      case IOCTL_SCSI_MINIPORT_IDENTIFY:
                         {
-                          OutParams->DriverStatus.bIDEError = 1;
-                          Result = SRB_STATUS_NO_DEVICE;
+                          SENDCMDOUTPARAMS OutParams;
+                          SENDCMDINPARAMS InParams = *(PSENDCMDINPARAMS)(SrbIoControl + 1);
+
+                          DPRINT("IOCTL_SCSI_MINIPORT_IDENTIFY\n");
+
+                          if (Srb->DataTransferLength < sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1)
+                            {
+                              Result = SRB_STATUS_INVALID_REQUEST;
+                              break;
+                            }
+                  
+                          RtlZeroMemory(&OutParams, sizeof(SENDCMDOUTPARAMS));
+
+                          if (InParams.irDriveRegs.bCommandReg != IDE_CMD_IDENT_ATA_DRV)
+                            {
+                              DPRINT("bCommandReg: %x\n", InParams.irDriveRegs.bCommandReg);
+                              OutParams.DriverStatus.bIDEError = 1;
+                              Result = SRB_STATUS_INVALID_REQUEST;
+                            }
+                          else if (InParams.bDriveNumber > 1 ||
+                                   (DevExt->DeviceFlags[InParams.bDriveNumber] & (DEVICE_PRESENT|DEVICE_ATAPI)) != DEVICE_PRESENT)
+                            {
+                              OutParams.DriverStatus.bIDEError = 1;
+                              Result = SRB_STATUS_NO_DEVICE;
+                            }
+                          else
+                            {
+                              Result = SRB_STATUS_SUCCESS;
+                            }
+                          if (Result == SRB_STATUS_SUCCESS)
+                            {
+                              SrbIoControl->Length = min(sizeof(SENDCMDOUTPARAMS) - 1 + IDENTIFY_BUFFER_SIZE, Srb->DataTransferLength - sizeof(SRB_IO_CONTROL));
+                            }
+                          else
+                            {
+                              SrbIoControl->Length = min(sizeof(SENDCMDOUTPARAMS) - 1, Srb->DataTransferLength - sizeof(SRB_IO_CONTROL));
+                            }
+
+                          if (SrbIoControl->Length >= sizeof(SENDCMDOUTPARAMS) - 1)
+                            {
+                              OutParams.cBufferSize = min(SrbIoControl->Length, IDENTIFY_BUFFER_SIZE);
+                            }
+                          
+                          memcpy(SrbIoControl + 1, &OutParams, min (SrbIoControl->Length, sizeof(SENDCMDOUTPARAMS) - 1));
+                         
+                          if (SrbIoControl->Length > sizeof(SENDCMDOUTPARAMS) - 1)
+                            {
+                              RtlCopyMemory((PVOID)((ULONG_PTR)(SrbIoControl + 1) + sizeof(SENDCMDOUTPARAMS) - 1), &DevExt->DeviceParams[InParams.bDriveNumber], OutParams.cBufferSize);
+                            }
                           break;
                         }
-
-                      if (Srb->DataTransferLength > sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1)
-                        {
-                          OutParams->cBufferSize = min(IDENTIFY_BUFFER_SIZE, Srb->DataTransferLength - (sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1));
-                          RtlCopyMemory(OutParams->bBuffer, &DevExt->DeviceParams[InParams.bDriveNumber], OutParams->cBufferSize);
-                        }
-
-                      Result = SRB_STATUS_SUCCESS;
-                      break;
                     }
                 }
+              else
+                {
+                  Result = SRB_STATUS_INVALID_REQUEST;
+                  SrbIoControl->Length = 0;
+                }
             }
-          else
-            {
-              Result = SRB_STATUS_INVALID_REQUEST;
-            }
-            
           break;
         }
 
@@ -1686,6 +1767,219 @@
 //  -------------------------------------------  Nondiscardable statics
 
 static ULONG
+AtapiSendSmartCommand(IN PATAPI_MINIPORT_EXTENSION DeviceExtension,
+		      IN PSCSI_REQUEST_BLOCK Srb)
+{
+  PSRB_IO_CONTROL SrbIoControl = (PSRB_IO_CONTROL)Srb->DataBuffer;
+  SENDCMDINPARAMS InParams;
+  PSENDCMDOUTPARAMS OutParams = (PSENDCMDOUTPARAMS)(SrbIoControl + 1);
+  ULONG Retries;
+  UCHAR Status;
+
+  if (Srb->DataTransferLength < sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1 ||
+      SrbIoControl->Length < sizeof(SENDCMDOUTPARAMS) - 1)
+    {
+      return SRB_STATUS_INVALID_REQUEST;
+    }
+  InParams = *(PSENDCMDINPARAMS)(SrbIoControl + 1);
+
+  DPRINT("%02x %02x %02x %02x %02x %02x %02x %02x\n",
+         InParams.irDriveRegs.bFeaturesReg,
+         InParams.irDriveRegs.bSectorCountReg,
+         InParams.irDriveRegs.bSectorNumberReg,
+         InParams.irDriveRegs.bCylLowReg,
+         InParams.irDriveRegs.bCylHighReg,
+         InParams.irDriveRegs.bDriveHeadReg,
+         InParams.irDriveRegs.bCommandReg,
+         InParams.irDriveRegs.bReserved);
+
+  if (InParams.bDriveNumber > 1 ||
+      (DeviceExtension->DeviceFlags[InParams.bDriveNumber] & (DEVICE_PRESENT|DEVICE_ATAPI)) != DEVICE_PRESENT)
+    {
+      RtlZeroMemory(&OutParams, sizeof(SENDCMDOUTPARAMS));
+      OutParams->DriverStatus.bIDEError = 1;
+      return SRB_STATUS_NO_DEVICE;
+    }
+
+  DeviceExtension->DataTransferLength = 0;
+
+  switch (SrbIoControl->ControlCode)
+    {
+      case IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS:
+        DPRINT("IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS\n");
+
+        if (Srb->DataTransferLength < sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE ||
+            SrbIoControl->Length < sizeof(SENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE ||
+            InParams.irDriveRegs.bFeaturesReg != READ_ATTRIBUTES ||
+            InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW ||
+            InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI ||
+            InParams.irDriveRegs.bCommandReg != SMART_CMD)
+          {
+            return SRB_STATUS_INVALID_REQUEST;            
+          }
+        InParams.irDriveRegs.bSectorCountReg = 0; 
+        InParams.irDriveRegs.bSectorNumberReg = 0;
+        DeviceExtension->DataTransferLength = READ_ATTRIBUTE_BUFFER_SIZE;
+        break;
+      
+      case IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS:
+        DPRINT("IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS\n");
+
+        if (Srb->DataTransferLength < sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1 + READ_THRESHOLD_BUFFER_SIZE ||
+            SrbIoControl->Length < sizeof(SENDCMDOUTPARAMS) - 1 + READ_THRESHOLD_BUFFER_SIZE ||
+            InParams.irDriveRegs.bFeaturesReg != READ_THRESHOLDS ||
+            InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW ||
+            InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI ||
+            InParams.irDriveRegs.bCommandReg != SMART_CMD)
+          {
+            return SRB_STATUS_INVALID_REQUEST;            
+          }
+        InParams.irDriveRegs.bSectorCountReg = 0; 
+        InParams.irDriveRegs.bSectorNumberReg = 0;
+        DeviceExtension->DataTransferLength = READ_THRESHOLD_BUFFER_SIZE;
+        break;
+
+      case IOCTL_SCSI_MINIPORT_READ_SMART_LOG:
+        DPRINT("IOCTL_SCSI_MINIPORT_READ_SMART_LOG\n");
+
+        if (Srb->DataTransferLength < sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1 + max(1, InParams.irDriveRegs.bSectorCountReg) * SMART_LOG_SECTOR_SIZE ||
+            SrbIoControl->Length < sizeof(SENDCMDOUTPARAMS) - 1 + max(1, InParams.irDriveRegs.bSectorCountReg) * SMART_LOG_SECTOR_SIZE ||
+            InParams.irDriveRegs.bFeaturesReg != SMART_READ_LOG ||
+            InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW ||
+            InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI ||
+            InParams.irDriveRegs.bCommandReg != SMART_CMD)
+          {
+            return SRB_STATUS_INVALID_REQUEST;            
+          }
+        DeviceExtension->DataTransferLength = max(1, InParams.irDriveRegs.bSectorCountReg) * SMART_LOG_SECTOR_SIZE;
+        break;
+
+      case IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE:
+        DPRINT("IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE\n");
+
+        if (InParams.irDriveRegs.bFeaturesReg != ENABLE_DISABLE_AUTOSAVE ||
+            (InParams.irDriveRegs.bSectorCountReg != 0 && InParams.irDriveRegs.bSectorCountReg != 1) || 
+            InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW ||
+            InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI ||
+            InParams.irDriveRegs.bCommandReg != SMART_CMD)
+          {
+            return SRB_STATUS_INVALID_REQUEST;            
+          }
+        InParams.irDriveRegs.bSectorNumberReg = 0;
+        break;
+
+      case IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES:
+        DPRINT("IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES\n");
+
+        if (InParams.irDriveRegs.bFeaturesReg != SAVE_ATTRIBUTE_VALUES ||
+            (InParams.irDriveRegs.bSectorCountReg != 0 && InParams.irDriveRegs.bSectorCountReg != 0xf1) || 
+            InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW ||
+            InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI ||
+            InParams.irDriveRegs.bCommandReg != SMART_CMD)
+          {
+            return SRB_STATUS_INVALID_REQUEST;            
+          }
+        InParams.irDriveRegs.bSectorNumberReg = 0;
+        break;
+
+      case IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS:
+        DPRINT("IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS\n");
+
+        if (InParams.irDriveRegs.bFeaturesReg != EXECUTE_OFFLINE_DIAGS ||
+            InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW ||
+            InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI ||
+            InParams.irDriveRegs.bCommandReg != SMART_CMD)
+          {
+            return SRB_STATUS_INVALID_REQUEST;            
+          }
+        InParams.irDriveRegs.bSectorCountReg = 0; 
+        break;
+
+      case IOCTL_SCSI_MINIPORT_ENABLE_SMART:
+        DPRINT("IOCTL_SCSI_MINIPORT_ENABLE_SMART\n");
+
+        if (InParams.irDriveRegs.bFeaturesReg != ENABLE_SMART ||
+            InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW ||
+            InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI ||
+            InParams.irDriveRegs.bCommandReg != SMART_CMD)
+          {
+            return SRB_STATUS_INVALID_REQUEST;            
+          }
+        InParams.irDriveRegs.bSectorCountReg = 0; 
+        InParams.irDriveRegs.bSectorNumberReg = 0;
+        break;
+
+      case IOCTL_SCSI_MINIPORT_DISABLE_SMART:
+        DPRINT("IOCTL_SCSI_MINIPORT_DISABLE_SMART\n");
+
+        if (InParams.irDriveRegs.bFeaturesReg != DISABLE_SMART ||
+            InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW ||
+            InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI ||
+            InParams.irDriveRegs.bCommandReg != SMART_CMD)
+          {
+            return SRB_STATUS_INVALID_REQUEST;            
+          }
+        InParams.irDriveRegs.bSectorCountReg = 0; 
+        InParams.irDriveRegs.bSectorNumberReg = 0;
+        break;
+
+      case IOCTL_SCSI_MINIPORT_RETURN_STATUS:
+        DPRINT("IOCTL_SCSI_MINIPORT_RETURN_STATUS\n");
+
+        if (InParams.irDriveRegs.bFeaturesReg != RETURN_SMART_STATUS ||
+            InParams.irDriveRegs.bCylLowReg != SMART_CYL_LOW ||
+            InParams.irDriveRegs.bCylHighReg != SMART_CYL_HI ||
+            InParams.irDriveRegs.bCommandReg != SMART_CMD)
+          {
+            return SRB_STATUS_INVALID_REQUEST;            
+          }
+        InParams.irDriveRegs.bSectorCountReg = 0; 
+        InParams.irDriveRegs.bSectorNumberReg = 0;
+        break;
+    }
+
+  Srb->TargetId = InParams.bDriveNumber;
+  
+  /* Set pointer to data buffer. */
+  DeviceExtension->DataBuffer = (PUCHAR)OutParams->bBuffer;
+
+  DeviceExtension->CurrentSrb = Srb;
+
+  /*  wait for BUSY to clear  */
+  for (Retries = 0; Retries < IDE_MAX_BUSY_RETRIES; Retries++)
+    {
+      Status = IDEReadStatus(DeviceExtension->CommandPortBase);
+      if (!(Status & IDE_SR_BUSY))
+        {
+          break;
+        }
+      ScsiPortStallExecution(10);
+    }
+  if (Retries >= IDE_MAX_BUSY_RETRIES)
+    {
+      DPRINT ("Drive is BUSY for too long\n");
+      return(SRB_STATUS_BUSY);
+    }
+
+  /*  Select the desired drive  */
+  InParams.irDriveRegs.bDriveHeadReg = (InParams.bDriveNumber ? IDE_DH_DRV1 : IDE_DH_DRV0) | IDE_DH_FIXED;
+  IDEWriteDriveHead(DeviceExtension->CommandPortBase, InParams.irDriveRegs.bDriveHeadReg);
+  ScsiPortStallExecution(2);
+
+  IDEWritePrecomp(DeviceExtension->CommandPortBase, InParams.irDriveRegs.bFeaturesReg);
+  IDEWriteSectorCount(DeviceExtension->CommandPortBase, InParams.irDriveRegs.bSectorCountReg);
+  IDEWriteSectorNum(DeviceExtension->CommandPortBase, InParams.irDriveRegs.bSectorNumberReg);
+  IDEWriteCylinderLow(DeviceExtension->CommandPortBase, InParams.irDriveRegs.bCylLowReg);
+  IDEWriteCylinderHigh(DeviceExtension->CommandPortBase, InParams.irDriveRegs.bCylHighReg);
+
+  AtapiExecuteCommand(DeviceExtension, InParams.irDriveRegs.bCommandReg, AtapiSmartInterrupt);
+
+  /* Wait for interrupt. */
+  return SRB_STATUS_PENDING;
+
+}
+
+static ULONG
 AtapiSendAtapiCommand(IN PATAPI_MINIPORT_EXTENSION DeviceExtension,
 		      IN PSCSI_REQUEST_BLOCK Srb)
 {
@@ -2240,8 +2534,6 @@
   IDEWriteCylinderLow(DeviceExtension->CommandPortBase, CylinderLow[0]);
   IDEWriteCylinderHigh(DeviceExtension->CommandPortBase, CylinderHigh[0]);
 
-  IDEWriteDriveHead(DeviceExtension->CommandPortBase, IDE_DH_FIXED | DrvHead);
-
 #ifdef ENABLE_DMA
   if (DeviceExtension->PRDTable &&
       DeviceExtension->DeviceFlags[Srb->TargetId] & DEVICE_DMA_CMD)
@@ -2940,6 +3232,79 @@
 #endif
 
 static BOOLEAN FASTCALL
+AtapiSmartInterrupt(PATAPI_MINIPORT_EXTENSION DevExt)
+{
+  PSCSI_REQUEST_BLOCK Srb;
+  UCHAR DeviceStatus;
+  PSRB_IO_CONTROL SrbIoControl;
+  PSENDCMDOUTPARAMS OutParams;
+  PIDEREGS IdeRegs;
+
+  DPRINT("AtapiSmartInterrupt() called!\n");
+
+  Srb = DevExt->CurrentSrb;
+
+
+  DeviceStatus = IDEReadStatus(DevExt->CommandPortBase);
+  if ((DeviceStatus & (IDE_SR_DRQ|IDE_SR_BUSY|IDE_SR_ERR)) != (DevExt->DataTransferLength ? IDE_SR_DRQ : 0))
+    {
+      if (DeviceStatus & (IDE_SR_ERR|IDE_SR_DRQ))
+        {
+	  AtapiCompleteRequest(DevExt, SRB_STATUS_ERROR);
+          DPRINT("AtapiSmartInterrupt() done!\n");
+	  return TRUE;
+        }
+      DPRINT("AtapiSmartInterrupt() done!\n");
+      return FALSE;
+    }
+
+  DPRINT("CommandPortBase: %lx  ControlPortBase: %lx\n", DevExt->CommandPortBase, DevExt->ControlPortBase);
+
+  if (DevExt->DataTransferLength)
+    {
+      IDEReadBlock(DevExt->CommandPortBase, DevExt->DataBuffer, 512);
+      DevExt->DataTransferLength -= 512;
+      DevExt->DataBuffer += 512;
+    }
+
+  if (DevExt->DataTransferLength == 0)
+    {
+      SrbIoControl = (PSRB_IO_CONTROL)Srb->DataBuffer;
+      OutParams = (PSENDCMDOUTPARAMS)(SrbIoControl + 1);
+
+      OutParams->DriverStatus.bDriverError = 0;
+      OutParams->DriverStatus.bIDEError = 0;
+
+      if (SrbIoControl->ControlCode == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
+        {
+          IdeRegs = (PIDEREGS)OutParams->bBuffer;
+
+          IdeRegs->bFeaturesReg = RETURN_SMART_STATUS;
+          IdeRegs->bSectorCountReg = IDEReadSectorCount(DevExt->CommandPortBase);
+          IdeRegs->bSectorNumberReg = IDEReadSectorNum(DevExt->CommandPortBase);
+          IdeRegs->bCylLowReg = IDEReadCylinderLow(DevExt->CommandPortBase);
+          IdeRegs->bCylHighReg = IDEReadCylinderHigh(DevExt->CommandPortBase);
+          IdeRegs->bDriveHeadReg = IDEReadDriveHead(DevExt->CommandPortBase);
+          IdeRegs->bCommandReg = SMART_CMD;
+          IdeRegs->bReserved = 0;
+
+          OutParams->cBufferSize = 8;
+        }
+      else
+        {
+          OutParams->cBufferSize = DevExt->DataBuffer - OutParams->bBuffer;
+        }
+
+      AtapiCompleteRequest(DevExt, SRB_STATUS_SUCCESS);
+    }
+
+  DPRINT("AtapiSmartInterrupt() done!\n");
+
+  return(TRUE);
+}
+
+
+static BOOLEAN FASTCALL
 AtapiReadInterrupt(PATAPI_MINIPORT_EXTENSION DevExt)
 {
   PSCSI_REQUEST_BLOCK Srb;

Modified: trunk/reactos/drivers/storage/disk/disk.c
--- trunk/reactos/drivers/storage/disk/disk.c	2005-10-16 11:11:35 UTC (rev 18495)
+++ trunk/reactos/drivers/storage/disk/disk.c	2005-10-16 11:17:26 UTC (rev 18496)
@@ -999,6 +999,11 @@
   PDISK_DATA DiskData;
   ULONG Information;
   NTSTATUS Status;
+  KEVENT Event;
+  IO_STATUS_BLOCK IoSB;
+  PIRP LocalIrp;
+  PSRB_IO_CONTROL SrbIoControl;
+  PSENDCMDINPARAMS InParams;
 
   DPRINT("DiskClassDeviceControl() called!\n");
 
@@ -1251,6 +1256,338 @@
 	Information = 0;
 	break;
 
+      case SMART_GET_VERSION:
+        {
+          PGETVERSIONINPARAMS Version;
+          DPRINT("SMART_GET_VERSION\n");
+          if (OutputLength < sizeof(GETVERSIONINPARAMS)) 
+            {
+              Status = STATUS_BUFFER_TOO_SMALL;
+              Information = sizeof(GETVERSIONINPARAMS);
+              break;
+            }
+
+          SrbIoControl = ExAllocatePool(NonPagedPool, sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS));
+          if (SrbIoControl == NULL)
+            {
+	      Status = STATUS_INSUFFICIENT_RESOURCES;
+              break;
+            }
+          Version = (PGETVERSIONINPARAMS)(SrbIoControl + 1);
+          memset(SrbIoControl, 0, sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS));
+
+          SrbIoControl->HeaderLength = sizeof(SRB_IO_CONTROL);
+          memcpy(SrbIoControl->Signature, "ScsiDisk", 8);
+          SrbIoControl->Timeout = DeviceExtension->TimeOutValue * 4;
+          SrbIoControl->Length = sizeof(GETVERSIONINPARAMS);
+          SrbIoControl->ControlCode = IOCTL_SCSI_MINIPORT_SMART_VERSION;
+
+          KeInitializeEvent(&Event, NotificationEvent, FALSE);
+          LocalIrp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT,
+                                                   DeviceExtension->PortDeviceObject,
+                                                   SrbIoControl,
+                                                   sizeof(SRB_IO_CONTROL),
+                                                   SrbIoControl,
+                                                   sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS),
+                                                   FALSE,
+                                                   &Event,
+                                                   &IoSB);
+          if (LocalIrp == NULL)
+            {
+              ExFreePool(SrbIoControl);
+	      Status = STATUS_INSUFFICIENT_RESOURCES;
+              break;
+            }
+
+          Status = IoCallDriver(DeviceExtension->PortDeviceObject, LocalIrp);
+          if (Status == STATUS_PENDING)
+            {
+              KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+              Status = IoSB.Status;
+            }
+          if (NT_SUCCESS(Status))
+            {
+              memcpy(Irp->AssociatedIrp.SystemBuffer, Version, sizeof(GETVERSIONINPARAMS));
+              Information = sizeof(GETVERSIONINPARAMS);
+            }
+          ExFreePool(SrbIoControl);
+          break;
+        }
+
+      case SMART_SEND_DRIVE_COMMAND: 
+        {
+          DPRINT("SMART_SEND_DRIVE_COMMAND\n");
+
+          if (InputLength < sizeof(SENDCMDINPARAMS) - 1)
+            {
+	      Status = STATUS_INFO_LENGTH_MISMATCH;
+              break;
+            }
+
+          InParams = (PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer;
+          if (InParams->irDriveRegs.bCommandReg == SMART_CMD)
+            {
+              if (InParams->irDriveRegs.bFeaturesReg == ENABLE_DISABLE_AUTOSAVE)
+                {
+                  DPRINT("IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE\n");
+                  ControlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE;
+                  Information = sizeof(SENDCMDOUTPARAMS) - 1;
+                }
+              else if (InParams->irDriveRegs.bFeaturesReg == SAVE_ATTRIBUTE_VALUES)
+                {
+                  DPRINT("IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES\n");
+                  ControlCode = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES;
+                  Information = sizeof(SENDCMDOUTPARAMS) - 1;
+                }
+              else if (InParams->irDriveRegs.bFeaturesReg == EXECUTE_OFFLINE_DIAGS)
+                {
+                  DPRINT("IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS\n");
+                  ControlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS;
+                  Information = sizeof(SENDCMDOUTPARAMS) - 1;
+                }
+              else if (InParams->irDriveRegs.bFeaturesReg == SMART_WRITE_LOG)
+                {
+                  DPRINT("IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG\n");
+                  ControlCode = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG;
+                  if (InputLength < sizeof(SENDCMDINPARAMS) - 1 + 512 * max(1, InParams->irDriveRegs.bSectorCountReg))
+                    {
+	              Status = STATUS_INFO_LENGTH_MISMATCH;
+                      break;
+                    }
+                  Information = sizeof(SENDCMDOUTPARAMS) - 1;
+                }
+              else if (InParams->irDriveRegs.bFeaturesReg == ENABLE_SMART)
+                {
+                  DPRINT("IOCTL_SCSI_MINIPORT_ENABLE_SMART\n");
+                  ControlCode = IOCTL_SCSI_MINIPORT_ENABLE_SMART;
+                  Information = sizeof(SENDCMDOUTPARAMS) - 1;
+                }
+              else if (InParams->irDriveRegs.bFeaturesReg == DISABLE_SMART)
+                {
+                  DPRINT("IOCTL_SCSI_MINIPORT_DISABLE_SMART\n");
+                  ControlCode = IOCTL_SCSI_MINIPORT_DISABLE_SMART;
+                  Information = sizeof(SENDCMDOUTPARAMS) - 1;
+                }
+              else if (InParams->irDriveRegs.bFeaturesReg == RETURN_SMART_STATUS)
+                {
+                  DPRINT("IOCTL_SCSI_MINIPORT_RETURN_STATUS\n");
+                  ControlCode = IOCTL_SCSI_MINIPORT_RETURN_STATUS;
+                  Information = sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS);
+                }
+              else if (InParams->irDriveRegs.bFeaturesReg == ENABLE_DISABLE_AUTO_OFFLINE)
+                {
+                  DPRINT("IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS\n");
+                  ControlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS;
+                  Information = sizeof(SENDCMDOUTPARAMS) - 1;
+                }
+              else
+                {
+                  DPRINT("%x\n", InParams->irDriveRegs.bFeaturesReg);
+                  Status = STATUS_INVALID_PARAMETER;
+                  break;
+                }
+            }
+          else
+            {
+              Status = STATUS_INVALID_PARAMETER;
+              break;
+            }
+          if (OutputLength < Information)
+            {
+              Status = STATUS_BUFFER_TOO_SMALL;
+              break;
+            }
+          
+          SrbIoControl = ExAllocatePool(NonPagedPool, sizeof(SRB_IO_CONTROL) + max(Information, sizeof(SENDCMDINPARAMS) - 1));
+          if (SrbIoControl == NULL) 
+            {
+              Status =  STATUS_INSUFFICIENT_RESOURCES;
+              Information = 0;
+              break;
+            }
+          memset(SrbIoControl, 0, sizeof(SRB_IO_CONTROL) + max(Information, sizeof(SENDCMDINPARAMS) - 1));
+
+          SrbIoControl->HeaderLength = sizeof(SRB_IO_CONTROL);
+          memcpy(SrbIoControl->Signature, "SCSIDISK", 8);
+          SrbIoControl->Timeout = DeviceExtension->TimeOutValue * 4;
+          SrbIoControl->Length = Information;
+          SrbIoControl->ControlCode = ControlCode;
+
+          InParams = (PSENDCMDINPARAMS)(SrbIoControl + 1);
+
+          memcpy(InParams, Irp->AssociatedIrp.SystemBuffer, sizeof(SENDCMDINPARAMS) - 1);
+          
+          InParams->bDriveNumber = DeviceExtension->TargetId;
+
+          KeInitializeEvent(&Event, NotificationEvent, FALSE);
+          LocalIrp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT,
+                                                   DeviceExtension->PortDeviceObject,
+                                                   SrbIoControl,
+                                                   sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1,
+                                                   SrbIoControl,
+                                                   sizeof(SRB_IO_CONTROL) + Information,
+                                                   FALSE,
+                                                   &Event,
+                                                   &IoSB);
+          if (LocalIrp == NULL)
+            {
+              ExFreePool(SrbIoControl);
+              Information = 0;
+	      Status = STATUS_INSUFFICIENT_RESOURCES;
+              break;
+            }
+          Status = IoCallDriver(DeviceExtension->PortDeviceObject, LocalIrp);
+          if (Status == STATUS_PENDING)
+            {
+              KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+              Status = IoSB.Status;
+            }
+          if (NT_SUCCESS(Status))
+            {
+              Information = SrbIoControl->Length;
+            }
+          else
+            {
+              Information = sizeof(SENDCMDOUTPARAMS) - 1;
+            }
+          memcpy(Irp->AssociatedIrp.SystemBuffer, InParams, Information);
+          ExFreePool(SrbIoControl);
+          break;
+        }
+
+      case SMART_RCV_DRIVE_DATA:
+        {
+          DPRINT("SMART_RCV_DRIVE_DATA\n");
+
+          if (InputLength < sizeof(SENDCMDINPARAMS) - 1)
+            {
+	      Status = STATUS_INFO_LENGTH_MISMATCH;
+              break;
+            }
+
+          InParams = (PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer;
+          if (InParams->irDriveRegs.bCommandReg == ID_CMD) 
+            {
+              DPRINT("IOCTL_SCSI_MINIPORT_IDENTIFY\n");
+              ControlCode = IOCTL_SCSI_MINIPORT_IDENTIFY;
+              Information = IDENTIFY_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS) - 1;
+            }
+          else if (InParams->irDriveRegs.bCommandReg == SMART_CMD) 
+            {
+              if (InParams->irDriveRegs.bFeaturesReg == READ_ATTRIBUTES)
+                {
+                  DPRINT("IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS\n");
+                  ControlCode = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS;
+                  Information = READ_ATTRIBUTE_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS) - 1;
+                }
+              else if (InParams->irDriveRegs.bFeaturesReg == READ_THRESHOLDS)
+                {
+                  DPRINT("IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS\n");
+                  ControlCode = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS;
+                  Information = READ_THRESHOLD_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS) - 1;
+                }
+              else if (InParams->irDriveRegs.bFeaturesReg == SMART_READ_LOG)
+                {
+                  DPRINT("IOCTL_SCSI_MINIPORT_READ_SMART_LOG\n");
+                  ControlCode = IOCTL_SCSI_MINIPORT_READ_SMART_LOG;
+                  Information = sizeof(SENDCMDOUTPARAMS) - 1 + 512 * max(1, InParams->irDriveRegs.bSectorCountReg); 
+                }
+              else
+                {
+                  DPRINT("%x\n", InParams->irDriveRegs.bFeaturesReg);
+                  Status = STATUS_INVALID_PARAMETER;
+                  break;
+                }
+            }
+          else
+            {
+              DPRINT("%x\n", InParams->irDriveRegs.bCommandReg);
+              Status = STATUS_INVALID_PARAMETER;
+              break;
+            }
+          if (OutputLength < Information)
+            {
+              Status =  STATUS_BUFFER_TOO_SMALL;
+              break;
+            }
+          SrbIoControl = ExAllocatePool(NonPagedPool, sizeof(SRB_IO_CONTROL) + Information);
+          if (SrbIoControl == NULL) 
+            {
+              Status =  STATUS_INSUFFICIENT_RESOURCES;
+              Information = 0;
+              break;
+            }
+          memset(SrbIoControl, 0, sizeof(SRB_IO_CONTROL) + Information);
+
+          SrbIoControl->HeaderLength = sizeof(SRB_IO_CONTROL);
+          memcpy(SrbIoControl->Signature, "SCSIDISK", 8);
+          SrbIoControl->Timeout = DeviceExtension->TimeOutValue * 4;
+          SrbIoControl->Length = Information;
+          SrbIoControl->ControlCode = ControlCode;
+
+          InParams = (PSENDCMDINPARAMS)(SrbIoControl + 1);
+
+          memcpy(InParams, Irp->AssociatedIrp.SystemBuffer, sizeof(SENDCMDINPARAMS) - 1);
+          
+          InParams->bDriveNumber = DeviceExtension->TargetId;
+
+          KeInitializeEvent(&Event, NotificationEvent, FALSE);
+          LocalIrp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT,
+                                                   DeviceExtension->PortDeviceObject,
+                                                   SrbIoControl,
+                                                   sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1,
+                                                   SrbIoControl,
+                                                   sizeof(SRB_IO_CONTROL) + Information,
+                                                   FALSE,
+                                                   &Event,
+                                                   &IoSB);
+          if (LocalIrp == NULL)
+            {
+              ExFreePool(SrbIoControl);
+              Information = 0;
+	      Status = STATUS_INSUFFICIENT_RESOURCES;
+              break;
+            }
+          Status = IoCallDriver(DeviceExtension->PortDeviceObject, LocalIrp);
+          if (Status == STATUS_PENDING)
+            {
+              KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+              Status = IoSB.Status;
+            }
+          if (NT_SUCCESS(Status))
+            {
+#if 0            	
+              CHAR Buffer[256];
+              ULONG i, j;
+              UCHAR sum = 0;
+              memset(Buffer, 0, sizeof(Buffer));
+              for (i = 0; i < 512; i += 16)
+              {
+                 for (j = 0; j < 16 && i + j < 512; j++)
+                   {
+                     sprintf(&Buffer[3*j], "%02x ", ((PSENDCMDOUTPARAMS)(SrbIoControl + 1))->bBuffer[i + j]);
+                     sum += ((PSENDCMDOUTPARAMS)(SrbIoControl + 1))->bBuffer[i + j];
+                   }
+                 for (j = 0; j < 16 && i + j < 512; j++)
+                   {
+                       sprintf(&Buffer[3*16 + j], "%c", isprint(((PSENDCMDOUTPARAMS)(SrbIoControl + 1))->bBuffer[i + j]) ? ((PSENDCMDOUTPARAMS)(SrbIoControl + 1))->bBuffer[i + j] : '.');
+                   }
+                 DPRINT1("%04x %s\n", i, Buffer);
+              }
+              DPRINT1("Sum %02x\n", sum);
+#endif              
+              Information = SrbIoControl->Length;
+             
+            }
+          else
+            {
+              Information = sizeof(SENDCMDOUTPARAMS) -1;
+            }
+          memcpy(Irp->AssociatedIrp.SystemBuffer, InParams, Information);
+          ExFreePool(SrbIoControl);
+          break;
+        }
+
       default:
 	/* Call the common device control function */
 	return(ScsiClassDeviceControl(DeviceObject, Irp));
@@ -1316,21 +1653,13 @@
       return(STATUS_INSUFFICIENT_RESOURCES);
     }
 
-  /* Initialize SRB */
   RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
-  Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
 
-  /* Set device IDs */
-  Srb->PathId = DeviceExtension->PathId;
-  Srb->TargetId = DeviceExtension->TargetId;
-  Srb->Lun = DeviceExtension->Lun;
 
   /* Set timeout */
   Srb->TimeOutValue = DeviceExtension->TimeOutValue * 4;
 
   /* Flush write cache */
-  Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
-  Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
   Srb->CdbLength = 10;
   Srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;
   ScsiClassSendSrbSynchronous(DeviceObject,

Modified: trunk/reactos/drivers/storage/scsiport/scsiport.c
--- trunk/reactos/drivers/storage/scsiport/scsiport.c	2005-10-16 11:11:35 UTC (rev 18495)
+++ trunk/reactos/drivers/storage/scsiport/scsiport.c	2005-10-16 11:17:26 UTC (rev 18496)
@@ -1776,9 +1776,9 @@
         break;
 
       case IOCTL_SCSI_MINIPORT:
-        DPRINT1("  IOCTL_SCSI_MINIPORT\n");
-        DPRINT1("  Signature: %.8s\n", ((PSRB_IO_CONTROL)Irp->AssociatedIrp.SystemBuffer)->Signature);
-        DPRINT1("  ControlCode: 0x%lX\n", ((PSRB_IO_CONTROL)Irp->AssociatedIrp.SystemBuffer)->ControlCode);
+        DPRINT("  IOCTL_SCSI_MINIPORT\n");
+        DPRINT("  Signature: %.8s\n", ((PSRB_IO_CONTROL)Irp->AssociatedIrp.SystemBuffer)->Signature);
+        DPRINT("  ControlCode: 0x%lX\n", ((PSRB_IO_CONTROL)Irp->AssociatedIrp.SystemBuffer)->ControlCode);
         return SpiScsiMiniport(DeviceObject, Irp);
 
       default:
@@ -1869,7 +1869,7 @@
   SrbIoControl = (PSRB_IO_CONTROL)Irp->AssociatedIrp.SystemBuffer;
 
   IrpStack = IoGetCurrentIrpStackLocation(Irp);
-  if (IrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1)
+  if (IrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SRB_IO_CONTROL))
     {
       Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
       IoCompleteRequest(Irp, IO_NO_INCREMENT);