I am using C# to build an application to write data to device(such as SD card) at specific address. I found many useful sample code online and I can write data to SD card now. But here is a problem. There are always 2 SCSIOP_READ_CAPACITY(0x25) sent before my application actually send my SCSIOP_WRITE(0x2A). Here are the Bushound log.
Device Phase Data Description Cmd.Phase.Ofs(rep)
------ ----- -------------------------------------------------- ---------------- ------------------
40 CMD 00 00 00 00 00 00 TEST UNIT READY 1.1.0(16)
40 ok 1.2.0
40 CMD 25 00 00 00 00 00 00 00 00 00 READ CAPACITY 17.1.0(2)
40 IN 00 e6 1f ff 00 00 02 00 ........ 17.2.0
40 CMD 2a 00 00 00 00 00 00 04 00 00 WRITE 19.1.0
40 OUT c0 76 db e7 c0 76 db e7 c0 76 db e7 c0 76 db e7 .v...v...v...v.. 19.2.0
c0 76 db e7 c0 76 db e7 c0 76 db e7 c0 76 db e7 .v...v...v...v.. 19.2.16
c0 76 db e7 c0 76 db e7 c0 76 db e7 c0 76 db e7 .v...v...v...v.. 19.2.32
c0 76 db e7 c0 76 db e7 c0 76 db e7 c0 76 db e7 .v...v...v...v.. 19.2.48
c0 76 db e7 c0 76 db e7 c0 76 db e7 c0 76 db e7 .v...v...v...v.. 19.2.64
c0 76 db e7 c0 76 db e7 c0 76 db e7 c0 76 db e7 .v...v...v...v.. 19.2.80
c0 76 db e7 c0 76 db e7 c0 76 db e7 c0 76 db e7 .v...v...v...v.. 19.2.96
c0 76 db e7 c0 76 db e7 c0 76 db e7 c0 76 db e7 .v...v...v...v.. 19.2.112
c0 76 db e7 c0 76 db e7 c0 76 db e7 c0 76 db e7 .v...v...v...v.. 19.2.128
As the log show, 2 READ_CAPACITY(0x25) are ahead of my WRITE(0x2A). But I do not even send or configure the CDB of READ_CAPACITY in my code. I don't know where the 2 READ_CAPACITY came from. Here are the code I am using.
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
static extern bool DeviceIoControl(
SafeFileHandle hDevice, //IntPtr hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
IntPtr lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped
);
[StructLayout(LayoutKind.Sequential)]
class SCSI_PASS_THROUGH_DIRECT
{
private const int _CDB_LENGTH = 16;
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 int DataTransferLength;
public int TimeOutValue;
public IntPtr DataBuffer;
public uint SenseInfoOffset;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = _CDB_LENGTH)]
public byte[] Cdb;
#region Constructors
public SCSI_PASS_THROUGH_DIRECT()
{
Cdb = new byte[_CDB_LENGTH];
}
#endregion Constructors
}
[StructLayout(LayoutKind.Sequential)]
class SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
{
private const int _SENSE_LENGTH = 32;
internal SCSI_PASS_THROUGH_DIRECT sptd = new SCSI_PASS_THROUGH_DIRECT();
[MarshalAs(UnmanagedType.ByValArray, SizeConst = _SENSE_LENGTH)]
internal byte[] sense;
#region Constructors
public SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER()
{
sense = new byte[_SENSE_LENGTH];
}
#endregion Constructors
}
private void btnWrite512KB_Click(object sender, EventArgs e)
{
uint Pattern = 0x12345678;
byte[] pattern = BitConverter.GetBytes(Pattern).Reverse().ToArray();
uint wbuffer_blkcnt = 0x400;
uint wbuffer_datasize = wbuffer_blkcnt * 512;
byte[] wBuffer = new byte[wbuffer_datasize];
int errorCode = 0;
Array.Clear(wBuffer, 0, wBuffer.Length);
for (int wbuffer_idx = 0; wbuffer_idx <= (wbuffer_datasize - pattern.Length); wbuffer_idx += pattern.Length)
{
Array.Copy(pattern, 0, wBuffer, wbuffer_idx, pattern.Length);
}
byte[] cdb = new byte[10];
cdb[0] = 0x2a;
cdb[1] = 0x00;
cdb[2] = 0x00; // MSB of address
cdb[3] = 0x00;
cdb[4] = 0x00;
cdb[5] = 0x00; // LSB of address
cdb[6] = 0x00;
cdb[7] = 0x04; // MSB of BlkLen
cdb[8] = 0x00; // LSB of BlkLen
cdb[9] = 0x00;
d.SendCommand(cdb, wBuffer);
}
public void SendCommand(byte[] cmd, byte[] data)
{
_SendCommand(_handle, cmd, data, 0);
}
private static byte[] _SendCommand(SafeFileHandle handle, byte[] cmd, byte[] data, int bytesExpected)
{
const int IOCTL_SCSI_BASE = 0x00000004;
const int IOCTL_SCSI_PASS_THROUGH = IOCTL_SCSI_BASE << 16 | 3 << 14 | 0x0401 << 2 | 0;
//const int IOCTL_SCSI_PASS_THROUGH_DIRECT = 0x4D014;
const int IOCTL_SCSI_PASS_THROUGH_DIRECT = IOCTL_SCSI_BASE << 16 | 3 << 14 | 0x0405 << 2 | 0;
const int TIMEOUT_SECS = 30;
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER scsi = null;
//SCSI_PASS_THROUGH_DIRECT scsi = null;
IntPtr inBuffer = IntPtr.Zero;
byte[] ret = null;
try
{
scsi = new SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER();
scsi.sptd.Length = (short)Marshal.SizeOf(scsi.sptd);
scsi.sptd.TimeOutValue = TIMEOUT_SECS;
scsi.sptd.SenseInfoOffset = (uint)Marshal.OffsetOf(typeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER), "sense");
scsi.sptd.SenseInfoLength = (byte)scsi.sense.Length;
scsi.sptd.CdbLength = (byte)cmd.Length;
Array.Copy(cmd, scsi.sptd.Cdb, cmd.Length);
scsi.sptd.DataIn = data != null && data.Length > 0 ? SCSI_IOCTL_DATA_OUT : SCSI_IOCTL_DATA_IN;
scsi.sptd.DataTransferLength = data != null && data.Length > 0 ? data.Length : bytesExpected;
scsi.sptd.DataBuffer = Marshal.AllocHGlobal(scsi.sptd.DataTransferLength);
if (data != null && data.Length > 0)
{
Marshal.Copy(data, 0, scsi.sptd.DataBuffer, data.Length);
}
uint bytesReturned;
inBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(scsi));
var size = (uint)Marshal.SizeOf(scsi);
Marshal.StructureToPtr(scsi, inBuffer, false);
//if (!DeviceIoControl(handle.DangerousGetHandle(), IOCTL_SCSI_PASS_THROUGH_DIRECT,
// inBuffer, size, inBuffer, size, out bytesReturned, IntPtr.Zero))
if (!DeviceIoControl(handle, IOCTL_SCSI_PASS_THROUGH_DIRECT,
inBuffer, size, inBuffer, size, out bytesReturned, IntPtr.Zero))
{
//Whoops, do something with the error code
int last = Marshal.GetLastWin32Error();
throw new InvalidOperationException("DeviceIoControl failed: " + last.ToString("X04"));
}
else
{
if (scsi.sptd.ScsiStatus != 0)
{
//Whoops, do something with the error code
throw new InvalidOperationException("SCSI command failed: " + scsi.sptd.ScsiStatus.ToString("X02"));
}
else
{
//Success, marshal back any data we received
if (scsi.sptd.DataTransferLength > 0)
{
ret = new byte[scsi.sptd.DataTransferLength];
Marshal.Copy(scsi.sptd.DataBuffer, ret, 0, ret.Length);
}
}
}
}
finally
{
/* Free any unmanaged resources */
if (scsi != null && scsi.sptd.DataBuffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(scsi.sptd.DataBuffer);
}
if (inBuffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(inBuffer);
}
}
return ret;
}
After d.SendCommand(cdb, wBuffer) executed, Bushound recorded the log above. Please help me get rid of the 2 READ_CAPACITY. There should be no READ_CAPACITY in Bushound log. Thank you!
User contributions licensed under CC BY-SA 3.0