Solution to Windows ioctls are hard

I solved the ReadCapacity issues I was having in sgio.d on Windows! This is a major milestone for me because I've been stuck on this problem for over two months.

My issue is described in a previous post but to summarize, the ioctls on Windows would only work when passing scsi Inquiry commands and the same calls worked on Linux. This was sad because all not-Inquiry commands would fail silently AND yet provide an Okay status. Actually that's the main difficulty I had in solving this problem: inquiries worked but ReadCapacity and others would fail so, at first glance, that led me believe my CDB had an issue (but those same CDBs work fine on linux).

The solution to this problem is hidden in the description of the SCSI_PASS_THROUGH_DIRECT structure's DataTransferLength field:

DataTransferLength: Indicates the size in bytes of the data buffer. Many devices transfer chunks of data of predefined length. The value in DataTransferLength must be an integral multiple of this predefined, minimum length that is specified by the device.

Do you see what I missed? Well you should, I made it bold. My code was using buffers much smaller than the "predefined length" and when I tested using 512 bytes it worked immediately. One LINE was my blunder!

I actually asked for help on StackOverflow and a user asked if the buffer's alignment was correct for the device, which I thought it was since the Inquiries worked... Wrong. Soon I'll have scsi WRITES working (scary!).

Here's the current more-than-likely-buggy-but-functional version of the Windows sgio_execute method:

scsiPassThrough.Cdb[0..size]       = cdb_buf[0..size];  
scsiPassThrough.Length             = SCSI_PASS_THROUGH_DIRECT.sizeof;  
scsiPassThrough.ScsiStatus         = 0x00;  
scsiPassThrough.TimeOutValue       = 0x40;  
scsiPassThrough.CdbLength          = cast(ubyte)(size);  
scsiPassThrough.SenseInfoOffset    = SCSI_PASS_THROUGH_DIRECT.sizeof;  
scsiPassThrough.SenseInfoLength    = SENSE_LENGTH;  
scsiPassThrough.DataIn             = SCSI_IOCTL_DATA_IN;  
scsiPassThrough.DataBuffer         = datain_buf.ptr;

// This MUST be a multiple of the device's block size!
scsiPassThrough.DataTransferLength = 512;  

Powered by Digital Ocean

Click the image for a $10 referral