DeviceIoControl SCSI_PASS_THROUGH RRS feed

  • Question

  • First off, I am new to posting to MSDN, so please just let me know if I have made any mistakes on where to post, how I posted, etc and I will correct them ASAP.

    Second, thank you very much for taking time to review my questions and helping out.

    I have a coding background (~15 years), but it's mainly in embedded systems and I have been doing other work for the last few years, so I am a bit rusty.

    I have been brought into a project to first update some code, then after it is running and we can safely continue testing, I can work on improving / rewriting it for future work. The code is fairly old (in PC terms) and hasn't been touched since around 2007. Here are some details:

    Written to originally test / evaluate parallel SCSI hard drives under Windows NT 3.51. Upgraded over the years to add SAS, ATA, SATA on different OS’s up through Windows XP. It is still however based around SCSI commands (I know this should change and will hopefully in the near future).

    I need to get this code running under Windows 7 so the group can continue testing. This is my priority. I have read about some of the differences between XP and Vista (and 7) for direct access to the SCSI / ATA drives. I have made a few minor changes that have allowed some of the tests to work, but I now feel I just don't understand enough to move forward, so I am hoping someone can help. I have changed the device type from "\\\\.\\SCSI%u" to "\\\\.\\PhysicalDrive%u". I have changed the running of the code to run as administrator. These 2 changes have gotten several of the commands to work, but not all. I have also tested many other changes, but just not gotten anywhere. I have tried disabling the drive (in the control panel), adding / removing partitions, etc. Generally commands that just require a set response seem to work. Those that cause the drive to write / read real data or run a test do not.

    My test setup uses a Western Digital 500 Gig SATA HDD, A Dell PC with Xenon processors and a on board SAS controller. The same setup and code work under XP works with no issue, but not under Windows 7. I have included a couple examples of commands that work and a couple that don't. I have also included the piece of the code that makes the actual calls that are failing.

    Any and all help you can provide is greatly appreciated!



    For all of these the physical drive, target ID and LUN are the same throughout.
    Couple examples of Working Commands:

    Temperature Data:
    SRB_Flags = "SRB_DIR_in"
    SRB_SenseLen = 18
    SRB_CDBLen = 10
    CDBByte = 0x4d004d00000000000000

    Returns data correctly

    Smart Attribute:
    SRB_Flags = "SRB_DIR_in"
    SRB_CDBLen = 16
    SRB_SenseLen = 22
    CDBByte = 0x85080E00D000010000004F00C200B000

    Returns the SMART attributes in the buffer. All looks good


    Couple examples of Failing commands:

    SMART Status:
    SRB_Flags = "SRB_DIR_in"
    SRB_SenseLen = 22
    SRB_CDBLen = 16
    CDBByte = 85062C00DA00000000004F00C200B000

    Returns no error, but sense data is all 0's which is not a valid result

    SMART Self Test - Short:
    SRB_Flags = "SRB_DIR_in"
    SRB_CDBLen = 16
    SRB_SenseLen = 22
    CDBByte = 85060C00D400000081004F00C200B000

    deviceIoCommand return - 0X04 SRB COMPLETED WITH ERROR.
    Tarket Status is: 0X0000045D ERROR_IO_DEVICE.

    Requesting the SMART attributes returns in the self test status field: 0x29
    This breaks down to: (bits 7-3) 0x05 - Failed electrical test and: (bits 3-0) 0x1 - 10% remaining in test.
    Note: All 3 SMART self test type return exactly the same way.
    Also all test that attempt to write data fail with an illegal command error (though I haven't narrowed this down yet).


    Sample of code:
    static DWORD execScsiCmd (PSRB_ExecSCSICmd psrb) {
       char path[1024], buffer[4096];
       int i;
       DWORD u, offset, error;
       HANDLE h;

       sprintf (path, "\\\\.\\PhysicalDrive%u", psrb->SRB_HaId);

     h = CreateFile (

       if (h == INVALID_HANDLE_VALUE) {
          return SS_INVALID_HA;
       else {
          SPTI_REQUEST t;
          memset (&t, 0, sizeof (t));
          t.spt.Length = sizeof (t.spt);
          t.spt.PathId = 0;
          t.spt.TargetId = psrb->SRB_Target;
          t.spt.Lun = psrb->SRB_Lun;
          t.spt.CdbLength = psrb->SRB_CDBLen;
          t.spt.SenseInfoLength = psrb->SRB_SenseLen;

          if (psrb->SRB_Flags & SRB_DIR_OUT) {
             t.spt.DataIn = SCSI_IOCTL_DATA_OUT;
          else if (psrb->SRB_Flags & SRB_DIR_IN) {
             t.spt.DataIn = SCSI_IOCTL_DATA_IN;
          else {
             t.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
          t.spt.DataTransferLength = psrb->SRB_BufLen;

          t.spt.TimeOutValue = 0x0000FFFF;
          t.spt.DataBuffer = psrb->SRB_BufPointer;
          t.spt.SenseInfoOffset = offsetof (SPTI_REQUEST, sense);
          memcpy (t.spt.Cdb, psrb->CDBByte, psrb->SRB_CDBLen);

          if (DeviceIoControl (h, IOCTL_SCSI_PASS_THROUGH_DIRECT, &t, sizeof (t), &t, sizeof (t), &u, NULL)) {
             memcpy (psrb->SenseArea, t.sense, psrb->SRB_SenseLen);
             psrb->SRB_TargStat = t.spt.ScsiStatus;
             psrb->SRB_HaStat = HASTAT_OK;
             psrb->SRB_Status = psrb->SRB_TargStat ? SS_ERR : SS_COMP;
          else {
             memset (psrb->SenseArea, 0, psrb->SRB_SenseLen);
             psrb->SRB_TargStat = STATUS_GOOD;


             psrb->SRB_HaStat = ~0;
             psrb->SRB_Status = SS_ERR;

          CloseHandle (h);
       return psrb->SRB_Status;

    Monday, July 1, 2013 2:58 PM

All replies

  • Any ideas? Is there a better place to ask this? How about with just one of the issues, the ATA Smart Status (my first failed example)?

    Any help is GREATLY appreciated.



    Monday, July 8, 2013 2:36 PM
  • In case anyone is curious, an updated driver, firmware and bios on my LSI SATA / SAS controller fixed most of my issues.

    Tuesday, July 23, 2013 1:45 PM