/// <summary>Sends an ATA command in 48-bit LBA mode</summary> /// <returns>0 if no error occurred, otherwise, errno</returns> /// <param name="fd">File handle</param> /// <param name="buffer">Buffer for SCSI command response</param> /// <param name="timeout">Timeout in seconds</param> /// <param name="duration">Time it took to execute the command in milliseconds</param> /// <param name="sense"><c>True</c> if ATA error returned non-OK status</param> /// <param name="registers">Registers to send to drive</param> /// <param name="errorRegisters">Registers returned by drive</param> /// <param name="protocol">ATA protocol to use</param> internal static int SendAtaCommand(SafeFileHandle fd, AtaRegistersLba48 registers, out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol, ref byte[] buffer, uint timeout, out double duration, out bool sense) { duration = 0; sense = false; errorRegisters = new AtaErrorRegistersLba48(); if (buffer == null) { return(-1); } uint offsetForBuffer = (uint)(Marshal.SizeOf(typeof(AtaPassThroughEx)) + Marshal.SizeOf(typeof(uint))); var aptdBuf = new AtaPassThroughExBuffer { aptd = new AtaPassThroughEx { TimeOutValue = timeout, DataBufferOffset = (IntPtr)offsetForBuffer, Length = (ushort)Marshal.SizeOf(typeof(AtaPassThroughEx)), DataTransferLength = (uint)buffer.Length, PreviousTaskFile = new AtaTaskFile { CylinderHigh = (byte)((registers.LbaHigh & 0xFF00) >> 8), CylinderLow = (byte)((registers.LbaMid & 0xFF00) >> 8), Features = (byte)((registers.Feature & 0xFF00) >> 8), SectorCount = (byte)((registers.SectorCount & 0xFF00) >> 8), SectorNumber = (byte)((registers.LbaLow & 0xFF00) >> 8) }, CurrentTaskFile = new AtaTaskFile { Command = registers.Command, CylinderHigh = (byte)(registers.LbaHigh & 0xFF), CylinderLow = (byte)(registers.LbaMid & 0xFF), DeviceHead = registers.DeviceHead, Features = (byte)(registers.Feature & 0xFF), SectorCount = (byte)(registers.SectorCount & 0xFF), SectorNumber = (byte)(registers.LbaLow & 0xFF) } }, dataBuffer = new byte[64 * 512] }; switch (protocol) { case AtaProtocol.PioIn: case AtaProtocol.UDmaIn: case AtaProtocol.Dma: aptdBuf.aptd.AtaFlags = AtaFlags.DataIn; break; case AtaProtocol.PioOut: case AtaProtocol.UDmaOut: aptdBuf.aptd.AtaFlags = AtaFlags.DataOut; break; } switch (protocol) { case AtaProtocol.Dma: case AtaProtocol.DmaQueued: case AtaProtocol.FpDma: case AtaProtocol.UDmaIn: case AtaProtocol.UDmaOut: aptdBuf.aptd.AtaFlags |= AtaFlags.Dma; break; } aptdBuf.aptd.AtaFlags |= AtaFlags.ExtendedCommand; // Unknown if needed aptdBuf.aptd.AtaFlags |= AtaFlags.DrdyRequired; uint k = 0; int error = 0; Array.Copy(buffer, 0, aptdBuf.dataBuffer, 0, buffer.Length); DateTime start = DateTime.Now; sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IoctlAtaPassThrough, ref aptdBuf, (uint)Marshal.SizeOf(aptdBuf), ref aptdBuf, (uint)Marshal.SizeOf(aptdBuf), ref k, IntPtr.Zero); DateTime end = DateTime.Now; if (sense) { error = Marshal.GetLastWin32Error(); } Array.Copy(aptdBuf.dataBuffer, 0, buffer, 0, buffer.Length); duration = (end - start).TotalMilliseconds; errorRegisters.SectorCount = (ushort)((aptdBuf.aptd.PreviousTaskFile.SectorCount << 8) + aptdBuf.aptd.CurrentTaskFile.SectorCount); errorRegisters.LbaLow = (ushort)((aptdBuf.aptd.PreviousTaskFile.SectorNumber << 8) + aptdBuf.aptd.CurrentTaskFile.SectorNumber); errorRegisters.LbaMid = (ushort)((aptdBuf.aptd.PreviousTaskFile.CylinderLow << 8) + aptdBuf.aptd.CurrentTaskFile.CylinderLow); errorRegisters.LbaHigh = (ushort)((aptdBuf.aptd.PreviousTaskFile.CylinderHigh << 8) + aptdBuf.aptd.CurrentTaskFile.CylinderHigh); errorRegisters.DeviceHead = aptdBuf.aptd.CurrentTaskFile.DeviceHead; errorRegisters.Error = aptdBuf.aptd.CurrentTaskFile.Error; errorRegisters.Status = aptdBuf.aptd.CurrentTaskFile.Status; sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; return(error); }
internal static extern bool DeviceIoControlAta(SafeFileHandle hDevice, WindowsIoctl ioControlCode, ref AtaPassThroughExBuffer inBuffer, uint nInBufferSize, ref AtaPassThroughExBuffer outBuffer, uint nOutBufferSize, ref uint pBytesReturned, IntPtr overlapped);