DeviceIoControl function for querying SCSI device

613 views
Skip to first unread message

Lorenzo Bergamini

unread,
Oct 7, 2015, 4:00:21 AM10/7/15
to Java Native Access
Hello everyone,

I am working on a project where I need to read a specific SCSI disk sector to retrieve some information about the device.

I have a complete code working on C++/Windows, but I wanted to make a Java version both because I am more familiar with Java GUI management

and because I need portability on OSx and I would like to avoid reimplementing everything from scratch.

This said, I started using the JNA library only recently and I think I need your help now :)

What I did so far is defining the SCSI_PASS_THROUGH_DIRECT structure as follows


public class SCSI_PASS_THROUGH_DIRECT extends Structure
{

   
public short Length;
   
public byte ScsiStatus;
   
public byte PathId;
   
public byte TargetId;
   
public byte Lun;
   
public byte CdbLength;
   
public byte SenseInfoLength;
   
public byte DataIn;
   
public short DataTransferLength;
   
public short TimeOutValue;
   
public Pointer DataBuffer;
   
public short SenseInfoOffset;
   
public byte[] Cdb = new byte[16];
   
@Override
   
protected List getFieldOrder()
   
{
       
return Arrays.asList(new String[] {"Length", "ScsiStatus", "PathId", "TargetId", "Lun", "CdbLength", "SenseInfoLength", "DataIn" ,
       
"DataTransferLength", "TimeOutValue", "DataBuffer", "SenseInfoOffset", "Cdb"});
   
}
   
}


According to what reported on msdn site and what I did in my C sample, (almost) everything is compliant. The bigger change I had to introduce is to replace the LONG values of DataTransferLength, TimeOutValue and SenseInfoOffset specified in the MSDN with short, otherwise I was getting an error 1306 ERROR_REVISION_MISMATCH when running the code. I think it was some mapping error and I am not sure the solution I did is good, but in any case for now I am getting a more "familiar" error 87 - INCORRECT PARAMETER.

The second structure I am using is


The following code is what I have for the moment


public static void main(String[] args)
   
{
       
       
StringBuffer devicePath = new StringBuffer("\\\\.\\I:");
       
WinNT.HANDLE hVolume = openDiskHandleString(devicePath.toString(), 0);

       
Kernel32 lib = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
       
        SCSI_PASS_THROUGH_DIRECT sptd
= new SCSI_PASS_THROUGH_DIRECT();
       
int IOCTL_SCSI_PASS_THROUGH_DIRECT = 0x0004d014;
        SCSI_result_buffer scsi_result
= new SCSI_result_buffer();
       
System.out.println("Structure size: " + sptd.size());
       
Pointer pointer = sptd.getPointer();
       
int sector_to_read = 0x500;
        sptd
.Length = (byte)sptd.size();
        sptd
.ScsiStatus = 0;
        sptd
.PathId = 0;
        sptd
.TargetId = 0;
        sptd
.Lun = 0;
        sptd
.CdbLength = 10;       //lenght of the SCSI Cdb we are using
        sptd
.SenseInfoLength = 0;
        sptd
.DataIn = 1;           //1 means read data from the device
        sptd
.DataTransferLength = 512;
        sptd
.TimeOutValue = 10;        
        sptd
.SenseInfoOffset = (short)sptd.size();
        sptd
.Cdb[0] = 0x28;        //0x28 is the command code for a read operation
        sptd
.Cdb[1] = 0;  
        sptd
.Cdb[5] = (byte)(sector_to_read % 256);           //LSB byte
        sector_to_read
= (int) sector_to_read / 256;              
        sptd
.Cdb[4] = (byte)(sector_to_read % 256);          
        sector_to_read
= (int) sector_to_read / 256;
        sptd
.Cdb[3] = (byte)(sector_to_read % 256);
        sector_to_read
= (int) sector_to_read / 256;
        sptd
.Cdb[2] = (byte)(sector_to_read % 256);
        sptd
.Cdb[7] = 0;
        sptd
.Cdb[8] = 01;
       
IntByReference dwBytesReturned = new IntByReference(0);
       
Pointer test = sptd.getPointer();
       
       
System.out.println("Pointer: " + test.dump(0, 12));
       
       
boolean control_code = lib.DeviceIoControl(hVolume, IOCTL_SCSI_PASS_THROUGH_DIRECT, test, sptd.size(), scsi_result.getPointer(), 512, dwBytesReturned, Pointer.NULL);
       
if (control_code)
       
{
           
System.out.println("Data OK");
       
}
       
else
           
System.out.println("DeviceIOControl error: " + lib.GetLastError());
       
       
       
System.out.println("Returned bytes: " + dwBytesReturned.getValue());
       
       
for (int i = 0; i < dwBytesReturned.getValue(); i++)
       
{
         
System.out.printf(" %02x", scsi_result.data[i]);        
       
}


   
}
   
       
private static WinNT.HANDLE openDiskHandleString(String path, int access) {
       
WinNT.HANDLE HandleToDevice;
       
int shareMode = WinNT.FILE_SHARE_READ | WinNT.FILE_SHARE_WRITE;
 
       
       
HandleToDevice = Kernel32.INSTANCE.CreateFile(
               
"\\\\.\\I:",
               
WinNT.GENERIC_READ|WinNT.GENERIC_WRITE,
                shareMode
,
               
null,
               
WinNT.OPEN_EXISTING,
               
0,
               
null);
       
         
if (HandleToDevice != WinNT.INVALID_HANDLE_VALUE)
         
{
             
System.out.println("OK");
             
return HandleToDevice;
         
}
         
else
         
{
             
System.out.println("Error");
             
return null;
         
}
   
}



So, as I said, I am able to open the handle to the device without any problem, but the execution of the code results in a "DeviceIOControl Error: 87", which, as I said, is an incorrect parameter.
I am pretty sure that the problem is in how I pass the Pointer, but I was not able to find a suitable description of how Pointers should be used in this situation.
Is there anyone able to help me either by suggesting a solution, or by pointing me to some extensive example on how to use Java Pointers?

Thank you very much for your help.

Lorenzo




L Will Ahonen

unread,
Oct 13, 2015, 3:30:39 AM10/13/15
to Java Native Access
Hi,

Does the size of the struct match on C and Java? If you replaced longs with shorts, it probably doesn't. You might want to use NativeLong here.

Cheers,
Will
...

Timothy Wall

unread,
Oct 13, 2015, 7:49:58 AM10/13/15
to jna-...@googlegroups.com
You’re using “short” where you should be using ULONG, NativeLong, or just plain int (safe on w32 since NativeLong on windows is always 32 bits).

This will result in a structure that is too small, and likely data corruption in everything after the “DataIn” field.
Reply all
Reply to author
Forward
0 new messages